LCOV - code coverage report
Current view: top level - netwerk/cache2 - CacheEntry.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 473 860 55.0 %
Date: 2017-07-14 16:53:18 Functions: 67 105 63.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "CacheLog.h"
       6             : #include "CacheEntry.h"
       7             : #include "CacheStorageService.h"
       8             : #include "CacheObserver.h"
       9             : #include "CacheFileUtils.h"
      10             : #include "CacheIndex.h"
      11             : 
      12             : #include "nsIInputStream.h"
      13             : #include "nsIOutputStream.h"
      14             : #include "nsISeekableStream.h"
      15             : #include "nsIURI.h"
      16             : #include "nsICacheEntryOpenCallback.h"
      17             : #include "nsICacheStorage.h"
      18             : #include "nsISerializable.h"
      19             : #include "nsIStreamTransportService.h"
      20             : #include "nsISizeOf.h"
      21             : 
      22             : #include "nsComponentManagerUtils.h"
      23             : #include "nsServiceManagerUtils.h"
      24             : #include "nsString.h"
      25             : #include "nsProxyRelease.h"
      26             : #include "nsSerializationHelper.h"
      27             : #include "nsThreadUtils.h"
      28             : #include "mozilla/Telemetry.h"
      29             : #include "mozilla/IntegerPrintfMacros.h"
      30             : #include <math.h>
      31             : #include <algorithm>
      32             : 
      33             : namespace mozilla {
      34             : namespace net {
      35             : 
      36             : static uint32_t const ENTRY_WANTED =
      37             :   nsICacheEntryOpenCallback::ENTRY_WANTED;
      38             : static uint32_t const RECHECK_AFTER_WRITE_FINISHED =
      39             :   nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED;
      40             : static uint32_t const ENTRY_NEEDS_REVALIDATION =
      41             :   nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION;
      42             : static uint32_t const ENTRY_NOT_WANTED =
      43             :   nsICacheEntryOpenCallback::ENTRY_NOT_WANTED;
      44             : 
      45         119 : NS_IMPL_ISUPPORTS(CacheEntryHandle, nsICacheEntry)
      46             : 
      47             : // CacheEntryHandle
      48             : 
      49          28 : CacheEntryHandle::CacheEntryHandle(CacheEntry* aEntry)
      50          28 : : mEntry(aEntry)
      51             : {
      52             : #ifdef DEBUG
      53          28 :   if (!mEntry->HandlesCount()) {
      54             :     // CacheEntry.mHandlesCount must go from zero to one only under
      55             :     // the service lock. Can access CacheStorageService::Self() w/o a check
      56             :     // since CacheEntry hrefs it.
      57          10 :     CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
      58             :   }
      59             : #endif
      60             : 
      61          28 :   mEntry->AddHandleRef();
      62             : 
      63          28 :   LOG(("New CacheEntryHandle %p for entry %p", this, aEntry));
      64          28 : }
      65             : 
      66          84 : CacheEntryHandle::~CacheEntryHandle()
      67             : {
      68          28 :   mEntry->ReleaseHandleRef();
      69          28 :   mEntry->OnHandleClosed(this);
      70          84 : }
      71             : 
      72             : // CacheEntry::Callback
      73             : 
      74          13 : CacheEntry::Callback::Callback(CacheEntry* aEntry,
      75             :                                nsICacheEntryOpenCallback *aCallback,
      76             :                                bool aReadOnly, bool aCheckOnAnyThread,
      77          13 :                                bool aSecret)
      78             : : mEntry(aEntry)
      79             : , mCallback(aCallback)
      80             : , mTarget(GetCurrentThreadEventTarget())
      81             : , mReadOnly(aReadOnly)
      82             : , mRevalidating(false)
      83             : , mCheckOnAnyThread(aCheckOnAnyThread)
      84             : , mRecheckAfterWrite(false)
      85             : , mNotWanted(false)
      86             : , mSecret(aSecret)
      87             : , mDoomWhenFoundPinned(false)
      88          13 : , mDoomWhenFoundNonPinned(false)
      89             : {
      90          13 :   MOZ_COUNT_CTOR(CacheEntry::Callback);
      91             : 
      92             :   // The counter may go from zero to non-null only under the service lock
      93             :   // but here we expect it to be already positive.
      94          13 :   MOZ_ASSERT(mEntry->HandlesCount());
      95          13 :   mEntry->AddHandleRef();
      96          13 : }
      97             : 
      98           0 : CacheEntry::Callback::Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus)
      99             : : mEntry(aEntry)
     100             : , mReadOnly(false)
     101             : , mRevalidating(false)
     102             : , mCheckOnAnyThread(true)
     103             : , mRecheckAfterWrite(false)
     104             : , mNotWanted(false)
     105             : , mSecret(false)
     106             : , mDoomWhenFoundPinned(aDoomWhenFoundInPinStatus == true)
     107           0 : , mDoomWhenFoundNonPinned(aDoomWhenFoundInPinStatus == false)
     108             : {
     109           0 :   MOZ_COUNT_CTOR(CacheEntry::Callback);
     110           0 :   MOZ_ASSERT(mEntry->HandlesCount());
     111           0 :   mEntry->AddHandleRef();
     112           0 : }
     113             : 
     114          36 : CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
     115             : : mEntry(aThat.mEntry)
     116             : , mCallback(aThat.mCallback)
     117             : , mTarget(aThat.mTarget)
     118          36 : , mReadOnly(aThat.mReadOnly)
     119          36 : , mRevalidating(aThat.mRevalidating)
     120          36 : , mCheckOnAnyThread(aThat.mCheckOnAnyThread)
     121          36 : , mRecheckAfterWrite(aThat.mRecheckAfterWrite)
     122          36 : , mNotWanted(aThat.mNotWanted)
     123          36 : , mSecret(aThat.mSecret)
     124          36 : , mDoomWhenFoundPinned(aThat.mDoomWhenFoundPinned)
     125         288 : , mDoomWhenFoundNonPinned(aThat.mDoomWhenFoundNonPinned)
     126             : {
     127          36 :   MOZ_COUNT_CTOR(CacheEntry::Callback);
     128             : 
     129             :   // The counter may go from zero to non-null only under the service lock
     130             :   // but here we expect it to be already positive.
     131          36 :   MOZ_ASSERT(mEntry->HandlesCount());
     132          36 :   mEntry->AddHandleRef();
     133          36 : }
     134             : 
     135          98 : CacheEntry::Callback::~Callback()
     136             : {
     137          49 :   ProxyRelease("CacheEntry::Callback::mCallback", mCallback, mTarget);
     138             : 
     139          49 :   mEntry->ReleaseHandleRef();
     140          49 :   MOZ_COUNT_DTOR(CacheEntry::Callback);
     141          49 : }
     142             : 
     143           0 : void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
     144             : {
     145           0 :   if (mEntry == aEntry)
     146           0 :     return;
     147             : 
     148             :   // The counter may go from zero to non-null only under the service lock
     149             :   // but here we expect it to be already positive.
     150           0 :   MOZ_ASSERT(aEntry->HandlesCount());
     151           0 :   aEntry->AddHandleRef();
     152           0 :   mEntry->ReleaseHandleRef();
     153           0 :   mEntry = aEntry;
     154             : }
     155             : 
     156          24 : bool CacheEntry::Callback::DeferDoom(bool *aDoom) const
     157             : {
     158          24 :   MOZ_ASSERT(mEntry->mPinningKnown);
     159             : 
     160          24 :   if (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) || MOZ_UNLIKELY(mDoomWhenFoundPinned)) {
     161           0 :     *aDoom = (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && MOZ_LIKELY(!mEntry->mPinned)) ||
     162           0 :              (MOZ_UNLIKELY(mDoomWhenFoundPinned) && MOZ_UNLIKELY(mEntry->mPinned));
     163             : 
     164           0 :     return true;
     165             :   }
     166             : 
     167          24 :   return false;
     168             : }
     169             : 
     170          15 : nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
     171             : {
     172          15 :   if (!mCheckOnAnyThread) {
     173             :     // Check we are on the target
     174           0 :     return mTarget->IsOnCurrentThread(aOnCheckThread);
     175             :   }
     176             : 
     177             :   // We can invoke check anywhere
     178          15 :   *aOnCheckThread = true;
     179          15 :   return NS_OK;
     180             : }
     181             : 
     182          19 : nsresult CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread) const
     183             : {
     184          19 :   return mTarget->IsOnCurrentThread(aOnAvailThread);
     185             : }
     186             : 
     187             : // CacheEntry
     188             : 
     189         391 : NS_IMPL_ISUPPORTS(CacheEntry,
     190             :                   nsICacheEntry,
     191             :                   nsIRunnable,
     192             :                   CacheFileListener)
     193             : 
     194           5 : CacheEntry::CacheEntry(const nsACString& aStorageID,
     195             :                        const nsACString& aURI,
     196             :                        const nsACString& aEnhanceID,
     197             :                        bool aUseDisk,
     198             :                        bool aSkipSizeCheck,
     199           5 :                        bool aPin)
     200             : : mFrecency(0)
     201             : , mSortingExpirationTime(uint32_t(-1))
     202             : , mLock("CacheEntry")
     203             : , mFileStatus(NS_ERROR_NOT_INITIALIZED)
     204             : , mURI(aURI)
     205             : , mEnhanceID(aEnhanceID)
     206             : , mStorageID(aStorageID)
     207             : , mUseDisk(aUseDisk)
     208             : , mSkipSizeCheck(aSkipSizeCheck)
     209             : , mIsDoomed(false)
     210             : , mSecurityInfoLoaded(false)
     211             : , mPreventCallbacks(false)
     212             : , mHasData(false)
     213             : , mPinned(aPin)
     214             : , mPinningKnown(false)
     215             : , mState(NOTLOADED)
     216             : , mRegistration(NEVERREGISTERED)
     217             : , mWriter(nullptr)
     218             : , mPredictedDataSize(0)
     219           5 : , mUseCount(0)
     220             : {
     221           5 :   LOG(("CacheEntry::CacheEntry [this=%p]", this));
     222             : 
     223           5 :   mService = CacheStorageService::Self();
     224             : 
     225           5 :   CacheStorageService::Self()->RecordMemoryOnlyEntry(
     226          10 :     this, !aUseDisk, true /* overwrite */);
     227           5 : }
     228             : 
     229           0 : CacheEntry::~CacheEntry()
     230             : {
     231           0 :   LOG(("CacheEntry::~CacheEntry [this=%p]", this));
     232           0 : }
     233             : 
     234           0 : char const * CacheEntry::StateString(uint32_t aState)
     235             : {
     236           0 :   switch (aState) {
     237           0 :   case NOTLOADED:     return "NOTLOADED";
     238           0 :   case LOADING:       return "LOADING";
     239           0 :   case EMPTY:         return "EMPTY";
     240           0 :   case WRITING:       return "WRITING";
     241           0 :   case READY:         return "READY";
     242           0 :   case REVALIDATING:  return "REVALIDATING";
     243             :   }
     244             : 
     245           0 :   return "?";
     246             : }
     247             : 
     248           5 : nsresult CacheEntry::HashingKeyWithStorage(nsACString &aResult) const
     249             : {
     250           5 :   return HashingKey(mStorageID, mEnhanceID, mURI, aResult);
     251             : }
     252             : 
     253          14 : nsresult CacheEntry::HashingKey(nsACString &aResult) const
     254             : {
     255          14 :   return HashingKey(EmptyCString(), mEnhanceID, mURI, aResult);
     256             : }
     257             : 
     258             : // static
     259           0 : nsresult CacheEntry::HashingKey(const nsACString& aStorageID,
     260             :                                 const nsACString& aEnhanceID,
     261             :                                 nsIURI* aURI,
     262             :                                 nsACString &aResult)
     263             : {
     264           0 :   nsAutoCString spec;
     265           0 :   nsresult rv = aURI->GetAsciiSpec(spec);
     266           0 :   NS_ENSURE_SUCCESS(rv, rv);
     267             : 
     268           0 :   return HashingKey(aStorageID, aEnhanceID, spec, aResult);
     269             : }
     270             : 
     271             : // static
     272          39 : nsresult CacheEntry::HashingKey(const nsACString& aStorageID,
     273             :                                 const nsACString& aEnhanceID,
     274             :                                 const nsACString& aURISpec,
     275             :                                 nsACString &aResult)
     276             : {
     277             :   /**
     278             :    * This key is used to salt hash that is a base for disk file name.
     279             :    * Changing it will cause we will not be able to find files on disk.
     280             :    */
     281             : 
     282          39 :   aResult.Assign(aStorageID);
     283             : 
     284          39 :   if (!aEnhanceID.IsEmpty()) {
     285           7 :     CacheFileUtils::AppendTagWithValue(aResult, '~', aEnhanceID);
     286             :   }
     287             : 
     288             :   // Appending directly
     289          39 :   aResult.Append(':');
     290          39 :   aResult.Append(aURISpec);
     291             : 
     292          39 :   return NS_OK;
     293             : }
     294             : 
     295          13 : void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
     296             : {
     297          13 :   LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
     298             :     this, StateString(mState), aFlags, aCallback));
     299             : 
     300          13 :   bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
     301          13 :   bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
     302          13 :   bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
     303          13 :   bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
     304          13 :   bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
     305          13 :   bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY;
     306             : 
     307          13 :   MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
     308          13 :   MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
     309             : 
     310          26 :   Callback callback(this, aCallback, readonly, multithread, secret);
     311             : 
     312          13 :   if (!Open(callback, truncate, priority, bypassIfBusy)) {
     313             :     // We get here when the callback wants to bypass cache when it's busy.
     314           0 :     LOG(("  writing or revalidating, callback wants to bypass cache"));
     315           0 :     callback.mNotWanted = true;
     316           0 :     InvokeAvailableCallback(callback);
     317             :   }
     318          13 : }
     319             : 
     320          13 : bool CacheEntry::Open(Callback & aCallback, bool aTruncate,
     321             :                       bool aPriority, bool aBypassIfBusy)
     322             : {
     323          26 :   mozilla::MutexAutoLock lock(mLock);
     324             : 
     325             :   // Check state under the lock
     326          13 :   if (aBypassIfBusy && (mState == WRITING || mState == REVALIDATING)) {
     327           0 :     return false;
     328             :   }
     329             : 
     330          13 :   RememberCallback(aCallback);
     331             : 
     332             :   // Load() opens the lock
     333          13 :   if (Load(aTruncate, aPriority)) {
     334             :     // Loading is in progress...
     335           5 :     return true;
     336             :   }
     337             : 
     338           8 :   InvokeCallbacks();
     339             : 
     340           8 :   return true;
     341             : }
     342             : 
     343          13 : bool CacheEntry::Load(bool aTruncate, bool aPriority)
     344             : {
     345          13 :   LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate));
     346             : 
     347          13 :   mLock.AssertCurrentThreadOwns();
     348             : 
     349          13 :   if (mState > LOADING) {
     350           8 :     LOG(("  already loaded"));
     351           8 :     return false;
     352             :   }
     353             : 
     354           5 :   if (mState == LOADING) {
     355           0 :     LOG(("  already loading"));
     356           0 :     return true;
     357             :   }
     358             : 
     359           5 :   mState = LOADING;
     360             : 
     361           5 :   MOZ_ASSERT(!mFile);
     362             : 
     363             :   nsresult rv;
     364             : 
     365          10 :   nsAutoCString fileKey;
     366           5 :   rv = HashingKeyWithStorage(fileKey);
     367             : 
     368           5 :   bool reportMiss = false;
     369             : 
     370             :   // Check the index under two conditions for two states and take appropriate action:
     371             :   // 1. When this is a disk entry and not told to truncate, check there is a disk file.
     372             :   //    If not, set the 'truncate' flag to true so that this entry will open instantly
     373             :   //    as a new one.
     374             :   // 2. When this is a memory-only entry, check there is a disk file.
     375             :   //    If there is or could be, doom that file.
     376           5 :   if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) {
     377             :     // Check the index right now to know we have or have not the entry
     378             :     // as soon as possible.
     379             :     CacheIndex::EntryStatus status;
     380           5 :     if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
     381           5 :       switch (status) {
     382             :       case CacheIndex::DOES_NOT_EXIST:
     383             :         // Doesn't apply to memory-only entries, Load() is called only once for them
     384             :         // and never again for their session lifetime.
     385           0 :         if (!aTruncate && mUseDisk) {
     386           0 :           LOG(("  entry doesn't exist according information from the index, truncating"));
     387           0 :           reportMiss = true;
     388           0 :           aTruncate = true;
     389             :         }
     390           0 :         break;
     391             :       case CacheIndex::EXISTS:
     392             :       case CacheIndex::DO_NOT_KNOW:
     393           5 :         if (!mUseDisk) {
     394           0 :           LOG(("  entry open as memory-only, but there is a file, status=%d, dooming it", status));
     395           0 :           CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
     396             :         }
     397           5 :         break;
     398             :       }
     399             :     }
     400             :   }
     401             : 
     402           5 :   mFile = new CacheFile();
     403             : 
     404           5 :   BackgroundOp(Ops::REGISTER);
     405             : 
     406           5 :   bool directLoad = aTruncate || !mUseDisk;
     407           5 :   if (directLoad) {
     408             :     // mLoadStart will be used to calculate telemetry of life-time of this entry.
     409             :     // Low resulution is then enough.
     410           0 :     mLoadStart = TimeStamp::NowLoRes();
     411           0 :     mPinningKnown = true;
     412             :   } else {
     413           5 :     mLoadStart = TimeStamp::Now();
     414             :   }
     415             : 
     416             :   {
     417          10 :     mozilla::MutexAutoUnlock unlock(mLock);
     418             : 
     419           5 :     if (reportMiss) {
     420             :       CacheFileUtils::DetailedCacheHitTelemetry::AddRecord(
     421           0 :         CacheFileUtils::DetailedCacheHitTelemetry::MISS, mLoadStart);
     422             :     }
     423             : 
     424           5 :     LOG(("  performing load, file=%p", mFile.get()));
     425           5 :     if (NS_SUCCEEDED(rv)) {
     426          25 :       rv = mFile->Init(fileKey,
     427             :                        aTruncate,
     428           5 :                        !mUseDisk,
     429           5 :                        mSkipSizeCheck,
     430             :                        aPriority,
     431           5 :                        mPinned,
     432          15 :                        directLoad ? nullptr : this);
     433             :     }
     434             : 
     435           5 :     if (NS_FAILED(rv)) {
     436           0 :       mFileStatus = rv;
     437           0 :       AsyncDoom(nullptr);
     438           0 :       return false;
     439             :     }
     440             :   }
     441             : 
     442           5 :   if (directLoad) {
     443             :     // Just fake the load has already been done as "new".
     444           0 :     mFileStatus = NS_OK;
     445           0 :     mState = EMPTY;
     446             :   }
     447             : 
     448           5 :   return mState == LOADING;
     449             : }
     450             : 
     451           5 : NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
     452             : {
     453           5 :   LOG(("CacheEntry::OnFileReady [this=%p, rv=0x%08" PRIx32 ", new=%d]",
     454             :        this, static_cast<uint32_t>(aResult), aIsNew));
     455             : 
     456           5 :   MOZ_ASSERT(!mLoadStart.IsNull());
     457             : 
     458           5 :   if (NS_SUCCEEDED(aResult)) {
     459           5 :     if (aIsNew) {
     460             :       CacheFileUtils::DetailedCacheHitTelemetry::AddRecord(
     461           2 :         CacheFileUtils::DetailedCacheHitTelemetry::MISS, mLoadStart);
     462             :     } else {
     463             :       CacheFileUtils::DetailedCacheHitTelemetry::AddRecord(
     464           3 :         CacheFileUtils::DetailedCacheHitTelemetry::HIT, mLoadStart);
     465             :     }
     466             :   }
     467             : 
     468             :   // OnFileReady, that is the only code that can transit from LOADING
     469             :   // to any follow-on state and can only be invoked ones on an entry.
     470             :   // Until this moment there is no consumer that could manipulate
     471             :   // the entry state.
     472             : 
     473          10 :   mozilla::MutexAutoLock lock(mLock);
     474             : 
     475           5 :   MOZ_ASSERT(mState == LOADING);
     476             : 
     477           8 :   mState = (aIsNew || NS_FAILED(aResult))
     478           7 :     ? EMPTY
     479             :     : READY;
     480             : 
     481           5 :   mFileStatus = aResult;
     482             : 
     483           5 :   mPinned = mFile->IsPinned();;
     484           5 :   mPinningKnown = true;
     485           5 :   LOG(("  pinning=%d", mPinned));
     486             : 
     487           5 :   if (mState == READY) {
     488           3 :     mHasData = true;
     489             : 
     490             :     uint32_t frecency;
     491           3 :     mFile->GetFrecency(&frecency);
     492             :     // mFrecency is held in a double to increase computance precision.
     493             :     // It is ok to persist frecency only as a uint32 with some math involved.
     494           3 :     mFrecency = INT2FRECENCY(frecency);
     495             :   }
     496             : 
     497           5 :   InvokeCallbacks();
     498             : 
     499          10 :   return NS_OK;
     500             : }
     501             : 
     502           0 : NS_IMETHODIMP CacheEntry::OnFileDoomed(nsresult aResult)
     503             : {
     504           0 :   if (mDoomCallback) {
     505             :     RefPtr<DoomCallbackRunnable> event =
     506           0 :       new DoomCallbackRunnable(this, aResult);
     507           0 :     NS_DispatchToMainThread(event);
     508             :   }
     509             : 
     510           0 :   return NS_OK;
     511             : }
     512             : 
     513           0 : already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
     514             :                                                                nsICacheEntryOpenCallback* aCallback)
     515             : {
     516           0 :   LOG(("CacheEntry::ReopenTruncated [this=%p]", this));
     517             : 
     518           0 :   mLock.AssertCurrentThreadOwns();
     519             : 
     520             :   // Hold callbacks invocation, AddStorageEntry would invoke from doom prematurly
     521           0 :   mPreventCallbacks = true;
     522             : 
     523           0 :   RefPtr<CacheEntryHandle> handle;
     524           0 :   RefPtr<CacheEntry> newEntry;
     525             :   {
     526           0 :     if (mPinned) {
     527           0 :       MOZ_ASSERT(mUseDisk);
     528             :       // We want to pin even no-store entries (the case we recreate a disk entry as
     529             :       // a memory-only entry.)
     530           0 :       aMemoryOnly = false;
     531             :     }
     532             : 
     533           0 :     mozilla::MutexAutoUnlock unlock(mLock);
     534             : 
     535             :     // The following call dooms this entry (calls DoomAlreadyRemoved on us)
     536           0 :     nsresult rv = CacheStorageService::Self()->AddStorageEntry(
     537           0 :       GetStorageID(), GetURI(), GetEnhanceID(),
     538           0 :       mUseDisk && !aMemoryOnly,
     539           0 :       mSkipSizeCheck,
     540           0 :       mPinned,
     541             :       true, // truncate existing (this one)
     542           0 :       getter_AddRefs(handle));
     543             : 
     544           0 :     if (NS_SUCCEEDED(rv)) {
     545           0 :       newEntry = handle->Entry();
     546           0 :       LOG(("  exchanged entry %p by entry %p, rv=0x%08" PRIx32,
     547             :            this, newEntry.get(), static_cast<uint32_t>(rv)));
     548           0 :       newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE);
     549             :     } else {
     550           0 :       LOG(("  exchanged of entry %p failed, rv=0x%08" PRIx32,
     551             :            this, static_cast<uint32_t>(rv)));
     552           0 :       AsyncDoom(nullptr);
     553             :     }
     554             :   }
     555             : 
     556           0 :   mPreventCallbacks = false;
     557             : 
     558           0 :   if (!newEntry)
     559           0 :     return nullptr;
     560             : 
     561           0 :   newEntry->TransferCallbacks(*this);
     562           0 :   mCallbacks.Clear();
     563             : 
     564             :   // Must return a new write handle, since the consumer is expected to
     565             :   // write to this newly recreated entry.  The |handle| is only a common
     566             :   // reference counter and doesn't revert entry state back when write
     567             :   // fails and also doesn't update the entry frecency.  Not updating
     568             :   // frecency causes entries to not be purged from our memory pools.
     569             :   RefPtr<CacheEntryHandle> writeHandle =
     570           0 :     newEntry->NewWriteHandle();
     571           0 :   return writeHandle.forget();
     572             : }
     573             : 
     574           0 : void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
     575             : {
     576           0 :   mozilla::MutexAutoLock lock(mLock);
     577             : 
     578           0 :   LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
     579             :     this, &aFromEntry));
     580             : 
     581           0 :   if (!mCallbacks.Length())
     582           0 :     mCallbacks.SwapElements(aFromEntry.mCallbacks);
     583             :   else
     584           0 :     mCallbacks.AppendElements(aFromEntry.mCallbacks);
     585             : 
     586           0 :   uint32_t callbacksLength = mCallbacks.Length();
     587           0 :   if (callbacksLength) {
     588             :     // Carry the entry reference (unfortunately, needs to be done manually...)
     589           0 :     for (uint32_t i = 0; i < callbacksLength; ++i)
     590           0 :       mCallbacks[i].ExchangeEntry(this);
     591             : 
     592           0 :     BackgroundOp(Ops::CALLBACKS, true);
     593             :   }
     594           0 : }
     595             : 
     596          13 : void CacheEntry::RememberCallback(Callback & aCallback)
     597             : {
     598          13 :   mLock.AssertCurrentThreadOwns();
     599             : 
     600          13 :   LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
     601             :     this, aCallback.mCallback.get(), StateString(mState)));
     602             : 
     603          13 :   mCallbacks.AppendElement(aCallback);
     604          13 : }
     605             : 
     606           0 : void CacheEntry::InvokeCallbacksLock()
     607             : {
     608           0 :   mozilla::MutexAutoLock lock(mLock);
     609           0 :   InvokeCallbacks();
     610           0 : }
     611             : 
     612          21 : void CacheEntry::InvokeCallbacks()
     613             : {
     614          21 :   mLock.AssertCurrentThreadOwns();
     615             : 
     616          21 :   LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
     617             : 
     618             :   // Invoke first all r/w callbacks, then all r/o callbacks.
     619          21 :   if (InvokeCallbacks(false))
     620          20 :     InvokeCallbacks(true);
     621             : 
     622          21 :   LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
     623          21 : }
     624             : 
     625          41 : bool CacheEntry::InvokeCallbacks(bool aReadOnly)
     626             : {
     627          41 :   mLock.AssertCurrentThreadOwns();
     628             : 
     629          82 :   RefPtr<CacheEntryHandle> recreatedHandle;
     630             : 
     631          41 :   uint32_t i = 0;
     632          89 :   while (i < mCallbacks.Length()) {
     633          25 :     if (mPreventCallbacks) {
     634           0 :       LOG(("  callbacks prevented!"));
     635           1 :       return false;
     636             :     }
     637             : 
     638          25 :     if (!mIsDoomed && (mState == WRITING || mState == REVALIDATING)) {
     639           1 :       LOG(("  entry is being written/revalidated"));
     640           1 :       return false;
     641             :     }
     642             : 
     643             :     bool recreate;
     644          24 :     if (mCallbacks[i].DeferDoom(&recreate)) {
     645           0 :       mCallbacks.RemoveElementAt(i);
     646           0 :       if (!recreate) {
     647           9 :         continue;
     648             :       }
     649             : 
     650           0 :       LOG(("  defer doom marker callback hit positive, recreating"));
     651           0 :       recreatedHandle = ReopenTruncated(!mUseDisk, nullptr);
     652           0 :       break;
     653             :     }
     654             : 
     655          24 :     if (mCallbacks[i].mReadOnly != aReadOnly) {
     656             :       // Callback is not r/w or r/o, go to another one in line
     657           9 :       ++i;
     658           9 :       continue;
     659             :     }
     660             : 
     661             :     bool onCheckThread;
     662          15 :     nsresult rv = mCallbacks[i].OnCheckThread(&onCheckThread);
     663             : 
     664          15 :     if (NS_SUCCEEDED(rv) && !onCheckThread) {
     665             :       // Redispatch to the target thread
     666           0 :       rv = mCallbacks[i].mTarget->Dispatch(NewRunnableMethod("net::CacheEntry::InvokeCallbacksLock",
     667             :                                                              this,
     668             :                                                              &CacheEntry::InvokeCallbacksLock),
     669           0 :                                            nsIEventTarget::DISPATCH_NORMAL);
     670           0 :       if (NS_SUCCEEDED(rv)) {
     671           0 :         LOG(("  re-dispatching to target thread"));
     672           0 :         return false;
     673             :       }
     674             :     }
     675             : 
     676          30 :     Callback callback = mCallbacks[i];
     677          15 :     mCallbacks.RemoveElementAt(i);
     678             : 
     679          15 :     if (NS_SUCCEEDED(rv) && !InvokeCallback(callback)) {
     680             :       // Callback didn't fire, put it back and go to another one in line.
     681             :       // Only reason InvokeCallback returns false is that onCacheEntryCheck
     682             :       // returns RECHECK_AFTER_WRITE_FINISHED.  If we would stop the loop, other
     683             :       // readers or potential writers would be unnecessarily kept from being
     684             :       // invoked.
     685           2 :       size_t pos = std::min(mCallbacks.Length(), static_cast<size_t>(i));
     686           2 :       mCallbacks.InsertElementAt(pos, callback);
     687           2 :       ++i;
     688             :     }
     689             :   }
     690             : 
     691          40 :   if (recreatedHandle) {
     692             :     // Must be released outside of the lock, enters InvokeCallback on the new entry
     693           0 :     mozilla::MutexAutoUnlock unlock(mLock);
     694           0 :     recreatedHandle = nullptr;
     695             :   }
     696             : 
     697          40 :   return true;
     698             : }
     699             : 
     700          16 : bool CacheEntry::InvokeCallback(Callback & aCallback)
     701             : {
     702          16 :   LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
     703             :     this, StateString(mState), aCallback.mCallback.get()));
     704             : 
     705          16 :   mLock.AssertCurrentThreadOwns();
     706             : 
     707             :   // When this entry is doomed we want to notify the callback any time
     708          16 :   if (!mIsDoomed) {
     709             :     // When we are here, the entry must be loaded from disk
     710          16 :     MOZ_ASSERT(mState > LOADING);
     711             : 
     712          16 :     if (mState == WRITING || mState == REVALIDATING) {
     713             :       // Prevent invoking other callbacks since one of them is now writing
     714             :       // or revalidating this entry.  No consumers should get this entry
     715             :       // until metadata are filled with values downloaded from the server
     716             :       // or the entry revalidated and output stream has been opened.
     717           0 :       LOG(("  entry is being written/revalidated, callback bypassed"));
     718           0 :       return false;
     719             :     }
     720             : 
     721             :     // mRecheckAfterWrite flag already set means the callback has already passed
     722             :     // the onCacheEntryCheck call. Until the current write is not finished this
     723             :     // callback will be bypassed.
     724          16 :     if (!aCallback.mRecheckAfterWrite) {
     725             : 
     726          14 :       if (!aCallback.mReadOnly) {
     727           7 :         if (mState == EMPTY) {
     728             :           // Advance to writing state, we expect to invoke the callback and let
     729             :           // it fill content of this entry.  Must set and check the state here
     730             :           // to prevent more then one
     731           2 :           mState = WRITING;
     732           2 :           LOG(("  advancing to WRITING state"));
     733             :         }
     734             : 
     735           7 :         if (!aCallback.mCallback) {
     736             :           // We can be given no callback only in case of recreate, it is ok
     737             :           // to advance to WRITING state since the caller of recreate is expected
     738             :           // to write this entry now.
     739           0 :           return true;
     740             :         }
     741             :       }
     742             : 
     743          14 :       if (mState == READY) {
     744             :         // Metadata present, validate the entry
     745             :         uint32_t checkResult;
     746             :         {
     747             :           // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
     748          20 :           mozilla::MutexAutoUnlock unlock(mLock);
     749             : 
     750          20 :           nsresult rv = aCallback.mCallback->OnCacheEntryCheck(
     751          20 :             this, nullptr, &checkResult);
     752          10 :           LOG(("  OnCacheEntryCheck: rv=0x%08" PRIx32 ", result=%" PRId32,
     753             :                static_cast<uint32_t>(rv), static_cast<uint32_t>(checkResult)));
     754             : 
     755          10 :           if (NS_FAILED(rv))
     756           0 :             checkResult = ENTRY_NOT_WANTED;
     757             :         }
     758             : 
     759          10 :         aCallback.mRevalidating = checkResult == ENTRY_NEEDS_REVALIDATION;
     760             : 
     761          10 :         switch (checkResult) {
     762             :         case ENTRY_WANTED:
     763             :           // Nothing more to do here, the consumer is responsible to handle
     764             :           // the result of OnCacheEntryCheck it self.
     765             :           // Proceed to callback...
     766           9 :           break;
     767             : 
     768             :         case RECHECK_AFTER_WRITE_FINISHED:
     769           1 :           LOG(("  consumer will check on the entry again after write is done"));
     770             :           // The consumer wants the entry to complete first.
     771           1 :           aCallback.mRecheckAfterWrite = true;
     772           1 :           break;
     773             : 
     774             :         case ENTRY_NEEDS_REVALIDATION:
     775           0 :           LOG(("  will be holding callbacks until entry is revalidated"));
     776             :           // State is READY now and from that state entry cannot transit to any other
     777             :           // state then REVALIDATING for which cocurrency is not an issue.  Potentially
     778             :           // no need to lock here.
     779           0 :           mState = REVALIDATING;
     780           0 :           break;
     781             : 
     782             :         case ENTRY_NOT_WANTED:
     783           0 :           LOG(("  consumer not interested in the entry"));
     784             :           // Do not give this entry to the consumer, it is not interested in us.
     785           0 :           aCallback.mNotWanted = true;
     786           0 :           break;
     787             :         }
     788             :       }
     789             :     }
     790             :   }
     791             : 
     792          16 :   if (aCallback.mCallback) {
     793          16 :     if (!mIsDoomed && aCallback.mRecheckAfterWrite) {
     794             :       // If we don't have data and the callback wants a complete entry,
     795             :       // don't invoke now.
     796           3 :       bool bypass = !mHasData;
     797           3 :       if (!bypass && NS_SUCCEEDED(mFileStatus)) {
     798             :         int64_t _unused;
     799           3 :         bypass = !mFile->DataSize(&_unused);
     800             :       }
     801             : 
     802           3 :       if (bypass) {
     803           2 :         LOG(("  bypassing, entry data still being written"));
     804           5 :         return false;
     805             :       }
     806             : 
     807             :       // Entry is complete now, do the check+avail call again
     808           1 :       aCallback.mRecheckAfterWrite = false;
     809           1 :       return InvokeCallback(aCallback);
     810             :     }
     811             : 
     812          26 :     mozilla::MutexAutoUnlock unlock(mLock);
     813          13 :     InvokeAvailableCallback(aCallback);
     814             :   }
     815             : 
     816          13 :   return true;
     817             : }
     818             : 
     819          19 : void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
     820             : {
     821          19 :   LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
     822             :     this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));
     823             : 
     824             :   nsresult rv;
     825             : 
     826          19 :   uint32_t const state = mState;
     827             : 
     828             :   // When we are here, the entry must be loaded from disk
     829          19 :   MOZ_ASSERT(state > LOADING || mIsDoomed);
     830             : 
     831             :   bool onAvailThread;
     832          19 :   rv = aCallback.OnAvailThread(&onAvailThread);
     833          19 :   if (NS_FAILED(rv)) {
     834           0 :     LOG(("  target thread dead?"));
     835          17 :     return;
     836             :   }
     837             : 
     838          19 :   if (!onAvailThread) {
     839             :     // Dispatch to the right thread
     840             :     RefPtr<AvailableCallbackRunnable> event =
     841          12 :       new AvailableCallbackRunnable(this, aCallback);
     842             : 
     843           6 :     rv = aCallback.mTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
     844           6 :     LOG(("  redispatched, (rv = 0x%08" PRIx32 ")", static_cast<uint32_t>(rv)));
     845           6 :     return;
     846             :   }
     847             : 
     848          13 :   if (mIsDoomed || aCallback.mNotWanted) {
     849           0 :     LOG(("  doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
     850           0 :     aCallback.mCallback->OnCacheEntryAvailable(
     851           0 :       nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
     852           0 :     return;
     853             :   }
     854             : 
     855          13 :   if (state == READY) {
     856           9 :     LOG(("  ready/has-meta, notifying OCEA with entry and NS_OK"));
     857             : 
     858           9 :     if (!aCallback.mSecret)
     859             :     {
     860           8 :       mozilla::MutexAutoLock lock(mLock);
     861           4 :       BackgroundOp(Ops::FRECENCYUPDATE);
     862             :     }
     863             : 
     864           9 :     OnFetched(aCallback);
     865             : 
     866          18 :     RefPtr<CacheEntryHandle> handle = NewHandle();
     867           9 :     aCallback.mCallback->OnCacheEntryAvailable(
     868           9 :       handle, false, nullptr, NS_OK);
     869           9 :     return;
     870             :   }
     871             : 
     872             :   // R/O callbacks may do revalidation, let them fall through
     873           4 :   if (aCallback.mReadOnly && !aCallback.mRevalidating) {
     874           2 :     LOG(("  r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
     875           2 :     aCallback.mCallback->OnCacheEntryAvailable(
     876           2 :       nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
     877           2 :     return;
     878             :   }
     879             : 
     880             :   // This is a new or potentially non-valid entry and needs to be fetched first.
     881             :   // The CacheEntryHandle blocks other consumers until the channel
     882             :   // either releases the entry or marks metadata as filled or whole entry valid,
     883             :   // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.
     884             : 
     885             :   // Consumer will be responsible to fill or validate the entry metadata and data.
     886             : 
     887           2 :   OnFetched(aCallback);
     888             : 
     889           4 :   RefPtr<CacheEntryHandle> handle = NewWriteHandle();
     890           4 :   rv = aCallback.mCallback->OnCacheEntryAvailable(
     891           4 :     handle, state == WRITING, nullptr, NS_OK);
     892             : 
     893           2 :   if (NS_FAILED(rv)) {
     894           0 :     LOG(("  writing/revalidating failed (0x%08" PRIx32 ")", static_cast<uint32_t>(rv)));
     895             : 
     896             :     // Consumer given a new entry failed to take care of the entry.
     897           0 :     OnHandleClosed(handle);
     898           0 :     return;
     899             :   }
     900             : 
     901           2 :   LOG(("  writing/revalidating"));
     902             : }
     903             : 
     904          11 : void CacheEntry::OnFetched(Callback const & aCallback)
     905             : {
     906          11 :   if (NS_SUCCEEDED(mFileStatus) && !aCallback.mSecret) {
     907             :     // Let the last-fetched and fetch-count properties be updated.
     908           6 :     mFile->OnFetched();
     909             :   }
     910          11 : }
     911             : 
     912          28 : CacheEntryHandle* CacheEntry::NewHandle()
     913             : {
     914          28 :   return new CacheEntryHandle(this);
     915             : }
     916             : 
     917           2 : CacheEntryHandle* CacheEntry::NewWriteHandle()
     918             : {
     919           4 :   mozilla::MutexAutoLock lock(mLock);
     920             : 
     921             :   // Ignore the OPEN_SECRETLY flag on purpose here, which should actually be
     922             :   // used only along with OPEN_READONLY, but there is no need to enforce that.
     923           2 :   BackgroundOp(Ops::FRECENCYUPDATE);
     924             : 
     925           4 :   return (mWriter = NewHandle());
     926             : }
     927             : 
     928          28 : void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle)
     929             : {
     930          28 :   LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle));
     931             : 
     932          30 :   mozilla::MutexAutoLock lock(mLock);
     933             : 
     934          28 :   if (IsDoomed() && NS_SUCCEEDED(mFileStatus) &&
     935             :       // Note: mHandlesCount is dropped before this method is called
     936           0 :       (mHandlesCount == 0 ||
     937           0 :        (mHandlesCount == 1 && mWriter && mWriter != aHandle))
     938             :       ) {
     939             :     // This entry is no longer referenced from outside and is doomed.
     940             :     // We can do this also when there is just reference from the writer,
     941             :     // no one else could ever reach the written data.
     942             :     // Tell the file to kill the handle, i.e. bypass any I/O operations
     943             :     // on it except removing the file.
     944           0 :     mFile->Kill();
     945             :   }
     946             : 
     947          28 :   if (mWriter != aHandle) {
     948          26 :     LOG(("  not the writer"));
     949          26 :     return;
     950             :   }
     951             : 
     952           2 :   if (mOutputStream) {
     953           0 :     LOG(("  abandoning phantom output stream"));
     954             :     // No one took our internal output stream, so there are no data
     955             :     // and output stream has to be open symultaneously with input stream
     956             :     // on this entry again.
     957           0 :     mHasData = false;
     958             :     // This asynchronously ends up invoking callbacks on this entry
     959             :     // through OnOutputClosed() call.
     960           0 :     mOutputStream->Close();
     961           0 :     mOutputStream = nullptr;
     962             :   } else {
     963             :     // We must always redispatch, otherwise there is a risk of stack
     964             :     // overflow.  This code can recurse deeply.  It won't execute sooner
     965             :     // than we release mLock.
     966           2 :     BackgroundOp(Ops::CALLBACKS, true);
     967             :   }
     968             : 
     969           2 :   mWriter = nullptr;
     970             : 
     971           2 :   if (mState == WRITING) {
     972           0 :     LOG(("  reverting to state EMPTY - write failed"));
     973           0 :     mState = EMPTY;
     974             :   }
     975           2 :   else if (mState == REVALIDATING) {
     976           0 :     LOG(("  reverting to state READY - reval failed"));
     977           0 :     mState = READY;
     978             :   }
     979             : 
     980           2 :   if (mState == READY && !mHasData) {
     981             :     // We may get to this state when following steps happen:
     982             :     // 1. a new entry is given to a consumer
     983             :     // 2. the consumer calls MetaDataReady(), we transit to READY
     984             :     // 3. abandons the entry w/o opening the output stream, mHasData left false
     985             :     //
     986             :     // In this case any following consumer will get a ready entry (with metadata)
     987             :     // but in state like the entry data write was still happening (was in progress)
     988             :     // and will indefinitely wait for the entry data or even the entry itself when
     989             :     // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck.
     990           0 :     LOG(("  we are in READY state, pretend we have data regardless it"
     991             :           " has actully been never touched"));
     992           0 :     mHasData = true;
     993             :   }
     994             : }
     995             : 
     996           2 : void CacheEntry::OnOutputClosed()
     997             : {
     998             :   // Called when the file's output stream is closed.  Invoke any callbacks
     999             :   // waiting for complete entry.
    1000             : 
    1001           4 :   mozilla::MutexAutoLock lock(mLock);
    1002           2 :   InvokeCallbacks();
    1003           2 : }
    1004             : 
    1005           0 : bool CacheEntry::IsReferenced() const
    1006             : {
    1007           0 :   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
    1008             : 
    1009             :   // Increasing this counter from 0 to non-null and this check both happen only
    1010             :   // under the service lock.
    1011           0 :   return mHandlesCount > 0;
    1012             : }
    1013             : 
    1014           0 : bool CacheEntry::IsFileDoomed()
    1015             : {
    1016           0 :   if (NS_SUCCEEDED(mFileStatus)) {
    1017           0 :     return mFile->IsDoomed();
    1018             :   }
    1019             : 
    1020           0 :   return false;
    1021             : }
    1022             : 
    1023           0 : uint32_t CacheEntry::GetMetadataMemoryConsumption()
    1024             : {
    1025           0 :   NS_ENSURE_SUCCESS(mFileStatus, 0);
    1026             : 
    1027             :   uint32_t size;
    1028           0 :   if (NS_FAILED(mFile->ElementsSize(&size)))
    1029           0 :     return 0;
    1030             : 
    1031           0 :   return size;
    1032             : }
    1033             : 
    1034             : // nsICacheEntry
    1035             : 
    1036           3 : NS_IMETHODIMP CacheEntry::GetPersistent(bool *aPersistToDisk)
    1037             : {
    1038             :   // No need to sync when only reading.
    1039             :   // When consumer needs to be consistent with state of the memory storage entries
    1040             :   // table, then let it use GetUseDisk getter that must be called under the service lock.
    1041           3 :   *aPersistToDisk = mUseDisk;
    1042           3 :   return NS_OK;
    1043             : }
    1044             : 
    1045           0 : NS_IMETHODIMP CacheEntry::GetKey(nsACString & aKey)
    1046             : {
    1047           0 :   aKey.Assign(mURI);
    1048           0 :   return NS_OK;
    1049             : }
    1050             : 
    1051           8 : NS_IMETHODIMP CacheEntry::GetFetchCount(int32_t *aFetchCount)
    1052             : {
    1053           8 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1054             : 
    1055           8 :   return mFile->GetFetchCount(reinterpret_cast<uint32_t*>(aFetchCount));
    1056             : }
    1057             : 
    1058           8 : NS_IMETHODIMP CacheEntry::GetLastFetched(uint32_t *aLastFetched)
    1059             : {
    1060           8 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1061             : 
    1062           8 :   return mFile->GetLastFetched(aLastFetched);
    1063             : }
    1064             : 
    1065           4 : NS_IMETHODIMP CacheEntry::GetLastModified(uint32_t *aLastModified)
    1066             : {
    1067           4 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1068             : 
    1069           4 :   return mFile->GetLastModified(aLastModified);
    1070             : }
    1071             : 
    1072           6 : NS_IMETHODIMP CacheEntry::GetExpirationTime(uint32_t *aExpirationTime)
    1073             : {
    1074           6 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1075             : 
    1076           6 :   return mFile->GetExpirationTime(aExpirationTime);
    1077             : }
    1078             : 
    1079           3 : nsresult CacheEntry::GetOnStartTime(uint64_t *aTime)
    1080             : {
    1081           3 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1082           3 :   return mFile->GetOnStartTime(aTime);
    1083             : }
    1084             : 
    1085           2 : nsresult CacheEntry::GetOnStopTime(uint64_t *aTime)
    1086             : {
    1087           2 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1088           2 :   return mFile->GetOnStopTime(aTime);
    1089             : }
    1090             : 
    1091           1 : nsresult CacheEntry::SetNetworkTimes(uint64_t aOnStartTime, uint64_t aOnStopTime)
    1092             : {
    1093           1 :   if (NS_SUCCEEDED(mFileStatus)) {
    1094           1 :     return mFile->SetNetworkTimes(aOnStartTime, aOnStopTime);
    1095             :   }
    1096           0 :   return NS_ERROR_NOT_AVAILABLE;
    1097             : }
    1098             : 
    1099           4 : NS_IMETHODIMP CacheEntry::GetIsForcedValid(bool *aIsForcedValid)
    1100             : {
    1101           4 :   NS_ENSURE_ARG(aIsForcedValid);
    1102             : 
    1103           4 :   MOZ_ASSERT(mState > LOADING);
    1104             : 
    1105           4 :   if (mPinned) {
    1106           0 :     *aIsForcedValid = true;
    1107           0 :     return NS_OK;
    1108             :   }
    1109             : 
    1110           8 :   nsAutoCString key;
    1111           4 :   nsresult rv = HashingKey(key);
    1112           4 :   if (NS_FAILED(rv)) {
    1113           0 :     return rv;
    1114             :   }
    1115             : 
    1116           4 :   *aIsForcedValid = CacheStorageService::Self()->IsForcedValidEntry(mStorageID, key);
    1117           4 :   LOG(("CacheEntry::GetIsForcedValid [this=%p, IsForcedValid=%d]", this, *aIsForcedValid));
    1118             : 
    1119           4 :   return NS_OK;
    1120             : }
    1121             : 
    1122           0 : NS_IMETHODIMP CacheEntry::ForceValidFor(uint32_t aSecondsToTheFuture)
    1123             : {
    1124           0 :   LOG(("CacheEntry::ForceValidFor [this=%p, aSecondsToTheFuture=%d]", this, aSecondsToTheFuture));
    1125             : 
    1126           0 :   nsAutoCString key;
    1127           0 :   nsresult rv = HashingKey(key);
    1128           0 :   if (NS_FAILED(rv)) {
    1129           0 :     return rv;
    1130             :   }
    1131             : 
    1132           0 :   CacheStorageService::Self()->ForceEntryValidFor(mStorageID, key, aSecondsToTheFuture);
    1133             : 
    1134           0 :   return NS_OK;
    1135             : }
    1136             : 
    1137           2 : NS_IMETHODIMP CacheEntry::SetExpirationTime(uint32_t aExpirationTime)
    1138             : {
    1139           2 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1140             : 
    1141           2 :   nsresult rv = mFile->SetExpirationTime(aExpirationTime);
    1142           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1143             : 
    1144             :   // Aligned assignment, thus atomic.
    1145           2 :   mSortingExpirationTime = aExpirationTime;
    1146           2 :   return NS_OK;
    1147             : }
    1148             : 
    1149           4 : NS_IMETHODIMP CacheEntry::OpenInputStream(int64_t offset, nsIInputStream * *_retval)
    1150             : {
    1151           4 :   LOG(("CacheEntry::OpenInputStream [this=%p]", this));
    1152           4 :   return OpenInputStreamInternal(offset, nullptr, _retval);
    1153             : }
    1154             : 
    1155           0 : NS_IMETHODIMP CacheEntry::OpenAlternativeInputStream(const nsACString & type, nsIInputStream * *_retval)
    1156             : {
    1157           0 :   LOG(("CacheEntry::OpenAlternativeInputStream [this=%p, type=%s]", this,
    1158             :        PromiseFlatCString(type).get()));
    1159           0 :   return OpenInputStreamInternal(0, PromiseFlatCString(type).get(), _retval);
    1160             : }
    1161             : 
    1162           4 : nsresult CacheEntry::OpenInputStreamInternal(int64_t offset, const char *aAltDataType, nsIInputStream * *_retval)
    1163             : {
    1164           4 :   LOG(("CacheEntry::OpenInputStreamInternal [this=%p]", this));
    1165             : 
    1166           4 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1167             : 
    1168             :   nsresult rv;
    1169             : 
    1170           8 :   RefPtr<CacheEntryHandle> selfHandle = NewHandle();
    1171             : 
    1172           8 :   nsCOMPtr<nsIInputStream> stream;
    1173           4 :   if (aAltDataType) {
    1174           0 :     rv = mFile->OpenAlternativeInputStream(selfHandle, aAltDataType,
    1175           0 :                                            getter_AddRefs(stream));
    1176           0 :     if (NS_FAILED(rv)) {
    1177             :       // Failure of this method may be legal when the alternative data requested
    1178             :       // is not avaialble or of a different type.  Console error logs are ensured
    1179             :       // by CacheFile::OpenAlternativeInputStream.
    1180           0 :       return rv;
    1181             :     }
    1182             :   } else {
    1183           4 :     rv = mFile->OpenInputStream(selfHandle, getter_AddRefs(stream));
    1184           4 :     NS_ENSURE_SUCCESS(rv, rv);
    1185             :   }
    1186             : 
    1187             :   nsCOMPtr<nsISeekableStream> seekable =
    1188           8 :     do_QueryInterface(stream, &rv);
    1189           4 :   NS_ENSURE_SUCCESS(rv, rv);
    1190             : 
    1191           4 :   rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1192           4 :   NS_ENSURE_SUCCESS(rv, rv);
    1193             : 
    1194           8 :   mozilla::MutexAutoLock lock(mLock);
    1195             : 
    1196           4 :   if (!mHasData) {
    1197             :     // So far output stream on this new entry not opened, do it now.
    1198           1 :     LOG(("  creating phantom output stream"));
    1199           1 :     rv = OpenOutputStreamInternal(0, getter_AddRefs(mOutputStream));
    1200           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1201             :   }
    1202             : 
    1203           4 :   stream.forget(_retval);
    1204           4 :   return NS_OK;
    1205             : }
    1206             : 
    1207           2 : NS_IMETHODIMP CacheEntry::OpenOutputStream(int64_t offset, nsIOutputStream * *_retval)
    1208             : {
    1209           2 :   LOG(("CacheEntry::OpenOutputStream [this=%p]", this));
    1210             : 
    1211             :   nsresult rv;
    1212             : 
    1213           4 :   mozilla::MutexAutoLock lock(mLock);
    1214             : 
    1215           2 :   MOZ_ASSERT(mState > EMPTY);
    1216             : 
    1217           2 :   if (mOutputStream && !mIsDoomed) {
    1218           1 :     LOG(("  giving phantom output stream"));
    1219           1 :     mOutputStream.forget(_retval);
    1220             :   }
    1221             :   else {
    1222           1 :     rv = OpenOutputStreamInternal(offset, _retval);
    1223           1 :     if (NS_FAILED(rv)) return rv;
    1224             :   }
    1225             : 
    1226             :   // Entry considered ready when writer opens output stream.
    1227           2 :   if (mState < READY)
    1228           0 :     mState = READY;
    1229             : 
    1230             :   // Invoke any pending readers now.
    1231           2 :   InvokeCallbacks();
    1232             : 
    1233           2 :   return NS_OK;
    1234             : }
    1235             : 
    1236           0 : NS_IMETHODIMP CacheEntry::OpenAlternativeOutputStream(const nsACString & type, nsIOutputStream * *_retval)
    1237             : {
    1238           0 :   LOG(("CacheEntry::OpenAlternativeOutputStream [this=%p, type=%s]", this,
    1239             :        PromiseFlatCString(type).get()));
    1240             : 
    1241             :   nsresult rv;
    1242             : 
    1243           0 :   mozilla::MutexAutoLock lock(mLock);
    1244             : 
    1245           0 :   if (!mHasData || mState < READY || mOutputStream || mIsDoomed) {
    1246           0 :     LOG(("  entry not in state to write alt-data"));
    1247           0 :     return NS_ERROR_NOT_AVAILABLE;
    1248             :   }
    1249             : 
    1250           0 :   nsCOMPtr<nsIOutputStream> stream;
    1251           0 :   rv = mFile->OpenAlternativeOutputStream(nullptr,
    1252           0 :                                           PromiseFlatCString(type).get(),
    1253           0 :                                           getter_AddRefs(stream));
    1254           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1255             : 
    1256           0 :   stream.swap(*_retval);
    1257           0 :   return NS_OK;
    1258             : }
    1259             : 
    1260           2 : nsresult CacheEntry::OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval)
    1261             : {
    1262           2 :   LOG(("CacheEntry::OpenOutputStreamInternal [this=%p]", this));
    1263             : 
    1264           2 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1265             : 
    1266           2 :   mLock.AssertCurrentThreadOwns();
    1267             : 
    1268           2 :   if (mIsDoomed) {
    1269           0 :     LOG(("  doomed..."));
    1270           0 :     return NS_ERROR_NOT_AVAILABLE;
    1271             :   }
    1272             : 
    1273           2 :   MOZ_ASSERT(mState > LOADING);
    1274             : 
    1275             :   nsresult rv;
    1276             : 
    1277             :   // No need to sync on mUseDisk here, we don't need to be consistent
    1278             :   // with content of the memory storage entries hash table.
    1279           2 :   if (!mUseDisk) {
    1280           0 :     rv = mFile->SetMemoryOnly();
    1281           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1282             :   }
    1283             : 
    1284             :   RefPtr<CacheOutputCloseListener> listener =
    1285           4 :     new CacheOutputCloseListener(this);
    1286             : 
    1287           4 :   nsCOMPtr<nsIOutputStream> stream;
    1288           2 :   rv = mFile->OpenOutputStream(listener, getter_AddRefs(stream));
    1289           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1290             : 
    1291             :   nsCOMPtr<nsISeekableStream> seekable =
    1292           4 :     do_QueryInterface(stream, &rv);
    1293           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1294             : 
    1295           2 :   rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1296           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1297             : 
    1298             :   // Prevent opening output stream again.
    1299           2 :   mHasData = true;
    1300             : 
    1301           2 :   stream.swap(*_retval);
    1302           2 :   return NS_OK;
    1303             : }
    1304             : 
    1305           0 : NS_IMETHODIMP CacheEntry::GetPredictedDataSize(int64_t *aPredictedDataSize)
    1306             : {
    1307           0 :   *aPredictedDataSize = mPredictedDataSize;
    1308           0 :   return NS_OK;
    1309             : }
    1310           5 : NS_IMETHODIMP CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize)
    1311             : {
    1312           5 :   mPredictedDataSize = aPredictedDataSize;
    1313             : 
    1314           5 :   if (!mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) {
    1315           0 :     LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
    1316           0 :     AsyncDoom(nullptr);
    1317             : 
    1318           0 :     return NS_ERROR_FILE_TOO_BIG;
    1319             :   }
    1320             : 
    1321           5 :   return NS_OK;
    1322             : }
    1323             : 
    1324           0 : NS_IMETHODIMP CacheEntry::GetSecurityInfo(nsISupports * *aSecurityInfo)
    1325             : {
    1326             :   {
    1327           0 :     mozilla::MutexAutoLock lock(mLock);
    1328           0 :     if (mSecurityInfoLoaded) {
    1329           0 :       NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
    1330           0 :       return NS_OK;
    1331             :     }
    1332             :   }
    1333             : 
    1334           0 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1335             : 
    1336           0 :   nsXPIDLCString info;
    1337           0 :   nsCOMPtr<nsISupports> secInfo;
    1338             :   nsresult rv;
    1339             : 
    1340           0 :   rv = mFile->GetElement("security-info", getter_Copies(info));
    1341           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1342             : 
    1343           0 :   if (info) {
    1344           0 :     rv = NS_DeserializeObject(info, getter_AddRefs(secInfo));
    1345           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1346             :   }
    1347             : 
    1348             :   {
    1349           0 :     mozilla::MutexAutoLock lock(mLock);
    1350             : 
    1351           0 :     mSecurityInfo.swap(secInfo);
    1352           0 :     mSecurityInfoLoaded = true;
    1353             : 
    1354           0 :     NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
    1355             :   }
    1356             : 
    1357           0 :   return NS_OK;
    1358             : }
    1359           0 : NS_IMETHODIMP CacheEntry::SetSecurityInfo(nsISupports *aSecurityInfo)
    1360             : {
    1361             :   nsresult rv;
    1362             : 
    1363           0 :   NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
    1364             : 
    1365             :   {
    1366           0 :     mozilla::MutexAutoLock lock(mLock);
    1367             : 
    1368           0 :     mSecurityInfo = aSecurityInfo;
    1369           0 :     mSecurityInfoLoaded = true;
    1370             :   }
    1371             : 
    1372             :   nsCOMPtr<nsISerializable> serializable =
    1373           0 :     do_QueryInterface(aSecurityInfo);
    1374           0 :   if (aSecurityInfo && !serializable)
    1375           0 :     return NS_ERROR_UNEXPECTED;
    1376             : 
    1377           0 :   nsCString info;
    1378           0 :   if (serializable) {
    1379           0 :     rv = NS_SerializeToString(serializable, info);
    1380           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1381             :   }
    1382             : 
    1383           0 :   rv = mFile->SetElement("security-info", info.Length() ? info.get() : nullptr);
    1384           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1385             : 
    1386           0 :   return NS_OK;
    1387             : }
    1388             : 
    1389           0 : NS_IMETHODIMP CacheEntry::GetStorageDataSize(uint32_t *aStorageDataSize)
    1390             : {
    1391           0 :   NS_ENSURE_ARG(aStorageDataSize);
    1392             : 
    1393             :   int64_t dataSize;
    1394           0 :   nsresult rv = GetDataSize(&dataSize);
    1395           0 :   if (NS_FAILED(rv))
    1396           0 :     return rv;
    1397             : 
    1398           0 :   *aStorageDataSize = (uint32_t)std::min(int64_t(uint32_t(-1)), dataSize);
    1399             : 
    1400           0 :   return NS_OK;
    1401             : }
    1402             : 
    1403           0 : NS_IMETHODIMP CacheEntry::AsyncDoom(nsICacheEntryDoomCallback *aCallback)
    1404             : {
    1405           0 :   LOG(("CacheEntry::AsyncDoom [this=%p]", this));
    1406             : 
    1407             :   {
    1408           0 :     mozilla::MutexAutoLock lock(mLock);
    1409             : 
    1410           0 :     if (mIsDoomed || mDoomCallback)
    1411           0 :       return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state
    1412             : 
    1413           0 :     RemoveForcedValidity();
    1414             : 
    1415           0 :     mIsDoomed = true;
    1416           0 :     mDoomCallback = aCallback;
    1417             :   }
    1418             : 
    1419             :   // This immediately removes the entry from the master hashtable and also
    1420             :   // immediately dooms the file.  This way we make sure that any consumer
    1421             :   // after this point asking for the same entry won't get
    1422             :   //   a) this entry
    1423             :   //   b) a new entry with the same file
    1424           0 :   PurgeAndDoom();
    1425             : 
    1426           0 :   return NS_OK;
    1427             : }
    1428             : 
    1429          30 : NS_IMETHODIMP CacheEntry::GetMetaDataElement(const char * aKey, char * *aRetval)
    1430             : {
    1431          30 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1432             : 
    1433          30 :   return mFile->GetElement(aKey, aRetval);
    1434             : }
    1435             : 
    1436          17 : NS_IMETHODIMP CacheEntry::SetMetaDataElement(const char * aKey, const char * aValue)
    1437             : {
    1438          17 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1439             : 
    1440          17 :   return mFile->SetElement(aKey, aValue);
    1441             : }
    1442             : 
    1443           1 : NS_IMETHODIMP CacheEntry::VisitMetaData(nsICacheEntryMetaDataVisitor *aVisitor)
    1444             : {
    1445           1 :   NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
    1446             : 
    1447           1 :   return mFile->VisitMetaData(aVisitor);
    1448             : }
    1449             : 
    1450           2 : NS_IMETHODIMP CacheEntry::MetaDataReady()
    1451             : {
    1452           4 :   mozilla::MutexAutoLock lock(mLock);
    1453             : 
    1454           2 :   LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState)));
    1455             : 
    1456           2 :   MOZ_ASSERT(mState > EMPTY);
    1457             : 
    1458           2 :   if (mState == WRITING)
    1459           2 :     mState = READY;
    1460             : 
    1461           2 :   InvokeCallbacks();
    1462             : 
    1463           4 :   return NS_OK;
    1464             : }
    1465             : 
    1466           0 : NS_IMETHODIMP CacheEntry::SetValid()
    1467             : {
    1468           0 :   LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState)));
    1469             : 
    1470           0 :   nsCOMPtr<nsIOutputStream> outputStream;
    1471             : 
    1472             :   {
    1473           0 :     mozilla::MutexAutoLock lock(mLock);
    1474             : 
    1475           0 :     MOZ_ASSERT(mState > EMPTY);
    1476             : 
    1477           0 :     mState = READY;
    1478           0 :     mHasData = true;
    1479             : 
    1480           0 :     InvokeCallbacks();
    1481             : 
    1482           0 :     outputStream.swap(mOutputStream);
    1483             :   }
    1484             : 
    1485           0 :   if (outputStream) {
    1486           0 :     LOG(("  abandoning phantom output stream"));
    1487           0 :     outputStream->Close();
    1488             :   }
    1489             : 
    1490           0 :   return NS_OK;
    1491             : }
    1492             : 
    1493           0 : NS_IMETHODIMP CacheEntry::Recreate(bool aMemoryOnly,
    1494             :                                    nsICacheEntry **_retval)
    1495             : {
    1496           0 :   LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));
    1497             : 
    1498           0 :   mozilla::MutexAutoLock lock(mLock);
    1499             : 
    1500           0 :   RefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
    1501           0 :   if (handle) {
    1502           0 :     handle.forget(_retval);
    1503           0 :     return NS_OK;
    1504             :   }
    1505             : 
    1506           0 :   BackgroundOp(Ops::CALLBACKS, true);
    1507           0 :   return NS_ERROR_NOT_AVAILABLE;
    1508             : }
    1509             : 
    1510           6 : NS_IMETHODIMP CacheEntry::GetDataSize(int64_t *aDataSize)
    1511             : {
    1512           6 :   LOG(("CacheEntry::GetDataSize [this=%p]", this));
    1513           6 :   *aDataSize = 0;
    1514             : 
    1515             :   {
    1516          11 :     mozilla::MutexAutoLock lock(mLock);
    1517             : 
    1518           6 :     if (!mHasData) {
    1519           1 :       LOG(("  write in progress (no data)"));
    1520           1 :       return NS_ERROR_IN_PROGRESS;
    1521             :     }
    1522             :   }
    1523             : 
    1524           5 :   NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
    1525             : 
    1526             :   // mayhemer: TODO Problem with compression?
    1527           5 :   if (!mFile->DataSize(aDataSize)) {
    1528           0 :     LOG(("  write in progress (stream active)"));
    1529           0 :     return NS_ERROR_IN_PROGRESS;
    1530             :   }
    1531             : 
    1532           5 :   LOG(("  size=%" PRId64, *aDataSize));
    1533           5 :   return NS_OK;
    1534             : }
    1535             : 
    1536             : 
    1537           0 : NS_IMETHODIMP CacheEntry::GetAltDataSize(int64_t *aDataSize)
    1538             : {
    1539           0 :   LOG(("CacheEntry::GetAltDataSize [this=%p]", this));
    1540           0 :   if (NS_FAILED(mFileStatus)) {
    1541           0 :     return mFileStatus;
    1542             :   }
    1543           0 :   return mFile->GetAltDataSize(aDataSize);
    1544             : }
    1545             : 
    1546             : 
    1547           0 : NS_IMETHODIMP CacheEntry::MarkValid()
    1548             : {
    1549             :   // NOT IMPLEMENTED ACTUALLY
    1550           0 :   return NS_OK;
    1551             : }
    1552             : 
    1553           4 : NS_IMETHODIMP CacheEntry::MaybeMarkValid()
    1554             : {
    1555             :   // NOT IMPLEMENTED ACTUALLY
    1556           4 :   return NS_OK;
    1557             : }
    1558             : 
    1559           2 : NS_IMETHODIMP CacheEntry::HasWriteAccess(bool aWriteAllowed, bool *aWriteAccess)
    1560             : {
    1561           2 :   *aWriteAccess = aWriteAllowed;
    1562           2 :   return NS_OK;
    1563             : }
    1564             : 
    1565           0 : NS_IMETHODIMP CacheEntry::Close()
    1566             : {
    1567             :   // NOT IMPLEMENTED ACTUALLY
    1568           0 :   return NS_OK;
    1569             : }
    1570             : 
    1571           2 : NS_IMETHODIMP CacheEntry::GetDiskStorageSizeInKB(uint32_t *aDiskStorageSize)
    1572             : {
    1573           2 :   if (NS_FAILED(mFileStatus)) {
    1574           0 :     return NS_ERROR_NOT_AVAILABLE;
    1575             :   }
    1576             : 
    1577           2 :   return mFile->GetDiskStorageSizeInKB(aDiskStorageSize);
    1578             : }
    1579             : 
    1580           2 : NS_IMETHODIMP CacheEntry::GetLoadContextInfo(nsILoadContextInfo** aInfo)
    1581             : {
    1582           4 :   nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(mStorageID);
    1583           2 :   if (!info) {
    1584           0 :     return NS_ERROR_FAILURE;
    1585             :   }
    1586             : 
    1587           2 :   info.forget(aInfo);
    1588             : 
    1589           2 :   return NS_OK;
    1590             : }
    1591             : 
    1592             : // nsIRunnable
    1593             : 
    1594          13 : NS_IMETHODIMP CacheEntry::Run()
    1595             : {
    1596          13 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1597             : 
    1598          26 :   mozilla::MutexAutoLock lock(mLock);
    1599             : 
    1600          13 :   BackgroundOp(mBackgroundOperations.Grab());
    1601          26 :   return NS_OK;
    1602             : }
    1603             : 
    1604             : // Management methods
    1605             : 
    1606           0 : double CacheEntry::GetFrecency() const
    1607             : {
    1608           0 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1609           0 :   return mFrecency;
    1610             : }
    1611             : 
    1612           0 : uint32_t CacheEntry::GetExpirationTime() const
    1613             : {
    1614           0 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1615           0 :   return mSortingExpirationTime;
    1616             : }
    1617             : 
    1618           0 : bool CacheEntry::IsRegistered() const
    1619             : {
    1620           0 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1621           0 :   return mRegistration == REGISTERED;
    1622             : }
    1623             : 
    1624           5 : bool CacheEntry::CanRegister() const
    1625             : {
    1626           5 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1627           5 :   return mRegistration == NEVERREGISTERED;
    1628             : }
    1629             : 
    1630           5 : void CacheEntry::SetRegistered(bool aRegistered)
    1631             : {
    1632           5 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1633             : 
    1634           5 :   if (aRegistered) {
    1635           5 :     MOZ_ASSERT(mRegistration == NEVERREGISTERED);
    1636           5 :     mRegistration = REGISTERED;
    1637             :   }
    1638             :   else {
    1639           0 :     MOZ_ASSERT(mRegistration == REGISTERED);
    1640           0 :     mRegistration = DEREGISTERED;
    1641             :   }
    1642           5 : }
    1643             : 
    1644           0 : bool CacheEntry::DeferOrBypassRemovalOnPinStatus(bool aPinned)
    1645             : {
    1646           0 :   LOG(("CacheEntry::DeferOrBypassRemovalOnPinStatus [this=%p]", this));
    1647             : 
    1648           0 :   mozilla::MutexAutoLock lock(mLock);
    1649             : 
    1650           0 :   if (mPinningKnown) {
    1651           0 :     LOG(("  pinned=%d, caller=%d", mPinned, aPinned));
    1652             :     // Bypass when the pin status of this entry doesn't match the pin status
    1653             :     // caller wants to remove
    1654           0 :     return mPinned != aPinned;
    1655             :   }
    1656             : 
    1657           0 :   LOG(("  pinning unknown, caller=%d", aPinned));
    1658             :   // Oterwise, remember to doom after the status is determined for any
    1659             :   // callback opening the entry after this point...
    1660           0 :   Callback c(this, aPinned);
    1661           0 :   RememberCallback(c);
    1662             :   // ...and always bypass
    1663           0 :   return true;
    1664             : }
    1665             : 
    1666           0 : bool CacheEntry::Purge(uint32_t aWhat)
    1667             : {
    1668           0 :   LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat));
    1669             : 
    1670           0 :   MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1671             : 
    1672           0 :   switch (aWhat) {
    1673             :   case PURGE_DATA_ONLY_DISK_BACKED:
    1674             :   case PURGE_WHOLE_ONLY_DISK_BACKED:
    1675             :     // This is an in-memory only entry, don't purge it
    1676           0 :     if (!mUseDisk) {
    1677           0 :       LOG(("  not using disk"));
    1678           0 :       return false;
    1679             :     }
    1680             :   }
    1681             : 
    1682           0 :   if (mState == WRITING || mState == LOADING || mFrecency == 0) {
    1683             :     // In-progress (write or load) entries should (at least for consistency and from
    1684             :     // the logical point of view) stay in memory.
    1685             :     // Zero-frecency entries are those which have never been given to any consumer, those
    1686             :     // are actually very fresh and should not go just because frecency had not been set
    1687             :     // so far.
    1688           0 :     LOG(("  state=%s, frecency=%1.10f", StateString(mState), mFrecency));
    1689           0 :     return false;
    1690             :   }
    1691             : 
    1692           0 :   if (NS_SUCCEEDED(mFileStatus) && mFile->IsWriteInProgress()) {
    1693             :     // The file is used when there are open streams or chunks/metadata still waiting for
    1694             :     // write.  In this case, this entry cannot be purged, otherwise reopenned entry
    1695             :     // would may not even find the data on disk - CacheFile is not shared and cannot be
    1696             :     // left orphan when its job is not done, hence keep the whole entry.
    1697           0 :     LOG(("  file still under use"));
    1698           0 :     return false;
    1699             :   }
    1700             : 
    1701           0 :   switch (aWhat) {
    1702             :   case PURGE_WHOLE_ONLY_DISK_BACKED:
    1703             :   case PURGE_WHOLE:
    1704             :     {
    1705           0 :       if (!CacheStorageService::Self()->RemoveEntry(this, true)) {
    1706           0 :         LOG(("  not purging, still referenced"));
    1707           0 :         return false;
    1708             :       }
    1709             : 
    1710           0 :       CacheStorageService::Self()->UnregisterEntry(this);
    1711             : 
    1712             :       // Entry removed it self from control arrays, return true
    1713           0 :       return true;
    1714             :     }
    1715             : 
    1716             :   case PURGE_DATA_ONLY_DISK_BACKED:
    1717             :     {
    1718           0 :       NS_ENSURE_SUCCESS(mFileStatus, false);
    1719             : 
    1720           0 :       mFile->ThrowMemoryCachedData();
    1721             : 
    1722             :       // Entry has been left in control arrays, return false (not purged)
    1723           0 :       return false;
    1724             :     }
    1725             :   }
    1726             : 
    1727           0 :   LOG(("  ?"));
    1728           0 :   return false;
    1729             : }
    1730             : 
    1731           0 : void CacheEntry::PurgeAndDoom()
    1732             : {
    1733           0 :   LOG(("CacheEntry::PurgeAndDoom [this=%p]", this));
    1734             : 
    1735           0 :   CacheStorageService::Self()->RemoveEntry(this);
    1736           0 :   DoomAlreadyRemoved();
    1737           0 : }
    1738             : 
    1739           0 : void CacheEntry::DoomAlreadyRemoved()
    1740             : {
    1741           0 :   LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
    1742             : 
    1743           0 :   mozilla::MutexAutoLock lock(mLock);
    1744             : 
    1745           0 :   RemoveForcedValidity();
    1746             : 
    1747           0 :   mIsDoomed = true;
    1748             : 
    1749             :   // Pretend pinning is know.  This entry is now doomed for good, so don't
    1750             :   // bother with defering doom because of unknown pinning state any more.
    1751           0 :   mPinningKnown = true;
    1752             : 
    1753             :   // This schedules dooming of the file, dooming is ensured to happen
    1754             :   // sooner than demand to open the same file made after this point
    1755             :   // so that we don't get this file for any newer opened entry(s).
    1756           0 :   DoomFile();
    1757             : 
    1758             :   // Must force post here since may be indirectly called from
    1759             :   // InvokeCallbacks of this entry and we don't want reentrancy here.
    1760           0 :   BackgroundOp(Ops::CALLBACKS, true);
    1761             :   // Process immediately when on the management thread.
    1762           0 :   BackgroundOp(Ops::UNREGISTER);
    1763           0 : }
    1764             : 
    1765           0 : void CacheEntry::DoomFile()
    1766             : {
    1767           0 :   nsresult rv = NS_ERROR_NOT_AVAILABLE;
    1768             : 
    1769           0 :   if (NS_SUCCEEDED(mFileStatus)) {
    1770           0 :     if (mHandlesCount == 0 ||
    1771           0 :         (mHandlesCount == 1 && mWriter)) {
    1772             :       // We kill the file also when there is just reference from the writer,
    1773             :       // no one else could ever reach the written data.  Obvisouly also
    1774             :       // when there is no reference at all (should we ever end up here
    1775             :       // in that case.)
    1776             :       // Tell the file to kill the handle, i.e. bypass any I/O operations
    1777             :       // on it except removing the file.
    1778           0 :       mFile->Kill();
    1779             :     }
    1780             : 
    1781             :     // Always calls the callback asynchronously.
    1782           0 :     rv = mFile->Doom(mDoomCallback ? this : nullptr);
    1783           0 :     if (NS_SUCCEEDED(rv)) {
    1784           0 :       LOG(("  file doomed"));
    1785           0 :       return;
    1786             :     }
    1787             : 
    1788           0 :     if (NS_ERROR_FILE_NOT_FOUND == rv) {
    1789             :       // File is set to be just memory-only, notify the callbacks
    1790             :       // and pretend dooming has succeeded.  From point of view of
    1791             :       // the entry it actually did - the data is gone and cannot be
    1792             :       // reused.
    1793           0 :       rv = NS_OK;
    1794             :     }
    1795             :   }
    1796             : 
    1797             :   // Always posts to the main thread.
    1798           0 :   OnFileDoomed(rv);
    1799             : }
    1800             : 
    1801           0 : void CacheEntry::RemoveForcedValidity()
    1802             : {
    1803           0 :   mLock.AssertCurrentThreadOwns();
    1804             : 
    1805             :   nsresult rv;
    1806             : 
    1807           0 :   if (mIsDoomed) {
    1808           0 :     return;
    1809             :   }
    1810             : 
    1811           0 :   nsAutoCString entryKey;
    1812           0 :   rv = HashingKey(entryKey);
    1813           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1814           0 :     return;
    1815             :   }
    1816             : 
    1817           0 :   CacheStorageService::Self()->RemoveEntryForceValid(mStorageID, entryKey);
    1818             : }
    1819             : 
    1820          26 : void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync)
    1821             : {
    1822          26 :   mLock.AssertCurrentThreadOwns();
    1823             : 
    1824          26 :   if (!CacheStorageService::IsOnManagementThread() || aForceAsync) {
    1825          13 :     if (mBackgroundOperations.Set(aOperations))
    1826          13 :       CacheStorageService::Self()->Dispatch(this);
    1827             : 
    1828          13 :     LOG(("CacheEntry::BackgroundOp this=%p dipatch of %x", this, aOperations));
    1829          13 :     return;
    1830             :   }
    1831             : 
    1832             :   {
    1833          26 :     mozilla::MutexAutoUnlock unlock(mLock);
    1834             : 
    1835          13 :     MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
    1836             : 
    1837          13 :     if (aOperations & Ops::FRECENCYUPDATE) {
    1838           6 :       ++mUseCount;
    1839             : 
    1840             :       #ifndef M_LN2
    1841             :       #define M_LN2 0.69314718055994530942
    1842             :       #endif
    1843             : 
    1844             :       // Half-life is dynamic, in seconds.
    1845           6 :       static double half_life = CacheObserver::HalfLifeSeconds();
    1846             :       // Must convert from seconds to milliseconds since PR_Now() gives usecs.
    1847           6 :       static double const decay = (M_LN2 / half_life) / static_cast<double>(PR_USEC_PER_SEC);
    1848             : 
    1849           6 :       double now_decay = static_cast<double>(PR_Now()) * decay;
    1850             : 
    1851           6 :       if (mFrecency == 0) {
    1852           2 :         mFrecency = now_decay;
    1853             :       }
    1854             :       else {
    1855             :         // TODO: when C++11 enabled, use std::log1p(n) which is equal to log(n + 1) but
    1856             :         // more precise.
    1857           4 :         mFrecency = log(exp(mFrecency - now_decay) + 1) + now_decay;
    1858             :       }
    1859           6 :       LOG(("CacheEntry FRECENCYUPDATE [this=%p, frecency=%1.10f]", this, mFrecency));
    1860             : 
    1861             :       // Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that
    1862             :       // is not thread-safe) we must post to the main thread...
    1863          12 :       NS_DispatchToMainThread(
    1864          12 :         NewRunnableMethod<double>("net::CacheEntry::StoreFrecency",
    1865             :                                   this,
    1866             :                                   &CacheEntry::StoreFrecency,
    1867           6 :                                   mFrecency));
    1868             :     }
    1869             : 
    1870          13 :     if (aOperations & Ops::REGISTER) {
    1871           5 :       LOG(("CacheEntry REGISTER [this=%p]", this));
    1872             : 
    1873           5 :       CacheStorageService::Self()->RegisterEntry(this);
    1874             :     }
    1875             : 
    1876          13 :     if (aOperations & Ops::UNREGISTER) {
    1877           0 :       LOG(("CacheEntry UNREGISTER [this=%p]", this));
    1878             : 
    1879           0 :       CacheStorageService::Self()->UnregisterEntry(this);
    1880             :     }
    1881             :   } // unlock
    1882             : 
    1883          13 :   if (aOperations & Ops::CALLBACKS) {
    1884           2 :     LOG(("CacheEntry CALLBACKS (invoke) [this=%p]", this));
    1885             : 
    1886           2 :     InvokeCallbacks();
    1887             :   }
    1888             : }
    1889             : 
    1890           6 : void CacheEntry::StoreFrecency(double aFrecency)
    1891             : {
    1892           6 :   MOZ_ASSERT(NS_IsMainThread());
    1893             : 
    1894           6 :   if (NS_SUCCEEDED(mFileStatus)) {
    1895           6 :     mFile->SetFrecency(FRECENCY2INT(aFrecency));
    1896             :   }
    1897           6 : }
    1898             : 
    1899             : // CacheOutputCloseListener
    1900             : 
    1901           2 : CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry* aEntry)
    1902             :   : Runnable("net::CacheOutputCloseListener")
    1903           2 :   , mEntry(aEntry)
    1904             : {
    1905           2 : }
    1906             : 
    1907           4 : CacheOutputCloseListener::~CacheOutputCloseListener()
    1908             : {
    1909           6 : }
    1910             : 
    1911           2 : void CacheOutputCloseListener::OnOutputClosed()
    1912             : {
    1913             :   // We need this class and to redispatch since this callback is invoked
    1914             :   // under the file's lock and to do the job we need to enter the entry's
    1915             :   // lock too.  That would lead to potential deadlocks.
    1916           2 :   NS_DispatchToCurrentThread(this);
    1917           2 : }
    1918             : 
    1919           2 : NS_IMETHODIMP CacheOutputCloseListener::Run()
    1920             : {
    1921           2 :   mEntry->OnOutputClosed();
    1922           2 :   return NS_OK;
    1923             : }
    1924             : 
    1925             : // Memory reporting
    1926             : 
    1927           0 : size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
    1928             : {
    1929           0 :   size_t n = 0;
    1930             : 
    1931           0 :   n += mCallbacks.ShallowSizeOfExcludingThis(mallocSizeOf);
    1932           0 :   if (mFile) {
    1933           0 :     n += mFile->SizeOfIncludingThis(mallocSizeOf);
    1934             :   }
    1935             : 
    1936           0 :   n += mURI.SizeOfExcludingThisIfUnshared(mallocSizeOf);
    1937           0 :   n += mEnhanceID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
    1938           0 :   n += mStorageID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
    1939             : 
    1940             :   // mDoomCallback is an arbitrary class that is probably reported elsewhere.
    1941             :   // mOutputStream is reported in mFile.
    1942             :   // mWriter is one of many handles we create, but (intentionally) not keep
    1943             :   // any reference to, so those unfortunately cannot be reported.  Handles are
    1944             :   // small, though.
    1945             :   // mSecurityInfo doesn't impl nsISizeOf.
    1946             : 
    1947           0 :   return n;
    1948             : }
    1949             : 
    1950           0 : size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
    1951             : {
    1952           0 :   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
    1953             : }
    1954             : 
    1955             : } // namespace net
    1956             : } // namespace mozilla

Generated by: LCOV version 1.13