LCOV - code coverage report
Current view: top level - netwerk/cache2 - CacheIndex.h (source / functions) Hit Total Coverage
Test: output.info Lines: 141 328 43.0 %
Date: 2017-07-14 16:53:18 Functions: 37 79 46.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13