Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheIndex.h
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
#ifndef CacheIndex__h__
6
#define CacheIndex__h__
7
8
#include "CacheLog.h"
9
#include "CacheFileIOManager.h"
10
#include "nsIRunnable.h"
11
#include "CacheHashUtils.h"
12
#include "nsICacheStorageService.h"
13
#include "nsICacheEntry.h"
14
#include "nsILoadContextInfo.h"
15
#include "nsTHashtable.h"
16
#include "nsThreadUtils.h"
17
#include "nsWeakReference.h"
18
#include "mozilla/IntegerPrintfMacros.h"
19
#include "mozilla/SHA1.h"
20
#include "mozilla/StaticMutex.h"
21
#include "mozilla/StaticPtr.h"
22
#include "mozilla/EndianUtils.h"
23
#include "mozilla/TimeStamp.h"
24
25
class nsIFile;
26
class nsIDirectoryEnumerator;
27
class nsITimer;
28
29
30
#ifdef DEBUG
31
#define DEBUG_STATS 1
32
#endif
33
34
namespace mozilla {
35
namespace net {
36
37
class CacheFileMetadata;
38
class FileOpenHelper;
39
class CacheIndexIterator;
40
41
const uint16_t kIndexTimeNotAvailable = 0xFFFFU;
42
const uint16_t kIndexTimeOutOfBound = 0xFFFEU;
43
44
typedef struct {
45
  // Version of the index. The index must be ignored and deleted when the file
46
  // on disk was written with a newer version.
47
  uint32_t mVersion;
48
49
  // Timestamp of time when the last successful write of the index started.
50
  // During update process we use this timestamp for a quick validation of entry
51
  // files. If last modified time of the file is lower than this timestamp, we
52
  // skip parsing of such file since the information in index should be up to
53
  // date.
54
  uint32_t mTimeStamp;
55
56
  // We set this flag as soon as possible after parsing index during startup
57
  // and clean it after we write journal to disk during shutdown. We ignore the
58
  // journal and start update process whenever this flag is set during index
59
  // parsing.
60
  uint32_t mIsDirty;
61
} CacheIndexHeader;
62
63
static_assert(
64
  sizeof(CacheIndexHeader::mVersion) + sizeof(CacheIndexHeader::mTimeStamp) +
65
  sizeof(CacheIndexHeader::mIsDirty) == sizeof(CacheIndexHeader),
66
  "Unexpected sizeof(CacheIndexHeader)!");
67
68
#pragma pack(push, 4)
69
struct CacheIndexRecord {
70
  SHA1Sum::Hash   mHash;
71
  uint32_t        mFrecency;
72
  OriginAttrsHash mOriginAttrsHash;
73
  uint32_t        mExpirationTime;
74
  uint16_t        mOnStartTime;
75
  uint16_t        mOnStopTime;
76
77
  /*
78
   *    1000 0000 0000 0000 0000 0000 0000 0000 : initialized
79
   *    0100 0000 0000 0000 0000 0000 0000 0000 : anonymous
80
   *    0010 0000 0000 0000 0000 0000 0000 0000 : removed
81
   *    0001 0000 0000 0000 0000 0000 0000 0000 : dirty
82
   *    0000 1000 0000 0000 0000 0000 0000 0000 : fresh
83
   *    0000 0100 0000 0000 0000 0000 0000 0000 : pinned
84
   *    0000 0010 0000 0000 0000 0000 0000 0000 : has cached alt data
85
   *    0000 0001 0000 0000 0000 0000 0000 0000 : reserved
86
   *    0000 0000 1111 1111 1111 1111 1111 1111 : file size (in kB)
87
   */
88
  uint32_t        mFlags;
89
90
  CacheIndexRecord()
91
    : mFrecency(0)
92
    , mOriginAttrsHash(0)
93
    , mExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
94
    , mOnStartTime(kIndexTimeNotAvailable)
95
    , mOnStopTime(kIndexTimeNotAvailable)
96
    , mFlags(0)
97
0
  {}
98
};
99
#pragma pack(pop)
100
101
static_assert(
102
  sizeof(CacheIndexRecord::mHash) + sizeof(CacheIndexRecord::mFrecency) +
103
  sizeof(CacheIndexRecord::mOriginAttrsHash) + sizeof(CacheIndexRecord::mExpirationTime) +
104
  sizeof(CacheIndexRecord::mOnStartTime) + sizeof(CacheIndexRecord::mOnStopTime) +
105
  sizeof(CacheIndexRecord::mFlags) == sizeof(CacheIndexRecord),
106
  "Unexpected sizeof(CacheIndexRecord)!");
107
108
class CacheIndexEntry : public PLDHashEntryHdr
109
{
110
public:
111
  typedef const SHA1Sum::Hash& KeyType;
112
  typedef const SHA1Sum::Hash* KeyTypePointer;
113
114
  explicit CacheIndexEntry(KeyTypePointer aKey)
115
0
  {
116
0
    MOZ_COUNT_CTOR(CacheIndexEntry);
117
0
    mRec = new CacheIndexRecord();
118
0
    LOG(("CacheIndexEntry::CacheIndexEntry() - Created record [rec=%p]", mRec.get()));
119
0
    memcpy(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash));
120
0
  }
121
  CacheIndexEntry(const CacheIndexEntry& aOther)
122
0
  {
123
0
    MOZ_ASSERT_UNREACHABLE("CacheIndexEntry copy constructor is forbidden!");
124
0
  }
125
  ~CacheIndexEntry()
126
0
  {
127
0
    MOZ_COUNT_DTOR(CacheIndexEntry);
128
0
    LOG(("CacheIndexEntry::~CacheIndexEntry() - Deleting record [rec=%p]",
129
0
         mRec.get()));
130
0
  }
131
132
  // KeyEquals(): does this entry match this key?
133
  bool KeyEquals(KeyTypePointer aKey) const
134
0
  {
135
0
    return memcmp(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash)) == 0;
136
0
  }
137
138
  // KeyToPointer(): Convert KeyType to KeyTypePointer
139
0
  static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
140
141
  // HashKey(): calculate the hash number
142
  static PLDHashNumber HashKey(KeyTypePointer aKey)
143
0
  {
144
0
    return (reinterpret_cast<const uint32_t *>(aKey))[0];
145
0
  }
146
147
  // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
148
  // to use the copy constructor?
149
  enum { ALLOW_MEMMOVE = true };
150
151
  bool operator==(const CacheIndexEntry& aOther) const
152
0
  {
153
0
    return KeyEquals(&aOther.mRec->mHash);
154
0
  }
155
156
  CacheIndexEntry& operator=(const CacheIndexEntry& aOther)
157
0
  {
158
0
    MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
159
0
               sizeof(SHA1Sum::Hash)) == 0);
160
0
    mRec->mFrecency = aOther.mRec->mFrecency;
161
0
    mRec->mExpirationTime = aOther.mRec->mExpirationTime;
162
0
    mRec->mOriginAttrsHash = aOther.mRec->mOriginAttrsHash;
163
0
    mRec->mOnStartTime = aOther.mRec->mOnStartTime;
164
0
    mRec->mOnStopTime = aOther.mRec->mOnStopTime;
165
0
    mRec->mFlags = aOther.mRec->mFlags;
166
0
    return *this;
167
0
  }
168
169
  void InitNew()
170
0
  {
171
0
    mRec->mFrecency = 0;
172
0
    mRec->mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
173
0
    mRec->mOriginAttrsHash = 0;
174
0
    mRec->mOnStartTime = kIndexTimeNotAvailable;
175
0
    mRec->mOnStopTime = kIndexTimeNotAvailable;
176
0
    mRec->mFlags = 0;
177
0
  }
178
179
  void Init(OriginAttrsHash aOriginAttrsHash, bool aAnonymous, bool aPinned)
180
0
  {
181
0
    MOZ_ASSERT(mRec->mFrecency == 0);
182
0
    MOZ_ASSERT(mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME);
183
0
    MOZ_ASSERT(mRec->mOriginAttrsHash == 0);
184
0
    MOZ_ASSERT(mRec->mOnStartTime == kIndexTimeNotAvailable);
185
0
    MOZ_ASSERT(mRec->mOnStopTime == kIndexTimeNotAvailable);
186
0
    // When we init the entry it must be fresh and may be dirty
187
0
    MOZ_ASSERT((mRec->mFlags & ~kDirtyMask) == kFreshMask);
188
0
189
0
    mRec->mOriginAttrsHash = aOriginAttrsHash;
190
0
    mRec->mFlags |= kInitializedMask;
191
0
    if (aAnonymous) {
192
0
      mRec->mFlags |= kAnonymousMask;
193
0
    }
194
0
    if (aPinned) {
195
0
      mRec->mFlags |= kPinnedMask;
196
0
    }
197
0
  }
198
199
0
  const SHA1Sum::Hash * Hash() const { return &mRec->mHash; }
200
201
0
  bool IsInitialized() const { return !!(mRec->mFlags & kInitializedMask); }
202
203
0
  mozilla::net::OriginAttrsHash OriginAttrsHash() const { return mRec->mOriginAttrsHash; }
204
205
0
  bool Anonymous() const { return !!(mRec->mFlags & kAnonymousMask); }
206
207
0
  bool IsRemoved() const { return !!(mRec->mFlags & kRemovedMask); }
208
0
  void MarkRemoved() { mRec->mFlags |= kRemovedMask; }
209
210
0
  bool IsDirty() const { return !!(mRec->mFlags & kDirtyMask); }
211
0
  void MarkDirty() { mRec->mFlags |= kDirtyMask; }
212
0
  void ClearDirty() { mRec->mFlags &= ~kDirtyMask; }
213
214
0
  bool IsFresh() const { return !!(mRec->mFlags & kFreshMask); }
215
0
  void MarkFresh() { mRec->mFlags |= kFreshMask; }
216
217
0
  bool IsPinned() const { return !!(mRec->mFlags & kPinnedMask); }
218
219
0
  void     SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
220
0
  uint32_t GetFrecency() const { return mRec->mFrecency; }
221
222
  void     SetExpirationTime(uint32_t aExpirationTime)
223
0
  {
224
0
    mRec->mExpirationTime = aExpirationTime;
225
0
  }
226
0
  uint32_t GetExpirationTime() const { return mRec->mExpirationTime; }
227
228
  void     SetHasAltData(bool aHasAltData)
229
0
  {
230
0
    aHasAltData ? mRec->mFlags |= kHasAltDataMask
231
0
                : mRec->mFlags &= ~kHasAltDataMask;
232
0
  }
233
0
  bool     GetHasAltData() const { return !!(mRec->mFlags & kHasAltDataMask); }
234
235
  void     SetOnStartTime(uint16_t aTime)
236
0
  {
237
0
    mRec->mOnStartTime = aTime;
238
0
  }
239
0
  uint16_t  GetOnStartTime() const { return mRec->mOnStartTime; }
240
241
  void     SetOnStopTime(uint16_t aTime)
242
0
  {
243
0
    mRec->mOnStopTime = aTime;
244
0
  }
245
0
  uint16_t  GetOnStopTime() const { return mRec->mOnStopTime; }
246
247
  // Sets filesize in kilobytes.
248
  void     SetFileSize(uint32_t aFileSize)
249
0
  {
250
0
    if (aFileSize > kFileSizeMask) {
251
0
      LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
252
0
           "truncating to %u", kFileSizeMask));
253
0
      aFileSize = kFileSizeMask;
254
0
    }
255
0
    mRec->mFlags &= ~kFileSizeMask;
256
0
    mRec->mFlags |= aFileSize;
257
0
  }
258
  // Returns filesize in kilobytes.
259
0
  uint32_t GetFileSize() const { return GetFileSize(mRec); }
260
  static uint32_t GetFileSize(CacheIndexRecord *aRec)
261
0
  {
262
0
    return aRec->mFlags & kFileSizeMask;
263
0
  }
264
  static uint32_t IsPinned(CacheIndexRecord *aRec)
265
0
  {
266
0
    return aRec->mFlags & kPinnedMask;
267
0
  }
268
0
  bool     IsFileEmpty() const { return GetFileSize() == 0; }
269
270
  void WriteToBuf(void *aBuf)
271
0
  {
272
0
    uint8_t* ptr = static_cast<uint8_t*>(aBuf);
273
0
    memcpy(ptr, mRec->mHash, sizeof(SHA1Sum::Hash)); ptr += sizeof(SHA1Sum::Hash);
274
0
    NetworkEndian::writeUint32(ptr, mRec->mFrecency); ptr += sizeof(uint32_t);
275
0
    NetworkEndian::writeUint64(ptr, mRec->mOriginAttrsHash); ptr += sizeof(uint64_t);
276
0
    NetworkEndian::writeUint32(ptr, mRec->mExpirationTime); ptr += sizeof(uint32_t);
277
0
    NetworkEndian::writeUint16(ptr, mRec->mOnStartTime); ptr += sizeof(uint16_t);
278
0
    NetworkEndian::writeUint16(ptr, mRec->mOnStopTime); ptr += sizeof(uint16_t);
279
0
    // Dirty and fresh flags should never go to disk, since they make sense only
280
0
    // during current session.
281
0
    NetworkEndian::writeUint32(ptr, mRec->mFlags & ~(kDirtyMask | kFreshMask));
282
0
  }
283
284
  void ReadFromBuf(void *aBuf)
285
0
  {
286
0
    const uint8_t* ptr = static_cast<const uint8_t*>(aBuf);
287
0
    MOZ_ASSERT(memcmp(&mRec->mHash, ptr, sizeof(SHA1Sum::Hash)) == 0); ptr += sizeof(SHA1Sum::Hash);
288
0
    mRec->mFrecency = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
289
0
    mRec->mOriginAttrsHash = NetworkEndian::readUint64(ptr); ptr += sizeof(uint64_t);
290
0
    mRec->mExpirationTime = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
291
0
    mRec->mOnStartTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
292
0
    mRec->mOnStopTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
293
0
    mRec->mFlags = NetworkEndian::readUint32(ptr);
294
0
  }
295
296
0
  void Log() const {
297
0
    LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
298
0
         " initialized=%u, removed=%u, dirty=%u, anonymous=%u, "
299
0
         "originAttrsHash=%" PRIx64 ", frecency=%u, expirationTime=%u, "
300
0
         "hasAltData=%u, onStartTime=%u, onStopTime=%u, size=%u]",
301
0
         this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
302
0
         IsDirty(), Anonymous(), OriginAttrsHash(), GetFrecency(),
303
0
         GetExpirationTime(), GetHasAltData(), GetOnStartTime(),
304
0
         GetOnStopTime(), GetFileSize()));
305
0
  }
306
307
  static bool RecordMatchesLoadContextInfo(CacheIndexRecord *aRec,
308
                                           nsILoadContextInfo *aInfo)
309
0
  {
310
0
    MOZ_ASSERT(aInfo);
311
0
312
0
    if (!aInfo->IsPrivate() &&
313
0
        GetOriginAttrsHash(*aInfo->OriginAttributesPtr()) == aRec->mOriginAttrsHash &&
314
0
        aInfo->IsAnonymous() == !!(aRec->mFlags & kAnonymousMask)) {
315
0
      return true;
316
0
    }
317
0
318
0
    return false;
319
0
  }
320
321
  // Memory reporting
322
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
323
0
  {
324
0
    return mallocSizeOf(mRec.get());
325
0
  }
326
327
  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
328
0
  {
329
0
    return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
330
0
  }
331
332
private:
333
  friend class CacheIndexEntryUpdate;
334
  friend class CacheIndex;
335
  friend class CacheIndexEntryAutoManage;
336
337
  static const uint32_t kInitializedMask = 0x80000000;
338
  static const uint32_t kAnonymousMask   = 0x40000000;
339
340
  // This flag is set when the entry was removed. We need to keep this
341
  // information in memory until we write the index file.
342
  static const uint32_t kRemovedMask     = 0x20000000;
343
344
  // This flag is set when the information in memory is not in sync with the
345
  // information in index file on disk.
346
  static const uint32_t kDirtyMask       = 0x10000000;
347
348
  // This flag is set when the information about the entry is fresh, i.e.
349
  // we've created or opened this entry during this session, or we've seen
350
  // this entry during update or build process.
351
  static const uint32_t kFreshMask       = 0x08000000;
352
353
  // Indicates a pinned entry.
354
  static const uint32_t kPinnedMask      = 0x04000000;
355
356
  // Indicates there is cached alternative data in the entry.
357
  static const uint32_t kHasAltDataMask = 0x02000000;
358
  static const uint32_t kReservedMask    = 0x01000000;
359
360
  // FileSize in kilobytes
361
  static const uint32_t kFileSizeMask    = 0x00FFFFFF;
362
363
  nsAutoPtr<CacheIndexRecord> mRec;
364
};
365
366
class CacheIndexEntryUpdate : public CacheIndexEntry
367
{
368
public:
369
  explicit CacheIndexEntryUpdate(CacheIndexEntry::KeyTypePointer aKey)
370
    : CacheIndexEntry(aKey)
371
    , mUpdateFlags(0)
372
0
  {
373
0
    MOZ_COUNT_CTOR(CacheIndexEntryUpdate);
374
0
    LOG(("CacheIndexEntryUpdate::CacheIndexEntryUpdate()"));
375
0
  }
376
  ~CacheIndexEntryUpdate()
377
0
  {
378
0
    MOZ_COUNT_DTOR(CacheIndexEntryUpdate);
379
0
    LOG(("CacheIndexEntryUpdate::~CacheIndexEntryUpdate()"));
380
0
  }
381
382
  CacheIndexEntryUpdate& operator=(const CacheIndexEntry& aOther)
383
0
  {
384
0
    MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
385
0
               sizeof(SHA1Sum::Hash)) == 0);
386
0
    mUpdateFlags = 0;
387
0
    *(static_cast<CacheIndexEntry *>(this)) = aOther;
388
0
    return *this;
389
0
  }
390
391
  void InitNew()
392
0
  {
393
0
    mUpdateFlags = kFrecencyUpdatedMask | kExpirationUpdatedMask |
394
0
                   kHasAltDataUpdatedMask | kOnStartTimeUpdatedMask |
395
0
                   kOnStopTimeUpdatedMask | kFileSizeUpdatedMask;
396
0
    CacheIndexEntry::InitNew();
397
0
  }
398
399
  void SetFrecency(uint32_t aFrecency)
400
0
  {
401
0
    mUpdateFlags |= kFrecencyUpdatedMask;
402
0
    CacheIndexEntry::SetFrecency(aFrecency);
403
0
  }
404
405
  void SetExpirationTime(uint32_t aExpirationTime)
406
0
  {
407
0
    mUpdateFlags |= kExpirationUpdatedMask;
408
0
    CacheIndexEntry::SetExpirationTime(aExpirationTime);
409
0
  }
410
411
  void SetHasAltData(bool aHasAltData)
412
0
  {
413
0
    mUpdateFlags |= kHasAltDataUpdatedMask;
414
0
    CacheIndexEntry::SetHasAltData(aHasAltData);
415
0
  }
416
417
  void SetOnStartTime(uint16_t aTime)
418
0
  {
419
0
    mUpdateFlags |= kOnStartTimeUpdatedMask;
420
0
    CacheIndexEntry::SetOnStartTime(aTime);
421
0
  }
422
423
  void SetOnStopTime(uint16_t aTime)
424
0
  {
425
0
    mUpdateFlags |= kOnStopTimeUpdatedMask;
426
0
    CacheIndexEntry::SetOnStopTime(aTime);
427
0
  }
428
429
  void SetFileSize(uint32_t aFileSize)
430
0
  {
431
0
    mUpdateFlags |= kFileSizeUpdatedMask;
432
0
    CacheIndexEntry::SetFileSize(aFileSize);
433
0
  }
434
435
0
  void ApplyUpdate(CacheIndexEntry *aDst) {
436
0
    MOZ_ASSERT(memcmp(&mRec->mHash, &aDst->mRec->mHash,
437
0
               sizeof(SHA1Sum::Hash)) == 0);
438
0
    if (mUpdateFlags & kFrecencyUpdatedMask) {
439
0
      aDst->mRec->mFrecency = mRec->mFrecency;
440
0
    }
441
0
    if (mUpdateFlags & kExpirationUpdatedMask) {
442
0
      aDst->mRec->mExpirationTime = mRec->mExpirationTime;
443
0
    }
444
0
    aDst->mRec->mOriginAttrsHash = mRec->mOriginAttrsHash;
445
0
    if (mUpdateFlags & kOnStartTimeUpdatedMask) {
446
0
      aDst->mRec->mOnStartTime = mRec->mOnStartTime;
447
0
    }
448
0
    if (mUpdateFlags & kOnStopTimeUpdatedMask) {
449
0
      aDst->mRec->mOnStopTime = mRec->mOnStopTime;
450
0
    }
451
0
    if (mUpdateFlags & kHasAltDataUpdatedMask &&
452
0
        ((aDst->mRec->mFlags ^ mRec->mFlags) & kHasAltDataMask)) {
453
0
      // Toggle the bit if we need to.
454
0
      aDst->mRec->mFlags ^= kHasAltDataMask;
455
0
    }
456
0
457
0
    if (mUpdateFlags & kFileSizeUpdatedMask) {
458
0
      // Copy all flags except |HasAltData|.
459
0
      aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask);
460
0
    } else {
461
0
      // Copy all flags except |HasAltData| and file size.
462
0
      aDst->mRec->mFlags &= kFileSizeMask;
463
0
      aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask & ~kFileSizeMask);
464
0
    }
465
0
  }
466
467
private:
468
  static const uint32_t kFrecencyUpdatedMask = 0x00000001;
469
  static const uint32_t kExpirationUpdatedMask = 0x00000002;
470
  static const uint32_t kFileSizeUpdatedMask = 0x00000004;
471
  static const uint32_t kHasAltDataUpdatedMask = 0x00000008;
472
  static const uint32_t kOnStartTimeUpdatedMask = 0x00000010;
473
  static const uint32_t kOnStopTimeUpdatedMask = 0x00000020;
474
475
  uint32_t mUpdateFlags;
476
};
477
478
class CacheIndexStats
479
{
480
public:
481
  CacheIndexStats()
482
    : mCount(0)
483
    , mNotInitialized(0)
484
    , mRemoved(0)
485
    , mDirty(0)
486
    , mFresh(0)
487
    , mEmpty(0)
488
    , mSize(0)
489
#ifdef DEBUG
490
    , mStateLogged(false)
491
    , mDisableLogging(false)
492
#endif
493
0
  {
494
0
  }
495
496
  bool operator==(const CacheIndexStats& aOther) const
497
0
  {
498
0
    return
499
0
#ifdef DEBUG
500
0
           aOther.mStateLogged == mStateLogged &&
501
0
#endif
502
0
           aOther.mCount == mCount &&
503
0
           aOther.mNotInitialized == mNotInitialized &&
504
0
           aOther.mRemoved == mRemoved &&
505
0
           aOther.mDirty == mDirty &&
506
0
           aOther.mFresh == mFresh &&
507
0
           aOther.mEmpty == mEmpty &&
508
0
           aOther.mSize == mSize;
509
0
  }
510
511
#ifdef DEBUG
512
  void DisableLogging() {
513
    mDisableLogging = true;
514
  }
515
#endif
516
517
0
  void Log() {
518
0
    LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
519
0
         "dirty=%u, fresh=%u, empty=%u, size=%u]", mCount, mNotInitialized,
520
0
         mRemoved, mDirty, mFresh, mEmpty, mSize));
521
0
  }
522
523
0
  void Clear() {
524
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Clear() - state logged!");
525
0
526
0
    mCount = 0;
527
0
    mNotInitialized = 0;
528
0
    mRemoved = 0;
529
0
    mDirty = 0;
530
0
    mFresh = 0;
531
0
    mEmpty = 0;
532
0
    mSize = 0;
533
0
  }
534
535
#ifdef DEBUG
536
  bool StateLogged() {
537
    return mStateLogged;
538
  }
539
#endif
540
541
0
  uint32_t Count() {
542
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Count() - state logged!");
543
0
    return mCount;
544
0
  }
545
546
0
  uint32_t Dirty() {
547
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Dirty() - state logged!");
548
0
    return mDirty;
549
0
  }
550
551
0
  uint32_t Fresh() {
552
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Fresh() - state logged!");
553
0
    return mFresh;
554
0
  }
555
556
0
  uint32_t ActiveEntriesCount() {
557
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::ActiveEntriesCount() - state "
558
0
               "logged!");
559
0
    return mCount - mRemoved - mNotInitialized - mEmpty;
560
0
  }
561
562
0
  uint32_t Size() {
563
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Size() - state logged!");
564
0
    return mSize;
565
0
  }
566
567
0
  void BeforeChange(const CacheIndexEntry *aEntry) {
568
#ifdef DEBUG_STATS
569
    if (!mDisableLogging) {
570
      LOG(("CacheIndexStats::BeforeChange()"));
571
      Log();
572
    }
573
#endif
574
575
0
    MOZ_ASSERT(!mStateLogged, "CacheIndexStats::BeforeChange() - state "
576
0
               "logged!");
577
#ifdef DEBUG
578
    mStateLogged = true;
579
#endif
580
0
    if (aEntry) {
581
0
      MOZ_ASSERT(mCount);
582
0
      mCount--;
583
0
      if (aEntry->IsDirty()) {
584
0
        MOZ_ASSERT(mDirty);
585
0
        mDirty--;
586
0
      }
587
0
      if (aEntry->IsFresh()) {
588
0
        MOZ_ASSERT(mFresh);
589
0
        mFresh--;
590
0
      }
591
0
      if (aEntry->IsRemoved()) {
592
0
        MOZ_ASSERT(mRemoved);
593
0
        mRemoved--;
594
0
      } else {
595
0
        if (!aEntry->IsInitialized()) {
596
0
          MOZ_ASSERT(mNotInitialized);
597
0
          mNotInitialized--;
598
0
        } else {
599
0
          if (aEntry->IsFileEmpty()) {
600
0
            MOZ_ASSERT(mEmpty);
601
0
            mEmpty--;
602
0
          } else {
603
0
            MOZ_ASSERT(mSize >= aEntry->GetFileSize());
604
0
            mSize -= aEntry->GetFileSize();
605
0
          }
606
0
        }
607
0
      }
608
0
    }
609
0
  }
610
611
0
  void AfterChange(const CacheIndexEntry *aEntry) {
612
0
    MOZ_ASSERT(mStateLogged, "CacheIndexStats::AfterChange() - state not "
613
0
               "logged!");
614
#ifdef DEBUG
615
    mStateLogged = false;
616
#endif
617
0
    if (aEntry) {
618
0
      ++mCount;
619
0
      if (aEntry->IsDirty()) {
620
0
        mDirty++;
621
0
      }
622
0
      if (aEntry->IsFresh()) {
623
0
        mFresh++;
624
0
      }
625
0
      if (aEntry->IsRemoved()) {
626
0
        mRemoved++;
627
0
      } else {
628
0
        if (!aEntry->IsInitialized()) {
629
0
          mNotInitialized++;
630
0
        } else {
631
0
          if (aEntry->IsFileEmpty()) {
632
0
            mEmpty++;
633
0
          } else {
634
0
            mSize += aEntry->GetFileSize();
635
0
          }
636
0
        }
637
0
      }
638
0
    }
639
0
640
#ifdef DEBUG_STATS
641
    if (!mDisableLogging) {
642
      LOG(("CacheIndexStats::AfterChange()"));
643
      Log();
644
    }
645
#endif
646
  }
647
648
private:
649
  uint32_t mCount;
650
  uint32_t mNotInitialized;
651
  uint32_t mRemoved;
652
  uint32_t mDirty;
653
  uint32_t mFresh;
654
  uint32_t mEmpty;
655
  uint32_t mSize;
656
#ifdef DEBUG
657
  // We completely remove the data about an entry from the stats in
658
  // BeforeChange() and set this flag to true. The entry is then modified,
659
  // deleted or created and the data is again put into the stats and this flag
660
  // set to false. Statistics must not be read during this time since the
661
  // information is not correct.
662
  bool     mStateLogged;
663
664
  // Disables logging in this instance of CacheIndexStats
665
  bool     mDisableLogging;
666
#endif
667
};
668
669
class CacheIndex final
670
  : public CacheFileIOListener
671
  , public nsIRunnable
672
{
673
public:
674
  NS_DECL_THREADSAFE_ISUPPORTS
675
  NS_DECL_NSIRUNNABLE
676
677
  CacheIndex();
678
679
  static nsresult Init(nsIFile *aCacheDirectory);
680
  static nsresult PreShutdown();
681
  static nsresult Shutdown();
682
683
  // Following methods can be called only on IO thread.
684
685
  // Add entry to the index. The entry shouldn't be present in index. This
686
  // method is called whenever a new handle for a new entry file is created. The
687
  // newly created entry is not initialized and it must be either initialized
688
  // with InitEntry() or removed with RemoveEntry().
689
  static nsresult AddEntry(const SHA1Sum::Hash *aHash);
690
691
  // Inform index about an existing entry that should be present in index. This
692
  // method is called whenever a new handle for an existing entry file is
693
  // created. Like in case of AddEntry(), either InitEntry() or RemoveEntry()
694
  // must be called on the entry, since the entry is not initizlized if the
695
  // index is outdated.
696
  static nsresult EnsureEntryExists(const SHA1Sum::Hash *aHash);
697
698
  // Initialize the entry. It MUST be present in index. Call to AddEntry() or
699
  // EnsureEntryExists() must precede the call to this method.
700
  static nsresult InitEntry(const SHA1Sum::Hash *aHash,
701
                            OriginAttrsHash      aOriginAttrsHash,
702
                            bool                 aAnonymous,
703
                            bool                 aPinned);
704
705
  // Remove entry from index. The entry should be present in index.
706
  static nsresult RemoveEntry(const SHA1Sum::Hash *aHash);
707
708
  // Update some information in entry. The entry MUST be present in index and
709
  // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
710
  // InitEntry() must precede the call to this method.
711
  // Pass nullptr if the value didn't change.
712
  static nsresult UpdateEntry(const SHA1Sum::Hash *aHash,
713
                              const uint32_t      *aFrecency,
714
                              const uint32_t      *aExpirationTime,
715
                              const bool          *aHasAltData,
716
                              const uint16_t      *aOnStartTime,
717
                              const uint16_t      *aOnStopTime,
718
                              const uint32_t      *aSize);
719
720
  // Remove all entries from the index. Called when clearing the whole cache.
721
  static nsresult RemoveAll();
722
723
  enum EntryStatus {
724
    EXISTS         = 0,
725
    DOES_NOT_EXIST = 1,
726
    DO_NOT_KNOW    = 2
727
  };
728
729
  // Returns status of the entry in index for the given key. It can be called
730
  // on any thread.
731
  // If the optional aCB callback is given, the it will be called with a
732
  // CacheIndexEntry only if _retval is EXISTS when the method returns.
733
  static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval,
734
                           const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
735
  static nsresult HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval,
736
                           const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
737
738
  // Returns a hash of the least important entry that should be evicted if the
739
  // cache size is over limit and also returns a total number of all entries in
740
  // the index minus the number of forced valid entries and unpinned entries
741
  // that we encounter when searching (see below)
742
  static nsresult GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt);
743
744
  // Checks if a cache entry is currently forced valid. Used to prevent an entry
745
  // (that has been forced valid) from being evicted when the cache size reaches
746
  // its limit.
747
  static bool IsForcedValidEntry(const SHA1Sum::Hash *aHash);
748
749
  // Returns cache size in kB.
750
  static nsresult GetCacheSize(uint32_t *_retval);
751
752
  // Returns number of entry files in the cache
753
  static nsresult GetEntryFileCount(uint32_t *_retval);
754
755
  // Synchronously returns the disk occupation and number of entries per-context.
756
  // Callable on any thread. It will ignore loadContextInfo and get stats for
757
  // all entries if the aInfo is a nullptr.
758
  static nsresult GetCacheStats(nsILoadContextInfo *aInfo, uint32_t *aSize, uint32_t *aCount);
759
760
  // Asynchronously gets the disk cache size, used for display in the UI.
761
  static nsresult AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver);
762
763
  // Returns an iterator that returns entries matching a given context that were
764
  // present in the index at the time this method was called. If aAddNew is true
765
  // then the iterator will also return entries created after this call.
766
  // NOTE: When some entry is removed from index it is removed also from the
767
  // iterator regardless what aAddNew was passed.
768
  static nsresult GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
769
                              CacheIndexIterator **_retval);
770
771
  // Returns true if we _think_ that the index is up to date. I.e. the state is
772
  // READY or WRITING and mIndexNeedsUpdate as well as mShuttingDown is false.
773
  static nsresult IsUpToDate(bool *_retval);
774
775
  // Called from CacheStorageService::Clear() and CacheFileContextEvictor::EvictEntries(),
776
  // sets a flag that blocks notification to AsyncGetDiskConsumption.
777
  static void OnAsyncEviction(bool aEvicting);
778
779
  // Memory reporting
780
  static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
781
  static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
782
783
private:
784
  friend class CacheIndexEntryAutoManage;
785
  friend class FileOpenHelper;
786
  friend class CacheIndexIterator;
787
788
  virtual ~CacheIndex();
789
790
  NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
791
  nsresult   OnFileOpenedInternal(FileOpenHelper *aOpener,
792
                                  CacheFileHandle *aHandle, nsresult aResult);
793
  NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
794
                           nsresult aResult) override;
795
  NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override;
796
  NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
797
  NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override;
798
  NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override;
799
800
  nsresult InitInternal(nsIFile *aCacheDirectory);
801
  void     PreShutdownInternal();
802
803
  // This method returns false when index is not initialized or is shut down.
804
  bool IsIndexUsable();
805
806
  // This method checks whether the entry has the same values of
807
  // originAttributes and isAnonymous. We don't expect to find a collision
808
  // since these values are part of the key that we hash and we use a strong
809
  // hash function.
810
  static bool IsCollision(CacheIndexEntry *aEntry,
811
                          OriginAttrsHash  aOriginAttrsHash,
812
                          bool             aAnonymous);
813
814
  // Checks whether any of the information about the entry has changed.
815
  static bool HasEntryChanged(CacheIndexEntry *aEntry,
816
                              const uint32_t  *aFrecency,
817
                              const uint32_t  *aExpirationTime,
818
                              const bool      *aHasAltData,
819
                              const uint16_t  *aOnStartTime,
820
                              const uint16_t  *aOnStopTime,
821
                              const uint32_t  *aSize);
822
823
  // Merge all pending operations from mPendingUpdates into mIndex.
824
  void ProcessPendingOperations();
825
826
  // Following methods perform writing of the index file.
827
  //
828
  // The index is written periodically, but not earlier than once in
829
  // kMinDumpInterval and there must be at least kMinUnwrittenChanges
830
  // differences between index on disk and in memory. Index is always first
831
  // written to a temporary file and the old index file is replaced when the
832
  // writing process succeeds.
833
  //
834
  // Starts writing of index when both limits (minimal delay between writes and
835
  // minimum number of changes in index) were exceeded.
836
  bool WriteIndexToDiskIfNeeded();
837
  // Starts writing of index file.
838
  void WriteIndexToDisk();
839
  // Serializes part of mIndex hashtable to the write buffer a writes the buffer
840
  // to the file.
841
  void WriteRecords();
842
  // Finalizes writing process.
843
  void FinishWrite(bool aSucceeded);
844
845
  // Following methods perform writing of the journal during shutdown. All these
846
  // methods must be called only during shutdown since they write/delete files
847
  // directly on the main thread instead of using CacheFileIOManager that does
848
  // it asynchronously on IO thread. Journal contains only entries that are
849
  // dirty, i.e. changes that are not present in the index file on the disk.
850
  // When the log is written successfully, the dirty flag in index file is
851
  // cleared.
852
  nsresult GetFile(const nsACString &aName, nsIFile **_retval);
853
  nsresult RemoveFile(const nsACString &aName);
854
  void     RemoveAllIndexFiles();
855
  void     RemoveJournalAndTempFile();
856
  // Writes journal to the disk and clears dirty flag in index header.
857
  nsresult WriteLogToDisk();
858
859
  // Following methods perform reading of the index from the disk.
860
  //
861
  // Index is read at startup just after initializing the CacheIndex. There are
862
  // 3 files used when manipulating with index: index file, journal file and
863
  // a temporary file. All files contain the hash of the data, so we can check
864
  // whether the content is valid and complete. Index file contains also a dirty
865
  // flag in the index header which is unset on a clean shutdown. During opening
866
  // and reading of the files we determine the status of the whole index from
867
  // the states of the separate files. Following table shows all possible
868
  // combinations:
869
  //
870
  // index, journal, tmpfile
871
  // M      *        *       - index is missing    -> BUILD
872
  // I      *        *       - index is invalid    -> BUILD
873
  // D      *        *       - index is dirty      -> UPDATE
874
  // C      M        *       - index is dirty      -> UPDATE
875
  // C      I        *       - unexpected state    -> UPDATE
876
  // C      V        E       - unexpected state    -> UPDATE
877
  // C      V        M       - index is up to date -> READY
878
  //
879
  // where the letters mean:
880
  //   * - any state
881
  //   E - file exists
882
  //   M - file is missing
883
  //   I - data is invalid (parsing failed or hash didn't match)
884
  //   D - dirty (data in index file is correct, but dirty flag is set)
885
  //   C - clean (index file is clean)
886
  //   V - valid (data in journal file is correct)
887
  //
888
  // Note: We accept the data from journal only when the index is up to date as
889
  // a whole (i.e. C,V,M state).
890
  //
891
  // We rename the journal file to the temporary file as soon as possible after
892
  // initial test to ensure that we start update process on the next startup if
893
  // FF crashes during parsing of the index.
894
  //
895
  // Initiates reading index from disk.
896
  void ReadIndexFromDisk();
897
  // Starts reading data from index file.
898
  void StartReadingIndex();
899
  // Parses data read from index file.
900
  void ParseRecords();
901
  // Starts reading data from journal file.
902
  void StartReadingJournal();
903
  // Parses data read from journal file.
904
  void ParseJournal();
905
  // Merges entries from journal into mIndex.
906
  void MergeJournal();
907
  // In debug build this method checks that we have no fresh entry in mIndex
908
  // after we finish reading index and before we process pending operations.
909
  void EnsureNoFreshEntry();
910
  // In debug build this method is called after processing pending operations
911
  // to make sure mIndexStats contains correct information.
912
  void EnsureCorrectStats();
913
  // Finalizes reading process.
914
  void FinishRead(bool aSucceeded);
915
916
  // Following methods perform updating and building of the index.
917
  // Timer callback that starts update or build process.
918
  static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
919
  void DelayedUpdateLocked();
920
  // Posts timer event that start update or build process.
921
  nsresult ScheduleUpdateTimer(uint32_t aDelay);
922
  nsresult SetupDirectoryEnumerator();
923
  nsresult InitEntryFromDiskData(CacheIndexEntry *aEntry,
924
                                 CacheFileMetadata *aMetaData,
925
                                 int64_t aFileSize);
926
  // Returns true when either a timer is scheduled or event is posted.
927
  bool IsUpdatePending();
928
  // Iterates through all files in entries directory that we didn't create/open
929
  // during this session, parses them and adds the entries to the index.
930
  void BuildIndex();
931
932
  bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
933
  // Starts update or build process or fires a timer when it is too early after
934
  // startup.
935
  void StartUpdatingIndex(bool aRebuild);
936
  // Iterates through all files in entries directory that we didn't create/open
937
  // during this session and theirs last modified time is newer than timestamp
938
  // in the index header. Parses the files and adds the entries to the index.
939
  void UpdateIndex();
940
  // Finalizes update or build process.
941
  void FinishUpdate(bool aSucceeded);
942
943
  void RemoveNonFreshEntries();
944
945
  enum EState {
946
    // Initial state in which the index is not usable
947
    // Possible transitions:
948
    //  -> READING
949
    INITIAL  = 0,
950
951
    // Index is being read from the disk.
952
    // Possible transitions:
953
    //  -> INITIAL  - We failed to dispatch a read event.
954
    //  -> BUILDING - No or corrupted index file was found.
955
    //  -> UPDATING - No or corrupted journal file was found.
956
    //              - Dirty flag was set in index header.
957
    //  -> READY    - Index was read successfully or was interrupted by
958
    //                pre-shutdown.
959
    //  -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
960
    READING  = 1,
961
962
    // Index is being written to the disk.
963
    // Possible transitions:
964
    //  -> READY    - Writing of index finished or was interrupted by
965
    //                pre-shutdown..
966
    //  -> UPDATING - Writing of index finished, but index was found outdated
967
    //                during writing.
968
    //  -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
969
    WRITING  = 2,
970
971
    // Index is being build.
972
    // Possible transitions:
973
    //  -> READY    - Building of index finished or was interrupted by
974
    //                pre-shutdown.
975
    //  -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
976
    BUILDING = 3,
977
978
    // Index is being updated.
979
    // Possible transitions:
980
    //  -> READY    - Updating of index finished or was interrupted by
981
    //                pre-shutdown.
982
    //  -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
983
    UPDATING = 4,
984
985
    // Index is ready.
986
    // Possible transitions:
987
    //  -> UPDATING - Index was found outdated.
988
    //  -> SHUTDOWN - Index is shutting down.
989
    READY    = 5,
990
991
    // Index is shutting down.
992
    SHUTDOWN = 6
993
  };
994
995
  static char const * StateString(EState aState);
996
  void ChangeState(EState aNewState);
997
  void NotifyAsyncGetDiskConsumptionCallbacks();
998
999
  // Allocates and releases buffer used for reading and writing index.
1000
  void AllocBuffer();
1001
  void ReleaseBuffer();
1002
1003
  // Methods used by CacheIndexEntryAutoManage to keep the iterators up to date.
1004
  void AddRecordToIterators(CacheIndexRecord *aRecord);
1005
  void RemoveRecordFromIterators(CacheIndexRecord *aRecord);
1006
  void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
1007
                                CacheIndexRecord *aNewRecord);
1008
1009
  // Memory reporting (private part)
1010
  size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
1011
1012
  void ReportHashStats();
1013
1014
  static mozilla::StaticRefPtr<CacheIndex> gInstance;
1015
  static StaticMutex sLock;
1016
1017
  nsCOMPtr<nsIFile> mCacheDirectory;
1018
1019
  EState         mState;
1020
  // Timestamp of time when the index was initialized. We use it to delay
1021
  // initial update or build of index.
1022
  TimeStamp      mStartTime;
1023
  // Set to true in PreShutdown(), it is checked on variaous places to prevent
1024
  // starting any process (write, update, etc.) during shutdown.
1025
  bool           mShuttingDown;
1026
  // When set to true, update process should start as soon as possible. This
1027
  // flag is set whenever we find some inconsistency which would be fixed by
1028
  // update process. The flag is checked always when switching to READY state.
1029
  // To make sure we start the update process as soon as possible, methods that
1030
  // set this flag should also call StartUpdatingIndexIfNeeded() to cover the
1031
  // case when we are currently in READY state.
1032
  bool           mIndexNeedsUpdate;
1033
  // Set at the beginning of RemoveAll() which clears the whole index. When
1034
  // removing all entries we must stop any pending reading, writing, updating or
1035
  // building operation. This flag is checked at various places and it prevents
1036
  // we won't start another operation (e.g. canceling reading of the index would
1037
  // normally start update or build process)
1038
  bool           mRemovingAll;
1039
  // Whether the index file on disk exists and is valid.
1040
  bool           mIndexOnDiskIsValid;
1041
  // When something goes wrong during updating or building process, we don't
1042
  // mark index clean (and also don't write journal) to ensure that update or
1043
  // build will be initiated on the next start.
1044
  bool           mDontMarkIndexClean;
1045
  // Timestamp value from index file. It is used during update process to skip
1046
  // entries that were last modified before this timestamp.
1047
  uint32_t       mIndexTimeStamp;
1048
  // Timestamp of last time the index was dumped to disk.
1049
  // NOTE: The index might not be necessarily dumped at this time. The value
1050
  // is used to schedule next dump of the index.
1051
  TimeStamp      mLastDumpTime;
1052
1053
  // Timer of delayed update/build.
1054
  nsCOMPtr<nsITimer> mUpdateTimer;
1055
  // True when build or update event is posted
1056
  bool               mUpdateEventPending;
1057
1058
  // Helper members used when reading/writing index from/to disk.
1059
  // Contains number of entries that should be skipped:
1060
  //  - in hashtable when writing index because they were already written
1061
  //  - in index file when reading index because they were already read
1062
  uint32_t                  mSkipEntries;
1063
  // Number of entries that should be written to disk. This is number of entries
1064
  // in hashtable that are initialized and are not marked as removed when writing
1065
  // begins.
1066
  uint32_t                  mProcessEntries;
1067
  char                     *mRWBuf;
1068
  uint32_t                  mRWBufSize;
1069
  uint32_t                  mRWBufPos;
1070
  RefPtr<CacheHash>         mRWHash;
1071
1072
  // True if read or write operation is pending. It is used to ensure that
1073
  // mRWBuf is not freed until OnDataRead or OnDataWritten is called.
1074
  bool                      mRWPending;
1075
1076
  // Reading of journal succeeded if true.
1077
  bool                      mJournalReadSuccessfully;
1078
1079
  // Handle used for writing and reading index file.
1080
  RefPtr<CacheFileHandle> mIndexHandle;
1081
  // Handle used for reading journal file.
1082
  RefPtr<CacheFileHandle> mJournalHandle;
1083
  // Used to check the existence of the file during reading process.
1084
  RefPtr<CacheFileHandle> mTmpHandle;
1085
1086
  RefPtr<FileOpenHelper>    mIndexFileOpener;
1087
  RefPtr<FileOpenHelper>    mJournalFileOpener;
1088
  RefPtr<FileOpenHelper>    mTmpFileOpener;
1089
1090
  // Directory enumerator used when building and updating index.
1091
  nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
1092
1093
  // Main index hashtable.
1094
  nsTHashtable<CacheIndexEntry> mIndex;
1095
1096
  // We cannot add, remove or change any entry in mIndex in states READING and
1097
  // WRITING. We track all changes in mPendingUpdates during these states.
1098
  nsTHashtable<CacheIndexEntryUpdate> mPendingUpdates;
1099
1100
  // Contains information statistics for mIndex + mPendingUpdates.
1101
  CacheIndexStats               mIndexStats;
1102
1103
  // When reading journal, we must first parse the whole file and apply the
1104
  // changes iff the journal was read successfully. mTmpJournal is used to store
1105
  // entries from the journal file. We throw away all these entries if parsing
1106
  // of the journal fails or the hash does not match.
1107
  nsTHashtable<CacheIndexEntry> mTmpJournal;
1108
1109
  // FrecencyArray maintains order of entry records for eviction. Ideally, the
1110
  // records would be ordered by frecency all the time, but since this would be
1111
  // quite expensive, we allow certain amount of entries to be out of order.
1112
  // When the frecency is updated the new value is always bigger than the old
1113
  // one. Instead of keeping updated entries at the same position, we move them
1114
  // at the end of the array. This protects recently updated entries from
1115
  // eviction. The array is sorted once we hit the limit of maximum unsorted
1116
  // entries.
1117
  class FrecencyArray
1118
  {
1119
    class Iterator
1120
    {
1121
    public:
1122
      explicit Iterator(nsTArray<CacheIndexRecord *> *aRecs)
1123
        : mRecs(aRecs)
1124
        , mIdx(0)
1125
0
      {
1126
0
        while (!Done() && !(*mRecs)[mIdx]) {
1127
0
          mIdx++;
1128
0
        }
1129
0
      }
1130
1131
0
      bool Done() const { return mIdx == mRecs->Length(); }
1132
1133
      CacheIndexRecord* Get() const
1134
0
      {
1135
0
        MOZ_ASSERT(!Done());
1136
0
        return (*mRecs)[mIdx];
1137
0
      }
1138
1139
      void Next()
1140
0
      {
1141
0
        MOZ_ASSERT(!Done());
1142
0
        ++mIdx;
1143
0
        while (!Done() && !(*mRecs)[mIdx]) {
1144
0
          mIdx++;
1145
0
        }
1146
0
      }
1147
1148
    private:
1149
      nsTArray<CacheIndexRecord *> *mRecs;
1150
      uint32_t mIdx;
1151
    };
1152
1153
  public:
1154
0
    Iterator Iter() { return Iterator(&mRecs); }
1155
1156
    FrecencyArray() : mUnsortedElements(0)
1157
0
                    , mRemovedElements(0) {}
1158
1159
    // Methods used by CacheIndexEntryAutoManage to keep the array up to date.
1160
    void AppendRecord(CacheIndexRecord *aRecord);
1161
    void RemoveRecord(CacheIndexRecord *aRecord);
1162
    void ReplaceRecord(CacheIndexRecord *aOldRecord,
1163
                       CacheIndexRecord *aNewRecord);
1164
    void SortIfNeeded();
1165
1166
0
    size_t Length() const { return mRecs.Length() - mRemovedElements; }
1167
0
    void Clear() { mRecs.Clear(); }
1168
1169
  private:
1170
    friend class CacheIndex;
1171
1172
    nsTArray<CacheIndexRecord *> mRecs;
1173
    uint32_t                     mUnsortedElements;
1174
    // Instead of removing elements from the array immediately, we null them out
1175
    // and the iterator skips them when accessing the array. The null pointers
1176
    // are placed at the end during sorting and we strip them out all at once.
1177
    // This saves moving a lot of memory in nsTArray::RemoveElementsAt.
1178
    uint32_t                     mRemovedElements;
1179
  };
1180
1181
  FrecencyArray mFrecencyArray;
1182
1183
  nsTArray<CacheIndexIterator *> mIterators;
1184
1185
  // This flag is true iff we are between CacheStorageService:Clear() and processing
1186
  // all contexts to be evicted.  It will make UI to show "calculating" instead of
1187
  // any intermediate cache size.
1188
  bool mAsyncGetDiskConsumptionBlocked;
1189
1190
  class DiskConsumptionObserver : public Runnable
1191
  {
1192
  public:
1193
    static DiskConsumptionObserver* Init(nsICacheStorageConsumptionObserver* aObserver)
1194
0
    {
1195
0
      nsWeakPtr observer = do_GetWeakReference(aObserver);
1196
0
      if (!observer)
1197
0
        return nullptr;
1198
0
1199
0
      return new DiskConsumptionObserver(observer);
1200
0
    }
1201
1202
    void OnDiskConsumption(int64_t aSize)
1203
0
    {
1204
0
      mSize = aSize;
1205
0
      NS_DispatchToMainThread(this);
1206
0
    }
1207
1208
  private:
1209
    explicit DiskConsumptionObserver(nsWeakPtr const& aWeakObserver)
1210
      : Runnable("net::CacheIndex::DiskConsumptionObserver")
1211
      , mObserver(aWeakObserver)
1212
      , mSize(0)
1213
0
    {
1214
0
    }
1215
0
    virtual ~DiskConsumptionObserver() {
1216
0
      if (mObserver && !NS_IsMainThread()) {
1217
0
        NS_ReleaseOnMainThreadSystemGroup(
1218
0
          "DiskConsumptionObserver::mObserver", mObserver.forget());
1219
0
      }
1220
0
    }
1221
1222
    NS_IMETHOD Run() override
1223
0
    {
1224
0
      MOZ_ASSERT(NS_IsMainThread());
1225
0
1226
0
      nsCOMPtr<nsICacheStorageConsumptionObserver> observer =
1227
0
        do_QueryReferent(mObserver);
1228
0
1229
0
      mObserver = nullptr;
1230
0
1231
0
      if (observer) {
1232
0
        observer->OnNetworkCacheDiskConsumption(mSize);
1233
0
      }
1234
0
1235
0
      return NS_OK;
1236
0
    }
1237
1238
    nsWeakPtr mObserver;
1239
    int64_t mSize;
1240
  };
1241
1242
  // List of async observers that want to get disk consumption information
1243
  nsTArray<RefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
1244
};
1245
1246
} // namespace net
1247
} // namespace mozilla
1248
1249
#endif