Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheStorageService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "CacheLog.h"
8
#include "CacheStorageService.h"
9
#include "CacheFileIOManager.h"
10
#include "CacheObserver.h"
11
#include "CacheIndex.h"
12
#include "CacheIndexIterator.h"
13
#include "CacheStorage.h"
14
#include "AppCacheStorage.h"
15
#include "CacheEntry.h"
16
#include "CacheFileUtils.h"
17
18
#include "OldWrappers.h"
19
#include "nsCacheService.h"
20
#include "nsDeleteDir.h"
21
22
#include "nsICacheStorageVisitor.h"
23
#include "nsIObserverService.h"
24
#include "nsIFile.h"
25
#include "nsIURI.h"
26
#include "nsCOMPtr.h"
27
#include "nsContentUtils.h"
28
#include "nsAutoPtr.h"
29
#include "nsNetCID.h"
30
#include "nsNetUtil.h"
31
#include "nsServiceManagerUtils.h"
32
#include "nsWeakReference.h"
33
#include "nsXULAppAPI.h"
34
#include "mozilla/TimeStamp.h"
35
#include "mozilla/DebugOnly.h"
36
#include "mozilla/Services.h"
37
#include "mozilla/IntegerPrintfMacros.h"
38
39
namespace mozilla {
40
namespace net {
41
42
namespace {
43
44
void AppendMemoryStorageID(nsAutoCString &key)
45
0
{
46
0
  key.Append('/');
47
0
  key.Append('M');
48
0
}
49
50
} // namespace
51
52
// Not defining as static or class member of CacheStorageService since
53
// it would otherwise need to include CacheEntry.h and that then would
54
// need to be exported to make nsNetModule.cpp compilable.
55
typedef nsClassHashtable<nsCStringHashKey, CacheEntryTable>
56
        GlobalEntryTables;
57
58
/**
59
 * Keeps tables of entries.  There is one entries table for each distinct load
60
 * context type.  The distinction is based on following load context info states:
61
 * <isPrivate|isAnon|appId|inIsolatedMozBrowser> which builds a mapping key.
62
 *
63
 * Thread-safe to access, protected by the service mutex.
64
 */
65
static GlobalEntryTables* sGlobalEntryTables;
66
67
CacheMemoryConsumer::CacheMemoryConsumer(uint32_t aFlags)
68
: mReportedMemoryConsumption(0)
69
, mFlags(aFlags)
70
0
{
71
0
}
72
73
void
74
CacheMemoryConsumer::DoMemoryReport(uint32_t aCurrentSize)
75
0
{
76
0
  if (!(mFlags & DONT_REPORT) && CacheStorageService::Self()) {
77
0
    CacheStorageService::Self()->OnMemoryConsumptionChange(this, aCurrentSize);
78
0
  }
79
0
}
80
81
CacheStorageService::MemoryPool::MemoryPool(EType aType)
82
: mType(aType)
83
, mMemorySize(0)
84
0
{
85
0
}
86
87
CacheStorageService::MemoryPool::~MemoryPool()
88
0
{
89
0
  if (mMemorySize != 0) {
90
0
    NS_ERROR("Network cache reported memory consumption is not at 0, probably leaking?");
91
0
  }
92
0
}
93
94
uint32_t
95
CacheStorageService::MemoryPool::Limit() const
96
0
{
97
0
  switch (mType) {
98
0
  case DISK:
99
0
    return CacheObserver::MetadataMemoryLimit();
100
0
  case MEMORY:
101
0
    return CacheObserver::MemoryCacheCapacity();
102
0
  }
103
0
104
0
  MOZ_CRASH("Bad pool type");
105
0
  return 0;
106
0
}
107
108
NS_IMPL_ISUPPORTS(CacheStorageService,
109
                  nsICacheStorageService,
110
                  nsIMemoryReporter,
111
                  nsITimerCallback,
112
                  nsICacheTesting,
113
                  nsINamed)
114
115
CacheStorageService* CacheStorageService::sSelf = nullptr;
116
117
CacheStorageService::CacheStorageService()
118
: mLock("CacheStorageService.mLock")
119
, mForcedValidEntriesLock("CacheStorageService.mForcedValidEntriesLock")
120
, mShutdown(false)
121
, mDiskPool(MemoryPool::DISK)
122
, mMemoryPool(MemoryPool::MEMORY)
123
0
{
124
0
  CacheFileIOManager::Init();
125
0
126
0
  MOZ_ASSERT(XRE_IsParentProcess());
127
0
  MOZ_ASSERT(!sSelf);
128
0
129
0
  sSelf = this;
130
0
  sGlobalEntryTables = new GlobalEntryTables();
131
0
132
0
  RegisterStrongMemoryReporter(this);
133
0
}
134
135
CacheStorageService::~CacheStorageService()
136
0
{
137
0
  LOG(("CacheStorageService::~CacheStorageService"));
138
0
  sSelf = nullptr;
139
0
}
140
141
void CacheStorageService::Shutdown()
142
0
{
143
0
  mozilla::MutexAutoLock lock(mLock);
144
0
145
0
  if (mShutdown)
146
0
    return;
147
0
148
0
  LOG(("CacheStorageService::Shutdown - start"));
149
0
150
0
  mShutdown = true;
151
0
152
0
  nsCOMPtr<nsIRunnable> event =
153
0
    NewRunnableMethod("net::CacheStorageService::ShutdownBackground",
154
0
                      this,
155
0
                      &CacheStorageService::ShutdownBackground);
156
0
  Dispatch(event);
157
0
158
#ifdef NS_FREE_PERMANENT_DATA
159
  sGlobalEntryTables->Clear();
160
  delete sGlobalEntryTables;
161
#endif
162
  sGlobalEntryTables = nullptr;
163
0
164
0
  LOG(("CacheStorageService::Shutdown - done"));
165
0
}
166
167
void CacheStorageService::ShutdownBackground()
168
0
{
169
0
  LOG(("CacheStorageService::ShutdownBackground - start"));
170
0
171
0
  MOZ_ASSERT(IsOnManagementThread());
172
0
173
0
  {
174
0
    mozilla::MutexAutoLock lock(mLock);
175
0
176
0
    // Cancel purge timer to avoid leaking.
177
0
    if (mPurgeTimer) {
178
0
      LOG(("  freeing the timer"));
179
0
      mPurgeTimer->Cancel();
180
0
    }
181
0
  }
182
0
183
#ifdef NS_FREE_PERMANENT_DATA
184
  Pool(false).mFrecencyArray.Clear();
185
  Pool(false).mExpirationArray.Clear();
186
  Pool(true).mFrecencyArray.Clear();
187
  Pool(true).mExpirationArray.Clear();
188
#endif
189
190
0
  LOG(("CacheStorageService::ShutdownBackground - done"));
191
0
}
192
193
// Internal management methods
194
195
namespace {
196
197
// WalkCacheRunnable
198
// Base class for particular storage entries visiting
199
class WalkCacheRunnable : public Runnable
200
                        , public CacheStorageService::EntryInfoCallback
201
{
202
protected:
203
  WalkCacheRunnable(nsICacheStorageVisitor* aVisitor, bool aVisitEntries)
204
    : Runnable("net::WalkCacheRunnable")
205
    , mService(CacheStorageService::Self())
206
    , mCallback(aVisitor)
207
    , mSize(0)
208
    , mNotifyStorage(true)
209
    , mVisitEntries(aVisitEntries)
210
    , mCancel(false)
211
0
  {
212
0
    MOZ_ASSERT(NS_IsMainThread());
213
0
  }
214
215
  virtual ~WalkCacheRunnable()
216
0
  {
217
0
    if (mCallback) {
218
0
      ProxyReleaseMainThread(
219
0
        "WalkCacheRunnable::mCallback", mCallback);
220
0
    }
221
0
  }
222
223
  RefPtr<CacheStorageService> mService;
224
  nsCOMPtr<nsICacheStorageVisitor> mCallback;
225
226
  uint64_t mSize;
227
228
  bool mNotifyStorage : 1;
229
  bool mVisitEntries : 1;
230
231
  Atomic<bool> mCancel;
232
};
233
234
// WalkMemoryCacheRunnable
235
// Responsible to visit memory storage and walk
236
// all entries on it asynchronously.
237
class WalkMemoryCacheRunnable : public WalkCacheRunnable
238
{
239
public:
240
  WalkMemoryCacheRunnable(nsILoadContextInfo *aLoadInfo,
241
                          bool aVisitEntries,
242
                          nsICacheStorageVisitor* aVisitor)
243
    : WalkCacheRunnable(aVisitor, aVisitEntries)
244
0
  {
245
0
    CacheFileUtils::AppendKeyPrefix(aLoadInfo, mContextKey);
246
0
    MOZ_ASSERT(NS_IsMainThread());
247
0
  }
248
249
  nsresult Walk()
250
0
  {
251
0
    return mService->Dispatch(this);
252
0
  }
253
254
private:
255
  NS_IMETHOD Run() override
256
0
  {
257
0
    if (CacheStorageService::IsOnManagementThread()) {
258
0
      LOG(("WalkMemoryCacheRunnable::Run - collecting [this=%p]", this));
259
0
      // First, walk, count and grab all entries from the storage
260
0
261
0
      mozilla::MutexAutoLock lock(CacheStorageService::Self()->Lock());
262
0
263
0
      if (!CacheStorageService::IsRunning())
264
0
        return NS_ERROR_NOT_INITIALIZED;
265
0
266
0
      CacheEntryTable* entries;
267
0
      if (sGlobalEntryTables->Get(mContextKey, &entries)) {
268
0
        for (auto iter = entries->Iter(); !iter.Done(); iter.Next()) {
269
0
          CacheEntry* entry = iter.UserData();
270
0
271
0
          // Ignore disk entries
272
0
          if (entry->IsUsingDisk()) {
273
0
            continue;
274
0
          }
275
0
276
0
          mSize += entry->GetMetadataMemoryConsumption();
277
0
278
0
          int64_t size;
279
0
          if (NS_SUCCEEDED(entry->GetDataSize(&size))) {
280
0
            mSize += size;
281
0
          }
282
0
          mEntryArray.AppendElement(entry);
283
0
        }
284
0
      }
285
0
286
0
      // Next, we dispatch to the main thread
287
0
    } else if (NS_IsMainThread()) {
288
0
      LOG(("WalkMemoryCacheRunnable::Run - notifying [this=%p]", this));
289
0
290
0
      if (mNotifyStorage) {
291
0
        LOG(("  storage"));
292
0
293
0
        // Second, notify overall storage info
294
0
        mCallback->OnCacheStorageInfo(mEntryArray.Length(), mSize,
295
0
                                      CacheObserver::MemoryCacheCapacity(), nullptr);
296
0
        if (!mVisitEntries)
297
0
          return NS_OK; // done
298
0
299
0
        mNotifyStorage = false;
300
0
301
0
      } else {
302
0
        LOG(("  entry [left=%zu, canceled=%d]", mEntryArray.Length(), (bool)mCancel));
303
0
304
0
        // Third, notify each entry until depleted or canceled
305
0
        if (!mEntryArray.Length() || mCancel) {
306
0
          mCallback->OnCacheEntryVisitCompleted();
307
0
          return NS_OK; // done
308
0
        }
309
0
310
0
        // Grab the next entry
311
0
        RefPtr<CacheEntry> entry = mEntryArray[0];
312
0
        mEntryArray.RemoveElementAt(0);
313
0
314
0
        // Invokes this->OnEntryInfo, that calls the callback with all
315
0
        // information of the entry.
316
0
        CacheStorageService::GetCacheEntryInfo(entry, this);
317
0
      }
318
0
    } else {
319
0
      MOZ_CRASH("Bad thread");
320
0
      return NS_ERROR_FAILURE;
321
0
    }
322
0
323
0
    NS_DispatchToMainThread(this);
324
0
    return NS_OK;
325
0
  }
326
327
  virtual ~WalkMemoryCacheRunnable()
328
0
  {
329
0
    if (mCallback)
330
0
      ProxyReleaseMainThread(
331
0
        "WalkMemoryCacheRunnable::mCallback", mCallback);
332
0
  }
333
334
  virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
335
                           int64_t aDataSize, int32_t aFetchCount,
336
                           uint32_t aLastModifiedTime, uint32_t aExpirationTime,
337
                           bool aPinned, nsILoadContextInfo* aInfo) override
338
0
  {
339
0
    nsresult rv;
340
0
341
0
    nsCOMPtr<nsIURI> uri;
342
0
    rv = NS_NewURI(getter_AddRefs(uri), aURISpec);
343
0
    if (NS_FAILED(rv)) {
344
0
      return;
345
0
    }
346
0
347
0
    rv = mCallback->OnCacheEntryInfo(uri, aIdEnhance, aDataSize, aFetchCount,
348
0
                                     aLastModifiedTime, aExpirationTime,
349
0
                                     aPinned, aInfo);
350
0
    if (NS_FAILED(rv)) {
351
0
      LOG(("  callback failed, canceling the walk"));
352
0
      mCancel = true;
353
0
    }
354
0
  }
355
356
private:
357
  nsCString mContextKey;
358
  nsTArray<RefPtr<CacheEntry> > mEntryArray;
359
};
360
361
// WalkDiskCacheRunnable
362
// Using the cache index information to get the list of files per context.
363
class WalkDiskCacheRunnable : public WalkCacheRunnable
364
{
365
public:
366
  WalkDiskCacheRunnable(nsILoadContextInfo *aLoadInfo,
367
                        bool aVisitEntries,
368
                        nsICacheStorageVisitor* aVisitor)
369
    : WalkCacheRunnable(aVisitor, aVisitEntries)
370
    , mLoadInfo(aLoadInfo)
371
    , mPass(COLLECT_STATS)
372
    , mCount(0)
373
0
  {
374
0
  }
375
376
  nsresult Walk()
377
0
  {
378
0
    // TODO, bug 998693
379
0
    // Initial index build should be forced here so that about:cache soon
380
0
    // after startup gives some meaningfull results.
381
0
382
0
    // Dispatch to the INDEX level in hope that very recent cache entries
383
0
    // information gets to the index list before we grab the index iterator
384
0
    // for the first time.  This tries to avoid miss of entries that has
385
0
    // been created right before the visit is required.
386
0
    RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
387
0
    NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
388
0
389
0
    return thread->Dispatch(this, CacheIOThread::INDEX);
390
0
  }
391
392
private:
393
  // Invokes OnCacheEntryInfo callback for each single found entry.
394
  // There is one instance of this class per one entry.
395
  class OnCacheEntryInfoRunnable : public Runnable
396
  {
397
  public:
398
    explicit OnCacheEntryInfoRunnable(WalkDiskCacheRunnable* aWalker)
399
      : Runnable("net::WalkDiskCacheRunnable::OnCacheEntryInfoRunnable")
400
      , mWalker(aWalker)
401
      , mDataSize(0)
402
      , mFetchCount(0)
403
      , mLastModifiedTime(0)
404
      , mExpirationTime(0)
405
      , mPinned(false)
406
0
    {
407
0
    }
408
409
    NS_IMETHOD Run() override
410
0
    {
411
0
      MOZ_ASSERT(NS_IsMainThread());
412
0
413
0
      nsresult rv;
414
0
415
0
      nsCOMPtr<nsIURI> uri;
416
0
      rv = NS_NewURI(getter_AddRefs(uri), mURISpec);
417
0
      if (NS_FAILED(rv)) {
418
0
        return NS_OK;
419
0
      }
420
0
421
0
      rv = mWalker->mCallback->OnCacheEntryInfo(
422
0
        uri, mIdEnhance, mDataSize, mFetchCount,
423
0
        mLastModifiedTime, mExpirationTime, mPinned, mInfo);
424
0
      if (NS_FAILED(rv)) {
425
0
        mWalker->mCancel = true;
426
0
      }
427
0
428
0
      return NS_OK;
429
0
    }
430
431
    RefPtr<WalkDiskCacheRunnable> mWalker;
432
433
    nsCString mURISpec;
434
    nsCString mIdEnhance;
435
    int64_t mDataSize;
436
    int32_t mFetchCount;
437
    uint32_t mLastModifiedTime;
438
    uint32_t mExpirationTime;
439
    bool mPinned;
440
    nsCOMPtr<nsILoadContextInfo> mInfo;
441
  };
442
443
  NS_IMETHOD Run() override
444
0
  {
445
0
    // The main loop
446
0
    nsresult rv;
447
0
448
0
    if (CacheStorageService::IsOnManagementThread()) {
449
0
      switch (mPass) {
450
0
      case COLLECT_STATS:
451
0
        // Get quickly the cache stats.
452
0
        uint32_t size;
453
0
        rv = CacheIndex::GetCacheStats(mLoadInfo, &size, &mCount);
454
0
        if (NS_FAILED(rv)) {
455
0
          if (mVisitEntries) {
456
0
            // both onStorageInfo and onCompleted are expected
457
0
            NS_DispatchToMainThread(this);
458
0
          }
459
0
          return NS_DispatchToMainThread(this);
460
0
        }
461
0
462
0
        mSize = static_cast<uint64_t>(size) << 10;
463
0
464
0
        // Invoke onCacheStorageInfo with valid information.
465
0
        NS_DispatchToMainThread(this);
466
0
467
0
        if (!mVisitEntries) {
468
0
          return NS_OK; // done
469
0
        }
470
0
471
0
        mPass = ITERATE_METADATA;
472
0
        MOZ_FALLTHROUGH;
473
0
474
0
      case ITERATE_METADATA:
475
0
        // Now grab the context iterator.
476
0
        if (!mIter) {
477
0
          rv = CacheIndex::GetIterator(mLoadInfo, true, getter_AddRefs(mIter));
478
0
          if (NS_FAILED(rv)) {
479
0
            // Invoke onCacheEntryVisitCompleted now
480
0
            return NS_DispatchToMainThread(this);
481
0
          }
482
0
        }
483
0
484
0
        while (!mCancel && !CacheObserver::ShuttingDown()) {
485
0
          if (CacheIOThread::YieldAndRerun())
486
0
            return NS_OK;
487
0
488
0
          SHA1Sum::Hash hash;
489
0
          rv = mIter->GetNextHash(&hash);
490
0
          if (NS_FAILED(rv))
491
0
            break; // done (or error?)
492
0
493
0
          // This synchronously invokes OnEntryInfo on this class where we
494
0
          // redispatch to the main thread for the consumer callback.
495
0
          CacheFileIOManager::GetEntryInfo(&hash, this);
496
0
        }
497
0
498
0
        // Invoke onCacheEntryVisitCompleted on the main thread
499
0
        NS_DispatchToMainThread(this);
500
0
      }
501
0
    } else if (NS_IsMainThread()) {
502
0
      if (mNotifyStorage) {
503
0
        nsCOMPtr<nsIFile> dir;
504
0
        CacheFileIOManager::GetCacheDirectory(getter_AddRefs(dir));
505
0
        mCallback->OnCacheStorageInfo(mCount, mSize, CacheObserver::DiskCacheCapacity(), dir);
506
0
        mNotifyStorage = false;
507
0
      } else {
508
0
        mCallback->OnCacheEntryVisitCompleted();
509
0
      }
510
0
    } else {
511
0
      MOZ_CRASH("Bad thread");
512
0
      return NS_ERROR_FAILURE;
513
0
    }
514
0
515
0
    return NS_OK;
516
0
  }
517
518
  virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
519
                           int64_t aDataSize, int32_t aFetchCount,
520
                           uint32_t aLastModifiedTime, uint32_t aExpirationTime,
521
                           bool aPinned, nsILoadContextInfo* aInfo) override
522
0
  {
523
0
    // Called directly from CacheFileIOManager::GetEntryInfo.
524
0
525
0
    // Invoke onCacheEntryInfo on the main thread for this entry.
526
0
    RefPtr<OnCacheEntryInfoRunnable> info = new OnCacheEntryInfoRunnable(this);
527
0
    info->mURISpec = aURISpec;
528
0
    info->mIdEnhance = aIdEnhance;
529
0
    info->mDataSize = aDataSize;
530
0
    info->mFetchCount = aFetchCount;
531
0
    info->mLastModifiedTime = aLastModifiedTime;
532
0
    info->mExpirationTime = aExpirationTime;
533
0
    info->mPinned = aPinned;
534
0
    info->mInfo = aInfo;
535
0
536
0
    NS_DispatchToMainThread(info);
537
0
  }
538
539
  RefPtr<nsILoadContextInfo> mLoadInfo;
540
  enum {
541
    // First, we collect stats for the load context.
542
    COLLECT_STATS,
543
544
    // Second, if demanded, we iterate over the entries gethered
545
    // from the iterator and call CacheFileIOManager::GetEntryInfo
546
    // for each found entry.
547
    ITERATE_METADATA,
548
  } mPass;
549
550
  RefPtr<CacheIndexIterator> mIter;
551
  uint32_t mCount;
552
};
553
554
} // namespace
555
556
void CacheStorageService::DropPrivateBrowsingEntries()
557
0
{
558
0
  mozilla::MutexAutoLock lock(mLock);
559
0
560
0
  if (mShutdown)
561
0
    return;
562
0
563
0
  nsTArray<nsCString> keys;
564
0
  for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
565
0
    const nsACString& key = iter.Key();
566
0
    nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(key);
567
0
    if (info && info->IsPrivate()) {
568
0
      keys.AppendElement(key);
569
0
    }
570
0
  }
571
0
572
0
  for (uint32_t i = 0; i < keys.Length(); ++i) {
573
0
    DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
574
0
  }
575
0
}
576
577
namespace {
578
579
class CleaupCacheDirectoriesRunnable : public Runnable
580
{
581
public:
582
  NS_DECL_NSIRUNNABLE
583
  static bool Post();
584
585
private:
586
  CleaupCacheDirectoriesRunnable()
587
    : Runnable("net::CleaupCacheDirectoriesRunnable")
588
0
  {
589
0
    nsCacheService::GetDiskCacheDirectory(getter_AddRefs(mCache1Dir));
590
0
    CacheFileIOManager::GetCacheDirectory(getter_AddRefs(mCache2Dir));
591
#if defined(MOZ_WIDGET_ANDROID)
592
    CacheFileIOManager::GetProfilelessCacheDirectory(getter_AddRefs(mCache2Profileless));
593
#endif
594
  }
595
596
0
  virtual ~CleaupCacheDirectoriesRunnable() = default;
597
  nsCOMPtr<nsIFile> mCache1Dir, mCache2Dir;
598
#if defined(MOZ_WIDGET_ANDROID)
599
  nsCOMPtr<nsIFile> mCache2Profileless;
600
#endif
601
};
602
603
// static
604
bool CleaupCacheDirectoriesRunnable::Post()
605
0
{
606
0
  // To obtain the cache1 directory we must unfortunately instantiate the old cache
607
0
  // service despite it may not be used at all...  This also initializes nsDeleteDir.
608
0
  nsCOMPtr<nsICacheService> service = do_GetService(NS_CACHESERVICE_CONTRACTID);
609
0
  if (!service)
610
0
    return false;
611
0
612
0
  nsCOMPtr<nsIEventTarget> thread;
613
0
  service->GetCacheIOTarget(getter_AddRefs(thread));
614
0
  if (!thread)
615
0
    return false;
616
0
617
0
  RefPtr<CleaupCacheDirectoriesRunnable> r = new CleaupCacheDirectoriesRunnable();
618
0
  thread->Dispatch(r, NS_DISPATCH_NORMAL);
619
0
  return true;
620
0
}
621
622
NS_IMETHODIMP CleaupCacheDirectoriesRunnable::Run()
623
0
{
624
0
  MOZ_ASSERT(!NS_IsMainThread());
625
0
626
0
  if (mCache1Dir) {
627
0
    nsDeleteDir::RemoveOldTrashes(mCache1Dir);
628
0
  }
629
0
  if (mCache2Dir) {
630
0
    nsDeleteDir::RemoveOldTrashes(mCache2Dir);
631
0
  }
632
#if defined(MOZ_WIDGET_ANDROID)
633
  if (mCache2Profileless) {
634
    nsDeleteDir::RemoveOldTrashes(mCache2Profileless);
635
    // Always delete the profileless cache on Android
636
    nsDeleteDir::DeleteDir(mCache2Profileless, true, 30000);
637
  }
638
#endif
639
640
0
  if (mCache1Dir) {
641
0
    nsDeleteDir::DeleteDir(mCache1Dir, true, 30000);
642
0
  }
643
0
644
0
  return NS_OK;
645
0
}
646
647
} // namespace
648
649
// static
650
void CacheStorageService::CleaupCacheDirectories()
651
0
{
652
0
  // Make sure we schedule just once in case CleaupCacheDirectories gets called
653
0
  // multiple times from some reason.
654
0
  static bool runOnce = CleaupCacheDirectoriesRunnable::Post();
655
0
  if (!runOnce) {
656
0
    NS_WARNING("Could not start cache trashes cleanup");
657
0
  }
658
0
}
659
660
// Helper methods
661
662
// static
663
bool CacheStorageService::IsOnManagementThread()
664
0
{
665
0
  RefPtr<CacheStorageService> service = Self();
666
0
  if (!service)
667
0
    return false;
668
0
669
0
  nsCOMPtr<nsIEventTarget> target = service->Thread();
670
0
  if (!target)
671
0
    return false;
672
0
673
0
  bool currentThread;
674
0
  nsresult rv = target->IsOnCurrentThread(&currentThread);
675
0
  return NS_SUCCEEDED(rv) && currentThread;
676
0
}
677
678
already_AddRefed<nsIEventTarget> CacheStorageService::Thread() const
679
0
{
680
0
  return CacheFileIOManager::IOTarget();
681
0
}
682
683
nsresult CacheStorageService::Dispatch(nsIRunnable* aEvent)
684
0
{
685
0
  RefPtr<CacheIOThread> cacheIOThread = CacheFileIOManager::IOThread();
686
0
  if (!cacheIOThread)
687
0
    return NS_ERROR_NOT_AVAILABLE;
688
0
689
0
  return cacheIOThread->Dispatch(aEvent, CacheIOThread::MANAGEMENT);
690
0
}
691
692
// nsICacheStorageService
693
694
NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo,
695
                                                      nsICacheStorage * *_retval)
696
0
{
697
0
  NS_ENSURE_ARG(aLoadContextInfo);
698
0
  NS_ENSURE_ARG(_retval);
699
0
700
0
  nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
701
0
    aLoadContextInfo, false, false, false, false);
702
0
  storage.forget(_retval);
703
0
  return NS_OK;
704
0
}
705
706
NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadContextInfo,
707
                                                    bool aLookupAppCache,
708
                                                    nsICacheStorage * *_retval)
709
0
{
710
0
  NS_ENSURE_ARG(aLoadContextInfo);
711
0
  NS_ENSURE_ARG(_retval);
712
0
713
0
  // TODO save some heap granularity - cache commonly used storages.
714
0
715
0
  // When disk cache is disabled, still provide a storage, but just keep stuff
716
0
  // in memory.
717
0
  bool useDisk = CacheObserver::UseDiskCache();
718
0
719
0
  nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
720
0
    aLoadContextInfo, useDisk, aLookupAppCache, false /* size limit */, false /* don't pin */);
721
0
  storage.forget(_retval);
722
0
  return NS_OK;
723
0
}
724
725
NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo,
726
                                                       nsICacheStorage * *_retval)
727
0
{
728
0
  NS_ENSURE_ARG(aLoadContextInfo);
729
0
  NS_ENSURE_ARG(_retval);
730
0
731
0
  // When disk cache is disabled don't pretend we cache.
732
0
  if (!CacheObserver::UseDiskCache()) {
733
0
    return NS_ERROR_NOT_AVAILABLE;
734
0
  }
735
0
736
0
  nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
737
0
    aLoadContextInfo, true /* use disk */, false /* no appcache */, true /* ignore size checks */, true /* pin */);
738
0
  storage.forget(_retval);
739
0
  return NS_OK;
740
0
}
741
742
NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo,
743
                                                   nsIApplicationCache *aApplicationCache,
744
                                                   nsICacheStorage * *_retval)
745
0
{
746
0
  NS_ENSURE_ARG(aLoadContextInfo);
747
0
  NS_ENSURE_ARG(_retval);
748
0
749
0
  nsCOMPtr<nsICacheStorage> storage;
750
0
  // Using classification since cl believes we want to instantiate this method
751
0
  // having the same name as the desired class...
752
0
  storage = new mozilla::net::AppCacheStorage(aLoadContextInfo, aApplicationCache);
753
0
754
0
  storage.forget(_retval);
755
0
  return NS_OK;
756
0
}
757
758
NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo,
759
                                                           nsICacheStorage * *_retval)
760
0
{
761
0
  NS_ENSURE_ARG(aLoadContextInfo);
762
0
  NS_ENSURE_ARG(_retval);
763
0
764
0
  nsCOMPtr<nsICacheStorage> storage = new CacheStorage(
765
0
    aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */, false /* no pinning */);
766
0
  storage.forget(_retval);
767
0
  return NS_OK;
768
0
}
769
770
NS_IMETHODIMP CacheStorageService::Clear()
771
0
{
772
0
  nsresult rv;
773
0
774
0
  // Tell the index to block notification to AsyncGetDiskConsumption.
775
0
  // Will be allowed again from CacheFileContextEvictor::EvictEntries()
776
0
  // when all the context have been removed from disk.
777
0
  CacheIndex::OnAsyncEviction(true);
778
0
779
0
  mozilla::MutexAutoLock lock(mLock);
780
0
781
0
  {
782
0
    mozilla::MutexAutoLock forcedValidEntriesLock(mForcedValidEntriesLock);
783
0
    mForcedValidEntries.Clear();
784
0
  }
785
0
786
0
  NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
787
0
788
0
  nsTArray<nsCString> keys;
789
0
  for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
790
0
    keys.AppendElement(iter.Key());
791
0
  }
792
0
793
0
  for (uint32_t i = 0; i < keys.Length(); ++i) {
794
0
    DoomStorageEntries(keys[i], nullptr, true, false, nullptr);
795
0
  }
796
0
797
0
  // Passing null as a load info means to evict all contexts.
798
0
  // EvictByContext() respects the entry pinning.  EvictAll() does not.
799
0
  rv = CacheFileIOManager::EvictByContext(nullptr, false, EmptyString());
800
0
  NS_ENSURE_SUCCESS(rv, rv);
801
0
802
0
  return NS_OK;
803
0
}
804
805
NS_IMETHODIMP CacheStorageService::ClearOrigin(nsIPrincipal* aPrincipal)
806
0
{
807
0
  nsresult rv;
808
0
809
0
  if (NS_WARN_IF(!aPrincipal)) {
810
0
    return NS_ERROR_FAILURE;
811
0
  }
812
0
813
0
  nsAutoString origin;
814
0
  rv = nsContentUtils::GetUTFOrigin(aPrincipal, origin);
815
0
  NS_ENSURE_SUCCESS(rv, rv);
816
0
817
0
  rv = ClearOriginInternal(origin, aPrincipal->OriginAttributesRef(), true);
818
0
  NS_ENSURE_SUCCESS(rv, rv);
819
0
820
0
  rv = ClearOriginInternal(origin, aPrincipal->OriginAttributesRef(), false);
821
0
  NS_ENSURE_SUCCESS(rv, rv);
822
0
823
0
  return NS_OK;
824
0
}
825
826
static bool
827
RemoveExactEntry(CacheEntryTable* aEntries,
828
                 nsACString const& aKey,
829
                 CacheEntry* aEntry,
830
                 bool aOverwrite)
831
0
{
832
0
  RefPtr<CacheEntry> existingEntry;
833
0
  if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
834
0
    LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
835
0
    return false; // Already removed...
836
0
  }
837
0
838
0
  if (!aOverwrite && existingEntry != aEntry) {
839
0
    LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
840
0
    return false; // Already replaced...
841
0
  }
842
0
843
0
  LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
844
0
  aEntries->Remove(aKey);
845
0
  return true;
846
0
}
847
848
nsresult
849
CacheStorageService::ClearOriginInternal(const nsAString& aOrigin,
850
                                         const OriginAttributes& aOriginAttributes,
851
                                         bool aAnonymous)
852
0
{
853
0
  nsresult rv;
854
0
855
0
  RefPtr<LoadContextInfo> info =
856
0
    GetLoadContextInfo(aAnonymous, aOriginAttributes);
857
0
  if (NS_WARN_IF(!info)) {
858
0
    return NS_ERROR_FAILURE;
859
0
  }
860
0
861
0
  mozilla::MutexAutoLock lock(mLock);
862
0
863
0
  if (sGlobalEntryTables) {
864
0
    for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
865
0
      bool matches = false;
866
0
      rv = CacheFileUtils::KeyMatchesLoadContextInfo(iter.Key(), info,
867
0
                                                     &matches);
868
0
      NS_ENSURE_SUCCESS(rv, rv);
869
0
      if (!matches) {
870
0
        continue;
871
0
      }
872
0
873
0
      CacheEntryTable* table = iter.UserData();
874
0
      MOZ_ASSERT(table);
875
0
876
0
      nsTArray<RefPtr<CacheEntry>> entriesToDelete;
877
0
878
0
      for (auto entryIter = table->Iter(); !entryIter.Done(); entryIter.Next()) {
879
0
        CacheEntry* entry = entryIter.UserData();
880
0
881
0
        nsCOMPtr<nsIURI> uri;
882
0
        rv = NS_NewURI(getter_AddRefs(uri), entry->GetURI());
883
0
        NS_ENSURE_SUCCESS(rv, rv);
884
0
885
0
        nsAutoString origin;
886
0
        rv = nsContentUtils::GetUTFOrigin(uri, origin);
887
0
        NS_ENSURE_SUCCESS(rv, rv);
888
0
889
0
        if (origin != aOrigin) {
890
0
          continue;
891
0
        }
892
0
893
0
        entriesToDelete.AppendElement(entry);
894
0
      }
895
0
896
0
      for (RefPtr<CacheEntry>& entry : entriesToDelete) {
897
0
        nsAutoCString entryKey;
898
0
        rv = entry->HashingKey(entryKey);
899
0
        if (NS_FAILED(rv)) {
900
0
          NS_ERROR("aEntry->HashingKey() failed?");
901
0
          return rv;
902
0
        }
903
0
904
0
        RemoveExactEntry(table, entryKey, entry, false /* don't overwrite */);
905
0
      }
906
0
    }
907
0
  }
908
0
909
0
  rv = CacheFileIOManager::EvictByContext(info, false /* pinned */, aOrigin);
910
0
  NS_ENSURE_SUCCESS(rv, rv);
911
0
912
0
  return NS_OK;
913
0
}
914
915
NS_IMETHODIMP CacheStorageService::PurgeFromMemory(uint32_t aWhat)
916
0
{
917
0
  uint32_t what;
918
0
919
0
  switch (aWhat) {
920
0
  case PURGE_DISK_DATA_ONLY:
921
0
    what = CacheEntry::PURGE_DATA_ONLY_DISK_BACKED;
922
0
    break;
923
0
924
0
  case PURGE_DISK_ALL:
925
0
    what = CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED;
926
0
    break;
927
0
928
0
  case PURGE_EVERYTHING:
929
0
    what = CacheEntry::PURGE_WHOLE;
930
0
    break;
931
0
932
0
  default:
933
0
    return NS_ERROR_INVALID_ARG;
934
0
  }
935
0
936
0
  nsCOMPtr<nsIRunnable> event =
937
0
    new PurgeFromMemoryRunnable(this, what);
938
0
939
0
  return Dispatch(event);
940
0
}
941
942
NS_IMETHODIMP CacheStorageService::PurgeFromMemoryRunnable::Run()
943
0
{
944
0
  if (NS_IsMainThread()) {
945
0
    nsCOMPtr<nsIObserverService> observerService =
946
0
      mozilla::services::GetObserverService();
947
0
    if (observerService) {
948
0
      observerService->NotifyObservers(nullptr, "cacheservice:purge-memory-pools", nullptr);
949
0
    }
950
0
951
0
    return NS_OK;
952
0
  }
953
0
954
0
  if (mService) {
955
0
    // TODO not all flags apply to both pools
956
0
    mService->Pool(true).PurgeAll(mWhat);
957
0
    mService->Pool(false).PurgeAll(mWhat);
958
0
    mService = nullptr;
959
0
  }
960
0
961
0
  NS_DispatchToMainThread(this);
962
0
  return NS_OK;
963
0
}
964
965
NS_IMETHODIMP CacheStorageService::AsyncGetDiskConsumption(
966
  nsICacheStorageConsumptionObserver* aObserver)
967
0
{
968
0
  NS_ENSURE_ARG(aObserver);
969
0
970
0
  nsresult rv;
971
0
972
0
  rv = CacheIndex::AsyncGetDiskConsumption(aObserver);
973
0
  NS_ENSURE_SUCCESS(rv, rv);
974
0
975
0
  return NS_OK;
976
0
}
977
978
NS_IMETHODIMP CacheStorageService::GetIoTarget(nsIEventTarget** aEventTarget)
979
0
{
980
0
  NS_ENSURE_ARG(aEventTarget);
981
0
982
0
  nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
983
0
  ioTarget.forget(aEventTarget);
984
0
985
0
  return NS_OK;
986
0
}
987
988
NS_IMETHODIMP CacheStorageService::AsyncVisitAllStorages(
989
  nsICacheStorageVisitor* aVisitor,
990
  bool aVisitEntries)
991
0
{
992
0
  LOG(("CacheStorageService::AsyncVisitAllStorages [cb=%p]", aVisitor));
993
0
  NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
994
0
995
0
  // Walking the disk cache also walks the memory cache.
996
0
  RefPtr<WalkDiskCacheRunnable> event =
997
0
    new WalkDiskCacheRunnable(nullptr, aVisitEntries, aVisitor);
998
0
  return event->Walk();
999
0
1000
0
  return NS_OK;
1001
0
}
1002
1003
// Methods used by CacheEntry for management of in-memory structures.
1004
1005
namespace {
1006
1007
class FrecencyComparator
1008
{
1009
public:
1010
0
  bool Equals(CacheEntry* a, CacheEntry* b) const {
1011
0
    return a->GetFrecency() == b->GetFrecency();
1012
0
  }
1013
0
  bool LessThan(CacheEntry* a, CacheEntry* b) const {
1014
0
    return a->GetFrecency() < b->GetFrecency();
1015
0
  }
1016
};
1017
1018
class ExpirationComparator
1019
{
1020
public:
1021
0
  bool Equals(CacheEntry* a, CacheEntry* b) const {
1022
0
    return a->GetExpirationTime() == b->GetExpirationTime();
1023
0
  }
1024
0
  bool LessThan(CacheEntry* a, CacheEntry* b) const {
1025
0
    return a->GetExpirationTime() < b->GetExpirationTime();
1026
0
  }
1027
};
1028
1029
} // namespace
1030
1031
void
1032
CacheStorageService::RegisterEntry(CacheEntry* aEntry)
1033
0
{
1034
0
  MOZ_ASSERT(IsOnManagementThread());
1035
0
1036
0
  if (mShutdown || !aEntry->CanRegister())
1037
0
    return;
1038
0
1039
0
  TelemetryRecordEntryCreation(aEntry);
1040
0
1041
0
  LOG(("CacheStorageService::RegisterEntry [entry=%p]", aEntry));
1042
0
1043
0
  MemoryPool& pool = Pool(aEntry->IsUsingDisk());
1044
0
  pool.mFrecencyArray.AppendElement(aEntry);
1045
0
  pool.mExpirationArray.AppendElement(aEntry);
1046
0
1047
0
  aEntry->SetRegistered(true);
1048
0
}
1049
1050
void
1051
CacheStorageService::UnregisterEntry(CacheEntry* aEntry)
1052
0
{
1053
0
  MOZ_ASSERT(IsOnManagementThread());
1054
0
1055
0
  if (!aEntry->IsRegistered())
1056
0
    return;
1057
0
1058
0
  TelemetryRecordEntryRemoval(aEntry);
1059
0
1060
0
  LOG(("CacheStorageService::UnregisterEntry [entry=%p]", aEntry));
1061
0
1062
0
  MemoryPool& pool = Pool(aEntry->IsUsingDisk());
1063
0
  mozilla::DebugOnly<bool> removedFrecency = pool.mFrecencyArray.RemoveElement(aEntry);
1064
0
  mozilla::DebugOnly<bool> removedExpiration = pool.mExpirationArray.RemoveElement(aEntry);
1065
0
1066
0
  MOZ_ASSERT(mShutdown || (removedFrecency && removedExpiration));
1067
0
1068
0
  // Note: aEntry->CanRegister() since now returns false
1069
0
  aEntry->SetRegistered(false);
1070
0
}
1071
1072
static bool
1073
AddExactEntry(CacheEntryTable* aEntries,
1074
              nsACString const& aKey,
1075
              CacheEntry* aEntry,
1076
              bool aOverwrite)
1077
0
{
1078
0
  RefPtr<CacheEntry> existingEntry;
1079
0
  if (!aOverwrite && aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
1080
0
    bool equals = existingEntry == aEntry;
1081
0
    LOG(("AddExactEntry [entry=%p equals=%d]", aEntry, equals));
1082
0
    return equals; // Already there...
1083
0
  }
1084
0
1085
0
  LOG(("AddExactEntry [entry=%p put]", aEntry));
1086
0
  aEntries->Put(aKey, aEntry);
1087
0
  return true;
1088
0
}
1089
1090
bool
1091
CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
1092
0
{
1093
0
  LOG(("CacheStorageService::RemoveEntry [entry=%p]", aEntry));
1094
0
1095
0
  nsAutoCString entryKey;
1096
0
  nsresult rv = aEntry->HashingKey(entryKey);
1097
0
  if (NS_FAILED(rv)) {
1098
0
    NS_ERROR("aEntry->HashingKey() failed?");
1099
0
    return false;
1100
0
  }
1101
0
1102
0
  mozilla::MutexAutoLock lock(mLock);
1103
0
1104
0
  if (mShutdown) {
1105
0
    LOG(("  after shutdown"));
1106
0
    return false;
1107
0
  }
1108
0
1109
0
  if (aOnlyUnreferenced) {
1110
0
    if (aEntry->IsReferenced()) {
1111
0
      LOG(("  still referenced, not removing"));
1112
0
      return false;
1113
0
    }
1114
0
1115
0
    if (!aEntry->IsUsingDisk() && IsForcedValidEntry(aEntry->GetStorageID(), entryKey)) {
1116
0
      LOG(("  forced valid, not removing"));
1117
0
      return false;
1118
0
    }
1119
0
  }
1120
0
1121
0
  CacheEntryTable* entries;
1122
0
  if (sGlobalEntryTables->Get(aEntry->GetStorageID(), &entries))
1123
0
    RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
1124
0
1125
0
  nsAutoCString memoryStorageID(aEntry->GetStorageID());
1126
0
  AppendMemoryStorageID(memoryStorageID);
1127
0
1128
0
  if (sGlobalEntryTables->Get(memoryStorageID, &entries))
1129
0
    RemoveExactEntry(entries, entryKey, aEntry, false /* don't overwrite */);
1130
0
1131
0
  return true;
1132
0
}
1133
1134
void
1135
CacheStorageService::RecordMemoryOnlyEntry(CacheEntry* aEntry,
1136
                                           bool aOnlyInMemory,
1137
                                           bool aOverwrite)
1138
0
{
1139
0
  LOG(("CacheStorageService::RecordMemoryOnlyEntry [entry=%p, memory=%d, overwrite=%d]",
1140
0
    aEntry, aOnlyInMemory, aOverwrite));
1141
0
  // This method is responsible to put this entry to a special record hashtable
1142
0
  // that contains only entries that are stored in memory.
1143
0
  // Keep in mind that every entry, regardless of whether is in-memory-only or not
1144
0
  // is always recorded in the storage master hash table, the one identified by
1145
0
  // CacheEntry.StorageID().
1146
0
1147
0
  mLock.AssertCurrentThreadOwns();
1148
0
1149
0
  if (mShutdown) {
1150
0
    LOG(("  after shutdown"));
1151
0
    return;
1152
0
  }
1153
0
1154
0
  nsresult rv;
1155
0
1156
0
  nsAutoCString entryKey;
1157
0
  rv = aEntry->HashingKey(entryKey);
1158
0
  if (NS_FAILED(rv)) {
1159
0
    NS_ERROR("aEntry->HashingKey() failed?");
1160
0
    return;
1161
0
  }
1162
0
1163
0
  CacheEntryTable* entries = nullptr;
1164
0
  nsAutoCString memoryStorageID(aEntry->GetStorageID());
1165
0
  AppendMemoryStorageID(memoryStorageID);
1166
0
1167
0
  if (!sGlobalEntryTables->Get(memoryStorageID, &entries)) {
1168
0
    if (!aOnlyInMemory) {
1169
0
      LOG(("  not recorded as memory only"));
1170
0
      return;
1171
0
    }
1172
0
1173
0
    entries = new CacheEntryTable(CacheEntryTable::MEMORY_ONLY);
1174
0
    sGlobalEntryTables->Put(memoryStorageID, entries);
1175
0
    LOG(("  new memory-only storage table for %s", memoryStorageID.get()));
1176
0
  }
1177
0
1178
0
  if (aOnlyInMemory) {
1179
0
    AddExactEntry(entries, entryKey, aEntry, aOverwrite);
1180
0
  }
1181
0
  else {
1182
0
    RemoveExactEntry(entries, entryKey, aEntry, aOverwrite);
1183
0
  }
1184
0
}
1185
1186
// Checks if a cache entry is forced valid (will be loaded directly from cache
1187
// without further validation) - see nsICacheEntry.idl for further details
1188
bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextKey,
1189
                                             nsACString const &aEntryKey)
1190
0
{
1191
0
  return IsForcedValidEntry(aContextKey + aEntryKey);
1192
0
}
1193
1194
bool CacheStorageService::IsForcedValidEntry(nsACString const &aContextEntryKey)
1195
0
{
1196
0
  mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1197
0
1198
0
  TimeStamp validUntil;
1199
0
1200
0
  if (!mForcedValidEntries.Get(aContextEntryKey, &validUntil)) {
1201
0
    return false;
1202
0
  }
1203
0
1204
0
  if (validUntil.IsNull()) {
1205
0
    return false;
1206
0
  }
1207
0
1208
0
  // Entry timeout not reached yet
1209
0
  if (TimeStamp::NowLoRes() <= validUntil) {
1210
0
    return true;
1211
0
  }
1212
0
1213
0
  // Entry timeout has been reached
1214
0
  mForcedValidEntries.Remove(aContextEntryKey);
1215
0
  return false;
1216
0
}
1217
1218
// Allows a cache entry to be loaded directly from cache without further
1219
// validation - see nsICacheEntry.idl for further details
1220
void CacheStorageService::ForceEntryValidFor(nsACString const &aContextKey,
1221
                                             nsACString const &aEntryKey,
1222
                                             uint32_t aSecondsToTheFuture)
1223
0
{
1224
0
  mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1225
0
1226
0
  TimeStamp now = TimeStamp::NowLoRes();
1227
0
  ForcedValidEntriesPrune(now);
1228
0
1229
0
  // This will be the timeout
1230
0
  TimeStamp validUntil = now + TimeDuration::FromSeconds(aSecondsToTheFuture);
1231
0
1232
0
  mForcedValidEntries.Put(aContextKey + aEntryKey, validUntil);
1233
0
}
1234
1235
void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey,
1236
                                                nsACString const &aEntryKey)
1237
0
{
1238
0
  mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1239
0
1240
0
  LOG(("CacheStorageService::RemoveEntryForceValid context='%s' entryKey=%s",
1241
0
       aContextKey.BeginReading(), aEntryKey.BeginReading()));
1242
0
  mForcedValidEntries.Remove(aContextKey + aEntryKey);
1243
0
}
1244
1245
// Cleans out the old entries in mForcedValidEntries
1246
void CacheStorageService::ForcedValidEntriesPrune(TimeStamp &now)
1247
0
{
1248
0
  static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
1249
0
  static TimeStamp dontPruneUntil = now + oneMinute;
1250
0
  if (now < dontPruneUntil)
1251
0
    return;
1252
0
1253
0
  for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
1254
0
    if (iter.Data() < now) {
1255
0
      iter.Remove();
1256
0
    }
1257
0
  }
1258
0
  dontPruneUntil = now + oneMinute;
1259
0
}
1260
1261
void
1262
CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer,
1263
                                               uint32_t aCurrentMemoryConsumption)
1264
0
{
1265
0
  LOG(("CacheStorageService::OnMemoryConsumptionChange [consumer=%p, size=%u]",
1266
0
    aConsumer, aCurrentMemoryConsumption));
1267
0
1268
0
  uint32_t savedMemorySize = aConsumer->mReportedMemoryConsumption;
1269
0
  if (savedMemorySize == aCurrentMemoryConsumption)
1270
0
    return;
1271
0
1272
0
  // Exchange saved size with current one.
1273
0
  aConsumer->mReportedMemoryConsumption = aCurrentMemoryConsumption;
1274
0
1275
0
  bool usingDisk = !(aConsumer->mFlags & CacheMemoryConsumer::MEMORY_ONLY);
1276
0
  bool overLimit = Pool(usingDisk).OnMemoryConsumptionChange(
1277
0
    savedMemorySize, aCurrentMemoryConsumption);
1278
0
1279
0
  if (!overLimit)
1280
0
    return;
1281
0
1282
0
  // It's likely the timer has already been set when we get here,
1283
0
  // check outside the lock to save resources.
1284
0
  if (mPurgeTimer)
1285
0
    return;
1286
0
1287
0
  // We don't know if this is called under the service lock or not,
1288
0
  // hence rather dispatch.
1289
0
  RefPtr<nsIEventTarget> cacheIOTarget = Thread();
1290
0
  if (!cacheIOTarget)
1291
0
    return;
1292
0
1293
0
  // Dispatch as a priority task, we want to set the purge timer
1294
0
  // ASAP to prevent vain redispatch of this event.
1295
0
  nsCOMPtr<nsIRunnable> event =
1296
0
    NewRunnableMethod("net::CacheStorageService::SchedulePurgeOverMemoryLimit",
1297
0
                      this,
1298
0
                      &CacheStorageService::SchedulePurgeOverMemoryLimit);
1299
0
  cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1300
0
}
1301
1302
bool
1303
CacheStorageService::MemoryPool::OnMemoryConsumptionChange(uint32_t aSavedMemorySize,
1304
                                                           uint32_t aCurrentMemoryConsumption)
1305
0
{
1306
0
  mMemorySize -= aSavedMemorySize;
1307
0
  mMemorySize += aCurrentMemoryConsumption;
1308
0
1309
0
  LOG(("  mMemorySize=%u (+%u,-%u)", uint32_t(mMemorySize), aCurrentMemoryConsumption, aSavedMemorySize));
1310
0
1311
0
  // Bypass purging when memory has not grew up significantly
1312
0
  if (aCurrentMemoryConsumption <= aSavedMemorySize)
1313
0
    return false;
1314
0
1315
0
  return mMemorySize > Limit();
1316
0
}
1317
1318
void
1319
CacheStorageService::SchedulePurgeOverMemoryLimit()
1320
0
{
1321
0
  LOG(("CacheStorageService::SchedulePurgeOverMemoryLimit"));
1322
0
1323
0
  mozilla::MutexAutoLock lock(mLock);
1324
0
1325
0
  if (mShutdown) {
1326
0
    LOG(("  past shutdown"));
1327
0
    return;
1328
0
  }
1329
0
1330
0
  if (mPurgeTimer) {
1331
0
    LOG(("  timer already up"));
1332
0
    return;
1333
0
  }
1334
0
1335
0
  mPurgeTimer = NS_NewTimer();
1336
0
  if (mPurgeTimer) {
1337
0
    nsresult rv;
1338
0
    rv = mPurgeTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
1339
0
    LOG(("  timer init rv=0x%08" PRIx32, static_cast<uint32_t>(rv)));
1340
0
  }
1341
0
}
1342
1343
NS_IMETHODIMP
1344
CacheStorageService::Notify(nsITimer* aTimer)
1345
0
{
1346
0
  LOG(("CacheStorageService::Notify"));
1347
0
1348
0
  mozilla::MutexAutoLock lock(mLock);
1349
0
1350
0
  if (aTimer == mPurgeTimer) {
1351
0
    mPurgeTimer = nullptr;
1352
0
1353
0
    nsCOMPtr<nsIRunnable> event =
1354
0
      NewRunnableMethod("net::CacheStorageService::PurgeOverMemoryLimit",
1355
0
                        this,
1356
0
                        &CacheStorageService::PurgeOverMemoryLimit);
1357
0
    Dispatch(event);
1358
0
  }
1359
0
1360
0
  return NS_OK;
1361
0
}
1362
1363
NS_IMETHODIMP
1364
CacheStorageService::GetName(nsACString& aName)
1365
0
{
1366
0
  aName.AssignLiteral("CacheStorageService");
1367
0
  return NS_OK;
1368
0
}
1369
1370
void
1371
CacheStorageService::PurgeOverMemoryLimit()
1372
0
{
1373
0
  MOZ_ASSERT(IsOnManagementThread());
1374
0
1375
0
  LOG(("CacheStorageService::PurgeOverMemoryLimit"));
1376
0
1377
0
  static TimeDuration const kFourSeconds = TimeDuration::FromSeconds(4);
1378
0
  TimeStamp now = TimeStamp::NowLoRes();
1379
0
1380
0
  if (!mLastPurgeTime.IsNull() && now - mLastPurgeTime < kFourSeconds) {
1381
0
    LOG(("  bypassed, too soon"));
1382
0
    return;
1383
0
  }
1384
0
1385
0
  mLastPurgeTime = now;
1386
0
1387
0
  Pool(true).PurgeOverMemoryLimit();
1388
0
  Pool(false).PurgeOverMemoryLimit();
1389
0
}
1390
1391
void
1392
CacheStorageService::MemoryPool::PurgeOverMemoryLimit()
1393
0
{
1394
0
  TimeStamp start(TimeStamp::Now());
1395
0
1396
0
  uint32_t const memoryLimit = Limit();
1397
0
  if (mMemorySize > memoryLimit) {
1398
0
    LOG(("  memory data consumption over the limit, abandon expired entries"));
1399
0
    PurgeExpired();
1400
0
  }
1401
0
1402
0
  bool frecencyNeedsSort = true;
1403
0
1404
0
  // No longer makes sense since:
1405
0
  // Memory entries are never purged partially, only as a whole when the memory
1406
0
  // cache limit is overreached.
1407
0
  // Disk entries throw the data away ASAP so that only metadata are kept.
1408
0
  // TODO when this concept of two separate pools is found working, the code should
1409
0
  // clean up.
1410
#if 0
1411
  if (mMemorySize > memoryLimit) {
1412
    LOG(("  memory data consumption over the limit, abandon disk backed data"));
1413
    PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_DATA_ONLY_DISK_BACKED);
1414
  }
1415
1416
  if (mMemorySize > memoryLimit) {
1417
    LOG(("  metadata consumtion over the limit, abandon disk backed entries"));
1418
    PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE_ONLY_DISK_BACKED);
1419
  }
1420
#endif
1421
1422
0
  if (mMemorySize > memoryLimit) {
1423
0
    LOG(("  memory data consumption over the limit, abandon any entry"));
1424
0
    PurgeByFrecency(frecencyNeedsSort, CacheEntry::PURGE_WHOLE);
1425
0
  }
1426
0
1427
0
  LOG(("  purging took %1.2fms", (TimeStamp::Now() - start).ToMilliseconds()));
1428
0
}
1429
1430
void
1431
CacheStorageService::MemoryPool::PurgeExpired()
1432
0
{
1433
0
  MOZ_ASSERT(IsOnManagementThread());
1434
0
1435
0
  mExpirationArray.Sort(ExpirationComparator());
1436
0
  uint32_t now = NowInSeconds();
1437
0
1438
0
  uint32_t const memoryLimit = Limit();
1439
0
1440
0
  for (uint32_t i = 0; mMemorySize > memoryLimit && i < mExpirationArray.Length();) {
1441
0
    if (CacheIOThread::YieldAndRerun())
1442
0
      return;
1443
0
1444
0
    RefPtr<CacheEntry> entry = mExpirationArray[i];
1445
0
1446
0
    uint32_t expirationTime = entry->GetExpirationTime();
1447
0
    if (expirationTime > 0 && expirationTime <= now &&
1448
0
        entry->Purge(CacheEntry::PURGE_WHOLE)) {
1449
0
      LOG(("  purged expired, entry=%p, exptime=%u (now=%u)",
1450
0
        entry.get(), entry->GetExpirationTime(), now));
1451
0
      continue;
1452
0
    }
1453
0
1454
0
    // not purged, move to the next one
1455
0
    ++i;
1456
0
  }
1457
0
}
1458
1459
void
1460
CacheStorageService::MemoryPool::PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat)
1461
0
{
1462
0
  MOZ_ASSERT(IsOnManagementThread());
1463
0
1464
0
  if (aFrecencyNeedsSort) {
1465
0
    mFrecencyArray.Sort(FrecencyComparator());
1466
0
    aFrecencyNeedsSort = false;
1467
0
  }
1468
0
1469
0
  uint32_t const memoryLimit = Limit();
1470
0
1471
0
  for (uint32_t i = 0; mMemorySize > memoryLimit && i < mFrecencyArray.Length();) {
1472
0
    if (CacheIOThread::YieldAndRerun())
1473
0
      return;
1474
0
1475
0
    RefPtr<CacheEntry> entry = mFrecencyArray[i];
1476
0
1477
0
    if (entry->Purge(aWhat)) {
1478
0
      LOG(("  abandoned (%d), entry=%p, frecency=%1.10f",
1479
0
        aWhat, entry.get(), entry->GetFrecency()));
1480
0
      continue;
1481
0
    }
1482
0
1483
0
    // not purged, move to the next one
1484
0
    ++i;
1485
0
  }
1486
0
}
1487
1488
void
1489
CacheStorageService::MemoryPool::PurgeAll(uint32_t aWhat)
1490
0
{
1491
0
  LOG(("CacheStorageService::MemoryPool::PurgeAll aWhat=%d", aWhat));
1492
0
  MOZ_ASSERT(IsOnManagementThread());
1493
0
1494
0
  for (uint32_t i = 0; i < mFrecencyArray.Length();) {
1495
0
    if (CacheIOThread::YieldAndRerun())
1496
0
      return;
1497
0
1498
0
    RefPtr<CacheEntry> entry = mFrecencyArray[i];
1499
0
1500
0
    if (entry->Purge(aWhat)) {
1501
0
      LOG(("  abandoned entry=%p", entry.get()));
1502
0
      continue;
1503
0
    }
1504
0
1505
0
    // not purged, move to the next one
1506
0
    ++i;
1507
0
  }
1508
0
}
1509
1510
// Methods exposed to and used by CacheStorage.
1511
1512
nsresult
1513
CacheStorageService::AddStorageEntry(CacheStorage const* aStorage,
1514
                                     const nsACString & aURI,
1515
                                     const nsACString & aIdExtension,
1516
                                     bool aReplace,
1517
                                     CacheEntryHandle** aResult)
1518
0
{
1519
0
  NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1520
0
1521
0
  NS_ENSURE_ARG(aStorage);
1522
0
1523
0
  nsAutoCString contextKey;
1524
0
  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1525
0
1526
0
  return AddStorageEntry(contextKey, aURI, aIdExtension,
1527
0
                         aStorage->WriteToDisk(),
1528
0
                         aStorage->SkipSizeCheck(),
1529
0
                         aStorage->Pinning(),
1530
0
                         aReplace,
1531
0
                         aResult);
1532
0
}
1533
1534
nsresult
1535
CacheStorageService::AddStorageEntry(const nsACString& aContextKey,
1536
                                     const nsACString & aURI,
1537
                                     const nsACString & aIdExtension,
1538
                                     bool aWriteToDisk,
1539
                                     bool aSkipSizeCheck,
1540
                                     bool aPin,
1541
                                     bool aReplace,
1542
                                     CacheEntryHandle** aResult)
1543
0
{
1544
0
  nsresult rv;
1545
0
1546
0
  nsAutoCString entryKey;
1547
0
  rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1548
0
  NS_ENSURE_SUCCESS(rv, rv);
1549
0
1550
0
  LOG(("CacheStorageService::AddStorageEntry [entryKey=%s, contextKey=%s]",
1551
0
    entryKey.get(), aContextKey.BeginReading()));
1552
0
1553
0
  RefPtr<CacheEntry> entry;
1554
0
  RefPtr<CacheEntryHandle> handle;
1555
0
1556
0
  {
1557
0
    mozilla::MutexAutoLock lock(mLock);
1558
0
1559
0
    NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1560
0
1561
0
    // Ensure storage table
1562
0
    CacheEntryTable* entries;
1563
0
    if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
1564
0
      entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
1565
0
      sGlobalEntryTables->Put(aContextKey, entries);
1566
0
      LOG(("  new storage entries table for context '%s'", aContextKey.BeginReading()));
1567
0
    }
1568
0
1569
0
    bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
1570
0
1571
0
    if (entryExists && !aReplace) {
1572
0
      // check whether we want to turn this entry to a memory-only.
1573
0
      if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
1574
0
        LOG(("  entry is persistent but we want mem-only, replacing it"));
1575
0
        aReplace = true;
1576
0
      }
1577
0
    }
1578
0
1579
0
    // If truncate is demanded, delete and doom the current entry
1580
0
    if (entryExists && aReplace) {
1581
0
      entries->Remove(entryKey);
1582
0
1583
0
      LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
1584
0
      // On purpose called under the lock to prevent races of doom and open on I/O thread
1585
0
      // No need to remove from both memory-only and all-entries tables.  The new entry
1586
0
      // will overwrite the shadow entry in its ctor.
1587
0
      entry->DoomAlreadyRemoved();
1588
0
1589
0
      entry = nullptr;
1590
0
      entryExists = false;
1591
0
1592
0
      // Would only lead to deleting force-valid timestamp again.  We don't need the
1593
0
      // replace information anymore after this point anyway.
1594
0
      aReplace = false;
1595
0
    }
1596
0
1597
0
    // Ensure entry for the particular URL
1598
0
    if (!entryExists) {
1599
0
      // When replacing with a new entry, always remove the current force-valid timestamp,
1600
0
      // this is the only place to do it.
1601
0
      if (aReplace) {
1602
0
        RemoveEntryForceValid(aContextKey, entryKey);
1603
0
      }
1604
0
1605
0
      // Entry is not in the hashtable or has just been truncated...
1606
0
      entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck, aPin);
1607
0
      entries->Put(entryKey, entry);
1608
0
      LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
1609
0
    }
1610
0
1611
0
    if (entry) {
1612
0
      // Here, if this entry was not for a long time referenced by any consumer,
1613
0
      // gets again first 'handles count' reference.
1614
0
      handle = entry->NewHandle();
1615
0
    }
1616
0
  }
1617
0
1618
0
  handle.forget(aResult);
1619
0
  return NS_OK;
1620
0
}
1621
1622
nsresult
1623
CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage,
1624
                                       const nsACString & aURI,
1625
                                       const nsACString & aIdExtension,
1626
                                       bool* aResult)
1627
0
{
1628
0
  nsresult rv;
1629
0
1630
0
  nsAutoCString contextKey;
1631
0
  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1632
0
1633
0
  if (!aStorage->WriteToDisk()) {
1634
0
    AppendMemoryStorageID(contextKey);
1635
0
  }
1636
0
1637
0
  LOG(("CacheStorageService::CheckStorageEntry [uri=%s, eid=%s, contextKey=%s]",
1638
0
    aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
1639
0
1640
0
  {
1641
0
    mozilla::MutexAutoLock lock(mLock);
1642
0
1643
0
    NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1644
0
1645
0
    nsAutoCString entryKey;
1646
0
    rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1647
0
    NS_ENSURE_SUCCESS(rv, rv);
1648
0
1649
0
    CacheEntryTable* entries;
1650
0
    if ((*aResult = sGlobalEntryTables->Get(contextKey, &entries)) &&
1651
0
        entries->GetWeak(entryKey, aResult)) {
1652
0
      LOG(("  found in hash tables"));
1653
0
      return NS_OK;
1654
0
    }
1655
0
  }
1656
0
1657
0
  if (!aStorage->WriteToDisk()) {
1658
0
    // Memory entry, nothing more to do.
1659
0
    LOG(("  not found in hash tables"));
1660
0
    return NS_OK;
1661
0
  }
1662
0
1663
0
  // Disk entry, not found in the hashtable, check the index.
1664
0
  nsAutoCString fileKey;
1665
0
  rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
1666
0
1667
0
  CacheIndex::EntryStatus status;
1668
0
  rv = CacheIndex::HasEntry(fileKey, &status);
1669
0
  if (NS_FAILED(rv) || status == CacheIndex::DO_NOT_KNOW) {
1670
0
    LOG(("  index doesn't know, rv=0x%08" PRIx32, static_cast<uint32_t>(rv)));
1671
0
    return NS_ERROR_NOT_AVAILABLE;
1672
0
  }
1673
0
1674
0
  *aResult = status == CacheIndex::EXISTS;
1675
0
  LOG(("  %sfound in index", *aResult ? "" : "not "));
1676
0
  return NS_OK;
1677
0
}
1678
1679
nsresult
1680
CacheStorageService::GetCacheIndexEntryAttrs(CacheStorage const* aStorage,
1681
                                             const nsACString &aURI,
1682
                                             const nsACString &aIdExtension,
1683
                                             bool *aHasAltData,
1684
                                             uint32_t *aFileSizeKb)
1685
0
{
1686
0
  nsresult rv;
1687
0
1688
0
  nsAutoCString contextKey;
1689
0
  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1690
0
1691
0
  LOG(("CacheStorageService::GetCacheIndexEntryAttrs [uri=%s, eid=%s, contextKey=%s]",
1692
0
    aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
1693
0
1694
0
  nsAutoCString fileKey;
1695
0
  rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
1696
0
  if (NS_FAILED(rv)) {
1697
0
    return rv;
1698
0
  }
1699
0
1700
0
  *aHasAltData = false;
1701
0
  *aFileSizeKb = 0;
1702
0
  auto closure = [&aHasAltData, &aFileSizeKb](const CacheIndexEntry *entry) {
1703
0
    *aHasAltData = entry->GetHasAltData();
1704
0
    *aFileSizeKb = entry->GetFileSize();
1705
0
  };
1706
0
1707
0
  CacheIndex::EntryStatus status;
1708
0
  rv = CacheIndex::HasEntry(fileKey, &status, closure);
1709
0
  if (NS_FAILED(rv)) {
1710
0
    return rv;
1711
0
  }
1712
0
1713
0
  if (status != CacheIndex::EXISTS) {
1714
0
    return NS_ERROR_CACHE_KEY_NOT_FOUND;
1715
0
  }
1716
0
1717
0
  return NS_OK;
1718
0
}
1719
1720
1721
namespace {
1722
1723
class CacheEntryDoomByKeyCallback : public CacheFileIOListener
1724
                                  , public nsIRunnable
1725
{
1726
public:
1727
  NS_DECL_THREADSAFE_ISUPPORTS
1728
  NS_DECL_NSIRUNNABLE
1729
1730
  explicit CacheEntryDoomByKeyCallback(nsICacheEntryDoomCallback* aCallback)
1731
    : mCallback(aCallback)
1732
    , mResult(NS_ERROR_NOT_INITIALIZED)
1733
0
  {
1734
0
  }
1735
1736
private:
1737
  virtual ~CacheEntryDoomByKeyCallback();
1738
1739
0
  NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1740
0
  NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, nsresult aResult) override { return NS_OK; }
1741
0
  NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override { return NS_OK; }
1742
  NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
1743
0
  NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1744
0
  NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override { return NS_OK; }
1745
1746
  nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1747
  nsresult mResult;
1748
};
1749
1750
CacheEntryDoomByKeyCallback::~CacheEntryDoomByKeyCallback()
1751
0
{
1752
0
  if (mCallback)
1753
0
    ProxyReleaseMainThread(
1754
0
      "CacheEntryDoomByKeyCallback::mCallback", mCallback);
1755
0
}
1756
1757
NS_IMETHODIMP CacheEntryDoomByKeyCallback::OnFileDoomed(CacheFileHandle *aHandle,
1758
                                                        nsresult aResult)
1759
0
{
1760
0
  if (!mCallback)
1761
0
    return NS_OK;
1762
0
1763
0
  mResult = aResult;
1764
0
  if (NS_IsMainThread()) {
1765
0
    Run();
1766
0
  } else {
1767
0
    NS_DispatchToMainThread(this);
1768
0
  }
1769
0
1770
0
  return NS_OK;
1771
0
}
1772
1773
NS_IMETHODIMP CacheEntryDoomByKeyCallback::Run()
1774
0
{
1775
0
  mCallback->OnCacheEntryDoomed(mResult);
1776
0
  return NS_OK;
1777
0
}
1778
1779
NS_IMPL_ISUPPORTS(CacheEntryDoomByKeyCallback, CacheFileIOListener, nsIRunnable);
1780
1781
} // namespace
1782
1783
nsresult
1784
CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage,
1785
                                      const nsACString & aURI,
1786
                                      const nsACString & aIdExtension,
1787
                                      nsICacheEntryDoomCallback* aCallback)
1788
0
{
1789
0
  LOG(("CacheStorageService::DoomStorageEntry"));
1790
0
1791
0
  NS_ENSURE_ARG(aStorage);
1792
0
1793
0
  nsAutoCString contextKey;
1794
0
  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1795
0
1796
0
  nsAutoCString entryKey;
1797
0
  nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey);
1798
0
  NS_ENSURE_SUCCESS(rv, rv);
1799
0
1800
0
  RefPtr<CacheEntry> entry;
1801
0
  {
1802
0
    mozilla::MutexAutoLock lock(mLock);
1803
0
1804
0
    NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1805
0
1806
0
    CacheEntryTable* entries;
1807
0
    if (sGlobalEntryTables->Get(contextKey, &entries)) {
1808
0
      if (entries->Get(entryKey, getter_AddRefs(entry))) {
1809
0
        if (aStorage->WriteToDisk() || !entry->IsUsingDisk()) {
1810
0
          // When evicting from disk storage, purge
1811
0
          // When evicting from memory storage and the entry is memory-only, purge
1812
0
          LOG(("  purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
1813
0
            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
1814
0
          entries->Remove(entryKey);
1815
0
        }
1816
0
        else {
1817
0
          // Otherwise, leave it
1818
0
          LOG(("  leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
1819
0
            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
1820
0
          entry = nullptr;
1821
0
        }
1822
0
      }
1823
0
    }
1824
0
1825
0
    if (!entry) {
1826
0
      RemoveEntryForceValid(contextKey, entryKey);
1827
0
    }
1828
0
  }
1829
0
1830
0
  if (entry) {
1831
0
    LOG(("  dooming entry %p for %s", entry.get(), entryKey.get()));
1832
0
    return entry->AsyncDoom(aCallback);
1833
0
  }
1834
0
1835
0
  LOG(("  no entry loaded for %s", entryKey.get()));
1836
0
1837
0
  if (aStorage->WriteToDisk()) {
1838
0
    nsAutoCString contextKey;
1839
0
    CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1840
0
1841
0
    rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey);
1842
0
    NS_ENSURE_SUCCESS(rv, rv);
1843
0
1844
0
    LOG(("  dooming file only for %s", entryKey.get()));
1845
0
1846
0
    RefPtr<CacheEntryDoomByKeyCallback> callback(
1847
0
      new CacheEntryDoomByKeyCallback(aCallback));
1848
0
    rv = CacheFileIOManager::DoomFileByKey(entryKey, callback);
1849
0
    NS_ENSURE_SUCCESS(rv, rv);
1850
0
1851
0
    return NS_OK;
1852
0
  }
1853
0
1854
0
  class Callback : public Runnable
1855
0
  {
1856
0
  public:
1857
0
    explicit Callback(nsICacheEntryDoomCallback* aCallback)
1858
0
      : mozilla::Runnable("Callback")
1859
0
      , mCallback(aCallback)
1860
0
    {
1861
0
    }
1862
0
    NS_IMETHOD Run() override
1863
0
    {
1864
0
      mCallback->OnCacheEntryDoomed(NS_ERROR_NOT_AVAILABLE);
1865
0
      return NS_OK;
1866
0
    }
1867
0
    nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1868
0
  };
1869
0
1870
0
  if (aCallback) {
1871
0
    RefPtr<Runnable> callback = new Callback(aCallback);
1872
0
    return NS_DispatchToMainThread(callback);
1873
0
  }
1874
0
1875
0
  return NS_OK;
1876
0
}
1877
1878
nsresult
1879
CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
1880
                                        nsICacheEntryDoomCallback* aCallback)
1881
0
{
1882
0
  LOG(("CacheStorageService::DoomStorageEntries"));
1883
0
1884
0
  NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
1885
0
  NS_ENSURE_ARG(aStorage);
1886
0
1887
0
  nsAutoCString contextKey;
1888
0
  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
1889
0
1890
0
  mozilla::MutexAutoLock lock(mLock);
1891
0
1892
0
  return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
1893
0
                            aStorage->WriteToDisk(), aStorage->Pinning(),
1894
0
                            aCallback);
1895
0
}
1896
1897
nsresult
1898
CacheStorageService::DoomStorageEntries(const nsACString& aContextKey,
1899
                                        nsILoadContextInfo* aContext,
1900
                                        bool aDiskStorage,
1901
                                        bool aPinned,
1902
                                        nsICacheEntryDoomCallback* aCallback)
1903
0
{
1904
0
  LOG(("CacheStorageService::DoomStorageEntries [context=%s]", aContextKey.BeginReading()));
1905
0
1906
0
  mLock.AssertCurrentThreadOwns();
1907
0
1908
0
  NS_ENSURE_TRUE(!mShutdown, NS_ERROR_NOT_INITIALIZED);
1909
0
1910
0
  nsAutoCString memoryStorageID(aContextKey);
1911
0
  AppendMemoryStorageID(memoryStorageID);
1912
0
1913
0
  if (aDiskStorage) {
1914
0
    LOG(("  dooming disk+memory storage of %s", aContextKey.BeginReading()));
1915
0
1916
0
    // Walk one by one and remove entries according their pin status
1917
0
    CacheEntryTable *diskEntries, *memoryEntries;
1918
0
    if (sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
1919
0
      sGlobalEntryTables->Get(memoryStorageID, &memoryEntries);
1920
0
1921
0
      for (auto iter = diskEntries->Iter(); !iter.Done(); iter.Next()) {
1922
0
        auto entry = iter.Data();
1923
0
        if (entry->DeferOrBypassRemovalOnPinStatus(aPinned)) {
1924
0
          continue;
1925
0
        }
1926
0
1927
0
        if (memoryEntries) {
1928
0
          RemoveExactEntry(memoryEntries, iter.Key(), entry, false);
1929
0
        }
1930
0
        iter.Remove();
1931
0
      }
1932
0
    }
1933
0
1934
0
    if (aContext && !aContext->IsPrivate()) {
1935
0
      LOG(("  dooming disk entries"));
1936
0
      CacheFileIOManager::EvictByContext(aContext, aPinned, EmptyString());
1937
0
    }
1938
0
  } else {
1939
0
    LOG(("  dooming memory-only storage of %s", aContextKey.BeginReading()));
1940
0
1941
0
    // Remove the memory entries table from the global tables.
1942
0
    // Since we store memory entries also in the disk entries table
1943
0
    // we need to remove the memory entries from the disk table one
1944
0
    // by one manually.
1945
0
    nsAutoPtr<CacheEntryTable> memoryEntries;
1946
0
    sGlobalEntryTables->Remove(memoryStorageID, &memoryEntries);
1947
0
1948
0
    CacheEntryTable* diskEntries;
1949
0
    if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) {
1950
0
      for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) {
1951
0
        auto entry = iter.Data();
1952
0
        RemoveExactEntry(diskEntries, iter.Key(), entry, false);
1953
0
      }
1954
0
    }
1955
0
  }
1956
0
1957
0
  {
1958
0
    mozilla::MutexAutoLock lock(mForcedValidEntriesLock);
1959
0
1960
0
    if (aContext) {
1961
0
      for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) {
1962
0
        bool matches;
1963
0
        DebugOnly<nsresult> rv = CacheFileUtils::KeyMatchesLoadContextInfo(
1964
0
          iter.Key(), aContext, &matches);
1965
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
1966
0
1967
0
        if (matches) {
1968
0
          iter.Remove();
1969
0
        }
1970
0
      }
1971
0
    } else {
1972
0
      mForcedValidEntries.Clear();
1973
0
    }
1974
0
  }
1975
0
1976
0
  // An artificial callback.  This is a candidate for removal tho.  In the new
1977
0
  // cache any 'doom' or 'evict' function ensures that the entry or entries
1978
0
  // being doomed is/are not accessible after the function returns.  So there is
1979
0
  // probably no need for a callback - has no meaning.  But for compatibility
1980
0
  // with the old cache that is still in the tree we keep the API similar to be
1981
0
  // able to make tests as well as other consumers work for now.
1982
0
  class Callback : public Runnable
1983
0
  {
1984
0
  public:
1985
0
    explicit Callback(nsICacheEntryDoomCallback* aCallback)
1986
0
      : mozilla::Runnable("Callback")
1987
0
      , mCallback(aCallback)
1988
0
    {
1989
0
    }
1990
0
    NS_IMETHOD Run() override
1991
0
    {
1992
0
      mCallback->OnCacheEntryDoomed(NS_OK);
1993
0
      return NS_OK;
1994
0
    }
1995
0
    nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
1996
0
  };
1997
0
1998
0
  if (aCallback) {
1999
0
    RefPtr<Runnable> callback = new Callback(aCallback);
2000
0
    return NS_DispatchToMainThread(callback);
2001
0
  }
2002
0
2003
0
  return NS_OK;
2004
0
}
2005
2006
nsresult
2007
CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage,
2008
                                        bool aVisitEntries,
2009
                                        nsICacheStorageVisitor* aVisitor)
2010
0
{
2011
0
  LOG(("CacheStorageService::WalkStorageEntries [cb=%p, visitentries=%d]", aVisitor, aVisitEntries));
2012
0
  NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
2013
0
2014
0
  NS_ENSURE_ARG(aStorage);
2015
0
2016
0
  if (aStorage->WriteToDisk()) {
2017
0
    RefPtr<WalkDiskCacheRunnable> event =
2018
0
      new WalkDiskCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
2019
0
    return event->Walk();
2020
0
  }
2021
0
2022
0
  RefPtr<WalkMemoryCacheRunnable> event =
2023
0
    new WalkMemoryCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor);
2024
0
  return event->Walk();
2025
0
}
2026
2027
void
2028
CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo,
2029
                                     const nsACString & aIdExtension,
2030
                                     const nsACString & aURISpec)
2031
0
{
2032
0
  nsAutoCString contextKey;
2033
0
  CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
2034
0
2035
0
  nsAutoCString entryKey;
2036
0
  CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
2037
0
2038
0
  mozilla::MutexAutoLock lock(mLock);
2039
0
2040
0
  if (mShutdown) {
2041
0
    return;
2042
0
  }
2043
0
2044
0
  CacheEntryTable* entries;
2045
0
  RefPtr<CacheEntry> entry;
2046
0
2047
0
  if (sGlobalEntryTables->Get(contextKey, &entries) &&
2048
0
      entries->Get(entryKey, getter_AddRefs(entry))) {
2049
0
    if (entry->IsFileDoomed()) {
2050
0
      // Need to remove under the lock to avoid possible race leading
2051
0
      // to duplication of the entry per its key.
2052
0
      RemoveExactEntry(entries, entryKey, entry, false);
2053
0
      entry->DoomAlreadyRemoved();
2054
0
    }
2055
0
2056
0
    // Entry found, but it's not the entry that has been found doomed
2057
0
    // by the lower eviction layer.  Just leave everything unchanged.
2058
0
    return;
2059
0
  }
2060
0
2061
0
  RemoveEntryForceValid(contextKey, entryKey);
2062
0
}
2063
2064
bool
2065
CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo,
2066
                                       const nsACString & aIdExtension,
2067
                                       const nsACString & aURISpec,
2068
                                       EntryInfoCallback *aCallback)
2069
0
{
2070
0
  nsAutoCString contextKey;
2071
0
  CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey);
2072
0
2073
0
  nsAutoCString entryKey;
2074
0
  CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey);
2075
0
2076
0
  RefPtr<CacheEntry> entry;
2077
0
  {
2078
0
    mozilla::MutexAutoLock lock(mLock);
2079
0
2080
0
    if (mShutdown) {
2081
0
      return false;
2082
0
    }
2083
0
2084
0
    CacheEntryTable* entries;
2085
0
    if (!sGlobalEntryTables->Get(contextKey, &entries)) {
2086
0
      return false;
2087
0
    }
2088
0
2089
0
    if (!entries->Get(entryKey, getter_AddRefs(entry))) {
2090
0
      return false;
2091
0
    }
2092
0
  }
2093
0
2094
0
  GetCacheEntryInfo(entry, aCallback);
2095
0
  return true;
2096
0
}
2097
2098
// static
2099
void
2100
CacheStorageService::GetCacheEntryInfo(CacheEntry* aEntry,
2101
                                       EntryInfoCallback *aCallback)
2102
0
{
2103
0
  nsCString const uriSpec = aEntry->GetURI();
2104
0
  nsCString const enhanceId = aEntry->GetEnhanceID();
2105
0
2106
0
  nsAutoCString entryKey;
2107
0
  aEntry->HashingKeyWithStorage(entryKey);
2108
0
2109
0
  nsCOMPtr<nsILoadContextInfo> info =
2110
0
    CacheFileUtils::ParseKey(entryKey);
2111
0
2112
0
  uint32_t dataSize;
2113
0
  if (NS_FAILED(aEntry->GetStorageDataSize(&dataSize))) {
2114
0
    dataSize = 0;
2115
0
  }
2116
0
  int32_t fetchCount;
2117
0
  if (NS_FAILED(aEntry->GetFetchCount(&fetchCount))) {
2118
0
    fetchCount = 0;
2119
0
  }
2120
0
  uint32_t lastModified;
2121
0
  if (NS_FAILED(aEntry->GetLastModified(&lastModified))) {
2122
0
    lastModified = 0;
2123
0
  }
2124
0
  uint32_t expirationTime;
2125
0
  if (NS_FAILED(aEntry->GetExpirationTime(&expirationTime))) {
2126
0
    expirationTime = 0;
2127
0
  }
2128
0
2129
0
  aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize,
2130
0
                         fetchCount, lastModified, expirationTime,
2131
0
                         aEntry->IsPinned(), info);
2132
0
}
2133
2134
// static
2135
uint32_t CacheStorageService::CacheQueueSize(bool highPriority)
2136
0
{
2137
0
  RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2138
0
  // The thread will be null at shutdown.
2139
0
  if (!thread) {
2140
0
    return 0;
2141
0
  }
2142
0
  return thread->QueueSize(highPriority);
2143
0
}
2144
2145
// Telemetry collection
2146
2147
namespace {
2148
2149
bool TelemetryEntryKey(CacheEntry const* entry, nsAutoCString& key)
2150
0
{
2151
0
  nsAutoCString entryKey;
2152
0
  nsresult rv = entry->HashingKey(entryKey);
2153
0
  if (NS_FAILED(rv))
2154
0
    return false;
2155
0
2156
0
  if (entry->GetStorageID().IsEmpty()) {
2157
0
    // Hopefully this will be const-copied, saves some memory
2158
0
    key = entryKey;
2159
0
  } else {
2160
0
    key.Assign(entry->GetStorageID());
2161
0
    key.Append(':');
2162
0
    key.Append(entryKey);
2163
0
  }
2164
0
2165
0
  return true;
2166
0
}
2167
2168
} // namespace
2169
2170
void
2171
CacheStorageService::TelemetryPrune(TimeStamp &now)
2172
0
{
2173
0
  static TimeDuration const oneMinute = TimeDuration::FromSeconds(60);
2174
0
  static TimeStamp dontPruneUntil = now + oneMinute;
2175
0
  if (now < dontPruneUntil)
2176
0
    return;
2177
0
2178
0
  static TimeDuration const fifteenMinutes = TimeDuration::FromSeconds(900);
2179
0
  for (auto iter = mPurgeTimeStamps.Iter(); !iter.Done(); iter.Next()) {
2180
0
    if (now - iter.Data() > fifteenMinutes) {
2181
0
      // We are not interested in resurrection of entries after 15 minutes
2182
0
      // of time.  This is also the limit for the telemetry.
2183
0
      iter.Remove();
2184
0
    }
2185
0
  }
2186
0
  dontPruneUntil = now + oneMinute;
2187
0
}
2188
2189
void
2190
CacheStorageService::TelemetryRecordEntryCreation(CacheEntry const* entry)
2191
0
{
2192
0
  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
2193
0
2194
0
  nsAutoCString key;
2195
0
  if (!TelemetryEntryKey(entry, key))
2196
0
    return;
2197
0
2198
0
  TimeStamp now = TimeStamp::NowLoRes();
2199
0
  TelemetryPrune(now);
2200
0
2201
0
  // When an entry is craeted (registered actually) we check if there is
2202
0
  // a timestamp marked when this very same cache entry has been removed
2203
0
  // (deregistered) because of over-memory-limit purging.  If there is such
2204
0
  // a timestamp found accumulate telemetry on how long the entry was away.
2205
0
  TimeStamp timeStamp;
2206
0
  if (!mPurgeTimeStamps.Get(key, &timeStamp))
2207
0
    return;
2208
0
2209
0
  mPurgeTimeStamps.Remove(key);
2210
0
2211
0
  Telemetry::AccumulateTimeDelta(Telemetry::HTTP_CACHE_ENTRY_RELOAD_TIME,
2212
0
                                 timeStamp, TimeStamp::NowLoRes());
2213
0
}
2214
2215
void
2216
CacheStorageService::TelemetryRecordEntryRemoval(CacheEntry const* entry)
2217
0
{
2218
0
  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
2219
0
2220
0
  // Doomed entries must not be considered, we are only interested in purged
2221
0
  // entries.  Note that the mIsDoomed flag is always set before deregistration
2222
0
  // happens.
2223
0
  if (entry->IsDoomed())
2224
0
    return;
2225
0
2226
0
  nsAutoCString key;
2227
0
  if (!TelemetryEntryKey(entry, key))
2228
0
    return;
2229
0
2230
0
  // When an entry is removed (deregistered actually) we put a timestamp for this
2231
0
  // entry to the hashtable so that when the entry is created (registered) again
2232
0
  // we know how long it was away.  Also accumulate number of AsyncOpen calls on
2233
0
  // the entry, this tells us how efficiently the pool actually works.
2234
0
2235
0
  TimeStamp now = TimeStamp::NowLoRes();
2236
0
  TelemetryPrune(now);
2237
0
  mPurgeTimeStamps.Put(key, now);
2238
0
2239
0
  Telemetry::Accumulate(Telemetry::HTTP_CACHE_ENTRY_REUSE_COUNT, entry->UseCount());
2240
0
  Telemetry::AccumulateTimeDelta(Telemetry::HTTP_CACHE_ENTRY_ALIVE_TIME,
2241
0
                                 entry->LoadStart(), TimeStamp::NowLoRes());
2242
0
}
2243
2244
// nsIMemoryReporter
2245
2246
size_t
2247
CacheStorageService::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
2248
0
{
2249
0
  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
2250
0
2251
0
  size_t n = 0;
2252
0
  // The elemets are referenced by sGlobalEntryTables and are reported from there
2253
0
  n += Pool(true).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2254
0
  n += Pool(true).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2255
0
  n += Pool(false).mFrecencyArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2256
0
  n += Pool(false).mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
2257
0
  // Entries reported manually in CacheStorageService::CollectReports callback
2258
0
  if (sGlobalEntryTables) {
2259
0
    n += sGlobalEntryTables->ShallowSizeOfIncludingThis(mallocSizeOf);
2260
0
  }
2261
0
  n += mPurgeTimeStamps.SizeOfExcludingThis(mallocSizeOf);
2262
0
2263
0
  return n;
2264
0
}
2265
2266
size_t
2267
CacheStorageService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
2268
0
{
2269
0
  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
2270
0
}
2271
2272
NS_IMETHODIMP
2273
CacheStorageService::CollectReports(nsIHandleReportCallback* aHandleReport,
2274
                                    nsISupports* aData, bool aAnonymize)
2275
0
{
2276
0
  MOZ_COLLECT_REPORT(
2277
0
    "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
2278
0
    CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
2279
0
    "Memory used by the cache IO manager.");
2280
0
2281
0
  MOZ_COLLECT_REPORT(
2282
0
    "explicit/network/cache2/index", KIND_HEAP, UNITS_BYTES,
2283
0
    CacheIndex::SizeOfIncludingThis(MallocSizeOf),
2284
0
    "Memory used by the cache index.");
2285
0
2286
0
  MutexAutoLock lock(mLock);
2287
0
2288
0
  // Report the service instance, this doesn't report entries, done lower
2289
0
  MOZ_COLLECT_REPORT(
2290
0
    "explicit/network/cache2/service", KIND_HEAP, UNITS_BYTES,
2291
0
    SizeOfIncludingThis(MallocSizeOf),
2292
0
    "Memory used by the cache storage service.");
2293
0
2294
0
  // Report all entries, each storage separately (by the context key)
2295
0
  //
2296
0
  // References are:
2297
0
  // sGlobalEntryTables to N CacheEntryTable
2298
0
  // CacheEntryTable to N CacheEntry
2299
0
  // CacheEntry to 1 CacheFile
2300
0
  // CacheFile to
2301
0
  //   N CacheFileChunk (keeping the actual data)
2302
0
  //   1 CacheFileMetadata (keeping http headers etc.)
2303
0
  //   1 CacheFileOutputStream
2304
0
  //   N CacheFileInputStream
2305
0
  if (sGlobalEntryTables) {
2306
0
    for (auto iter1 = sGlobalEntryTables->Iter(); !iter1.Done(); iter1.Next()) {
2307
0
      CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
2308
0
2309
0
      CacheEntryTable* table = iter1.UserData();
2310
0
2311
0
      size_t size = 0;
2312
0
      mozilla::MallocSizeOf mallocSizeOf = CacheStorageService::MallocSizeOf;
2313
0
2314
0
      size += table->ShallowSizeOfIncludingThis(mallocSizeOf);
2315
0
      for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
2316
0
        size += iter2.Key().SizeOfExcludingThisIfUnshared(mallocSizeOf);
2317
0
2318
0
        // Bypass memory-only entries, those will be reported when iterating the
2319
0
        // memory only table. Memory-only entries are stored in both ALL_ENTRIES
2320
0
        // and MEMORY_ONLY hashtables.
2321
0
        RefPtr<mozilla::net::CacheEntry> const& entry = iter2.Data();
2322
0
        if (table->Type() == CacheEntryTable::MEMORY_ONLY ||
2323
0
            entry->IsUsingDisk()) {
2324
0
          size += entry->SizeOfIncludingThis(mallocSizeOf);
2325
0
        }
2326
0
      }
2327
0
2328
0
      // These key names are not privacy-sensitive.
2329
0
      aHandleReport->Callback(
2330
0
        EmptyCString(),
2331
0
        nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
2332
0
          table->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
2333
0
          iter1.Key().BeginReading()),
2334
0
        nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, size,
2335
0
        NS_LITERAL_CSTRING("Memory used by the cache storage."),
2336
0
        aData);
2337
0
    }
2338
0
  }
2339
0
2340
0
  return NS_OK;
2341
0
}
2342
2343
// nsICacheTesting
2344
2345
NS_IMETHODIMP
2346
CacheStorageService::IOThreadSuspender::Run()
2347
0
{
2348
0
  MonitorAutoLock mon(mMon);
2349
0
  while (!mSignaled) {
2350
0
    mon.Wait();
2351
0
  }
2352
0
  return NS_OK;
2353
0
}
2354
2355
void
2356
CacheStorageService::IOThreadSuspender::Notify()
2357
0
{
2358
0
  MonitorAutoLock mon(mMon);
2359
0
  mSignaled = true;
2360
0
  mon.Notify();
2361
0
}
2362
2363
NS_IMETHODIMP
2364
CacheStorageService::SuspendCacheIOThread(uint32_t aLevel)
2365
0
{
2366
0
  RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2367
0
  if (!thread) {
2368
0
    return NS_ERROR_NOT_AVAILABLE;
2369
0
  }
2370
0
2371
0
  MOZ_ASSERT(!mActiveIOSuspender);
2372
0
  mActiveIOSuspender = new IOThreadSuspender();
2373
0
  return thread->Dispatch(mActiveIOSuspender, aLevel);
2374
0
}
2375
2376
NS_IMETHODIMP
2377
CacheStorageService::ResumeCacheIOThread()
2378
0
{
2379
0
  MOZ_ASSERT(mActiveIOSuspender);
2380
0
2381
0
  RefPtr<IOThreadSuspender> suspender;
2382
0
  suspender.swap(mActiveIOSuspender);
2383
0
  suspender->Notify();
2384
0
  return NS_OK;
2385
0
}
2386
2387
NS_IMETHODIMP
2388
CacheStorageService::Flush(nsIObserver* aObserver)
2389
0
{
2390
0
  RefPtr<CacheIOThread> thread = CacheFileIOManager::IOThread();
2391
0
  if (!thread) {
2392
0
    return NS_ERROR_NOT_AVAILABLE;
2393
0
  }
2394
0
2395
0
  nsCOMPtr<nsIObserverService> observerService =
2396
0
    mozilla::services::GetObserverService();
2397
0
  if (!observerService) {
2398
0
    return NS_ERROR_NOT_AVAILABLE;
2399
0
  }
2400
0
2401
0
  // Adding as weak, the consumer is responsible to keep the reference
2402
0
  // until notified.
2403
0
  observerService->AddObserver(aObserver, "cacheservice:purge-memory-pools", false);
2404
0
2405
0
  // This runnable will do the purging and when done, notifies the above observer.
2406
0
  // We dispatch it to the CLOSE level, so all data writes scheduled up to this time
2407
0
  // will be done before this purging happens.
2408
0
  RefPtr<CacheStorageService::PurgeFromMemoryRunnable> r =
2409
0
    new CacheStorageService::PurgeFromMemoryRunnable(this, CacheEntry::PURGE_WHOLE);
2410
0
2411
0
  return thread->Dispatch(r, CacheIOThread::WRITE);
2412
0
}
2413
2414
} // namespace net
2415
} // namespace mozilla