LCOV - code coverage report
Current view: top level - netwerk/cache2 - CacheStorageService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 248 1025 24.2 %
Date: 2017-07-14 16:53:18 Functions: 42 123 34.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13