netwerk/cache2/CacheEntry.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/cache2/CacheEntry.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1584 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "CacheLog.h"
     1.9 +#include "CacheEntry.h"
    1.10 +#include "CacheStorageService.h"
    1.11 +#include "CacheObserver.h"
    1.12 +#include "CacheFileUtils.h"
    1.13 +
    1.14 +#include "nsIInputStream.h"
    1.15 +#include "nsIOutputStream.h"
    1.16 +#include "nsISeekableStream.h"
    1.17 +#include "nsIURI.h"
    1.18 +#include "nsICacheEntryOpenCallback.h"
    1.19 +#include "nsICacheStorage.h"
    1.20 +#include "nsISerializable.h"
    1.21 +#include "nsIStreamTransportService.h"
    1.22 +#include "nsISizeOf.h"
    1.23 +
    1.24 +#include "nsComponentManagerUtils.h"
    1.25 +#include "nsServiceManagerUtils.h"
    1.26 +#include "nsString.h"
    1.27 +#include "nsProxyRelease.h"
    1.28 +#include "nsSerializationHelper.h"
    1.29 +#include "nsThreadUtils.h"
    1.30 +#include "mozilla/Telemetry.h"
    1.31 +#include <math.h>
    1.32 +#include <algorithm>
    1.33 +
    1.34 +namespace mozilla {
    1.35 +namespace net {
    1.36 +
    1.37 +static uint32_t const ENTRY_WANTED =
    1.38 +  nsICacheEntryOpenCallback::ENTRY_WANTED;
    1.39 +static uint32_t const RECHECK_AFTER_WRITE_FINISHED =
    1.40 +  nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED;
    1.41 +static uint32_t const ENTRY_NEEDS_REVALIDATION =
    1.42 +  nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION;
    1.43 +static uint32_t const ENTRY_NOT_WANTED =
    1.44 +  nsICacheEntryOpenCallback::ENTRY_NOT_WANTED;
    1.45 +
    1.46 +NS_IMPL_ISUPPORTS(CacheEntryHandle, nsICacheEntry)
    1.47 +
    1.48 +// CacheEntryHandle
    1.49 +
    1.50 +CacheEntryHandle::CacheEntryHandle(CacheEntry* aEntry)
    1.51 +: mEntry(aEntry)
    1.52 +{
    1.53 +  MOZ_COUNT_CTOR(CacheEntryHandle);
    1.54 +
    1.55 +#ifdef DEBUG
    1.56 +  if (!mEntry->HandlesCount()) {
    1.57 +    // CacheEntry.mHandlesCount must go from zero to one only under
    1.58 +    // the service lock. Can access CacheStorageService::Self() w/o a check
    1.59 +    // since CacheEntry hrefs it.
    1.60 +    CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
    1.61 +  }
    1.62 +#endif
    1.63 +
    1.64 +  mEntry->AddHandleRef();
    1.65 +
    1.66 +  LOG(("New CacheEntryHandle %p for entry %p", this, aEntry));
    1.67 +}
    1.68 +
    1.69 +CacheEntryHandle::~CacheEntryHandle()
    1.70 +{
    1.71 +  mEntry->ReleaseHandleRef();
    1.72 +  mEntry->OnHandleClosed(this);
    1.73 +
    1.74 +  MOZ_COUNT_DTOR(CacheEntryHandle);
    1.75 +}
    1.76 +
    1.77 +// CacheEntry::Callback
    1.78 +
    1.79 +CacheEntry::Callback::Callback(CacheEntry* aEntry,
    1.80 +                               nsICacheEntryOpenCallback *aCallback,
    1.81 +                               bool aReadOnly, bool aCheckOnAnyThread)
    1.82 +: mEntry(aEntry)
    1.83 +, mCallback(aCallback)
    1.84 +, mTargetThread(do_GetCurrentThread())
    1.85 +, mReadOnly(aReadOnly)
    1.86 +, mCheckOnAnyThread(aCheckOnAnyThread)
    1.87 +, mRecheckAfterWrite(false)
    1.88 +, mNotWanted(false)
    1.89 +{
    1.90 +  MOZ_COUNT_CTOR(CacheEntry::Callback);
    1.91 +
    1.92 +  // The counter may go from zero to non-null only under the service lock
    1.93 +  // but here we expect it to be already positive.
    1.94 +  MOZ_ASSERT(mEntry->HandlesCount());
    1.95 +  mEntry->AddHandleRef();
    1.96 +}
    1.97 +
    1.98 +CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
    1.99 +: mEntry(aThat.mEntry)
   1.100 +, mCallback(aThat.mCallback)
   1.101 +, mTargetThread(aThat.mTargetThread)
   1.102 +, mReadOnly(aThat.mReadOnly)
   1.103 +, mCheckOnAnyThread(aThat.mCheckOnAnyThread)
   1.104 +, mRecheckAfterWrite(aThat.mRecheckAfterWrite)
   1.105 +, mNotWanted(aThat.mNotWanted)
   1.106 +{
   1.107 +  MOZ_COUNT_CTOR(CacheEntry::Callback);
   1.108 +
   1.109 +  // The counter may go from zero to non-null only under the service lock
   1.110 +  // but here we expect it to be already positive.
   1.111 +  MOZ_ASSERT(mEntry->HandlesCount());
   1.112 +  mEntry->AddHandleRef();
   1.113 +}
   1.114 +
   1.115 +CacheEntry::Callback::~Callback()
   1.116 +{
   1.117 +  ProxyRelease(mCallback, mTargetThread);
   1.118 +
   1.119 +  mEntry->ReleaseHandleRef();
   1.120 +  MOZ_COUNT_DTOR(CacheEntry::Callback);
   1.121 +}
   1.122 +
   1.123 +void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
   1.124 +{
   1.125 +  if (mEntry == aEntry)
   1.126 +    return;
   1.127 +
   1.128 +  // The counter may go from zero to non-null only under the service lock
   1.129 +  // but here we expect it to be already positive.
   1.130 +  MOZ_ASSERT(aEntry->HandlesCount());
   1.131 +  aEntry->AddHandleRef();
   1.132 +  mEntry->ReleaseHandleRef();
   1.133 +  mEntry = aEntry;
   1.134 +}
   1.135 +
   1.136 +nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
   1.137 +{
   1.138 +  if (!mCheckOnAnyThread) {
   1.139 +    // Check we are on the target
   1.140 +    return mTargetThread->IsOnCurrentThread(aOnCheckThread);
   1.141 +  }
   1.142 +
   1.143 +  // We can invoke check anywhere
   1.144 +  *aOnCheckThread = true;
   1.145 +  return NS_OK;
   1.146 +}
   1.147 +
   1.148 +nsresult CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread) const
   1.149 +{
   1.150 +  return mTargetThread->IsOnCurrentThread(aOnAvailThread);
   1.151 +}
   1.152 +
   1.153 +// CacheEntry
   1.154 +
   1.155 +NS_IMPL_ISUPPORTS(CacheEntry,
   1.156 +                  nsICacheEntry,
   1.157 +                  nsIRunnable,
   1.158 +                  CacheFileListener)
   1.159 +
   1.160 +CacheEntry::CacheEntry(const nsACString& aStorageID,
   1.161 +                       nsIURI* aURI,
   1.162 +                       const nsACString& aEnhanceID,
   1.163 +                       bool aUseDisk)
   1.164 +: mFrecency(0)
   1.165 +, mSortingExpirationTime(uint32_t(-1))
   1.166 +, mLock("CacheEntry")
   1.167 +, mFileStatus(NS_ERROR_NOT_INITIALIZED)
   1.168 +, mURI(aURI)
   1.169 +, mEnhanceID(aEnhanceID)
   1.170 +, mStorageID(aStorageID)
   1.171 +, mUseDisk(aUseDisk)
   1.172 +, mIsDoomed(false)
   1.173 +, mSecurityInfoLoaded(false)
   1.174 +, mPreventCallbacks(false)
   1.175 +, mHasData(false)
   1.176 +, mState(NOTLOADED)
   1.177 +, mRegistration(NEVERREGISTERED)
   1.178 +, mWriter(nullptr)
   1.179 +, mPredictedDataSize(0)
   1.180 +, mReleaseThread(NS_GetCurrentThread())
   1.181 +{
   1.182 +  MOZ_COUNT_CTOR(CacheEntry);
   1.183 +
   1.184 +  mService = CacheStorageService::Self();
   1.185 +
   1.186 +  CacheStorageService::Self()->RecordMemoryOnlyEntry(
   1.187 +    this, !aUseDisk, true /* overwrite */);
   1.188 +}
   1.189 +
   1.190 +CacheEntry::~CacheEntry()
   1.191 +{
   1.192 +  ProxyRelease(mURI, mReleaseThread);
   1.193 +
   1.194 +  LOG(("CacheEntry::~CacheEntry [this=%p]", this));
   1.195 +  MOZ_COUNT_DTOR(CacheEntry);
   1.196 +}
   1.197 +
   1.198 +#ifdef PR_LOG
   1.199 +
   1.200 +char const * CacheEntry::StateString(uint32_t aState)
   1.201 +{
   1.202 +  switch (aState) {
   1.203 +  case NOTLOADED:     return "NOTLOADED";
   1.204 +  case LOADING:       return "LOADING";
   1.205 +  case EMPTY:         return "EMPTY";
   1.206 +  case WRITING:       return "WRITING";
   1.207 +  case READY:         return "READY";
   1.208 +  case REVALIDATING:  return "REVALIDATING";
   1.209 +  }
   1.210 +
   1.211 +  return "?";
   1.212 +}
   1.213 +
   1.214 +#endif
   1.215 +
   1.216 +nsresult CacheEntry::HashingKeyWithStorage(nsACString &aResult)
   1.217 +{
   1.218 +  return HashingKey(mStorageID, mEnhanceID, mURI, aResult);
   1.219 +}
   1.220 +
   1.221 +nsresult CacheEntry::HashingKey(nsACString &aResult)
   1.222 +{
   1.223 +  return HashingKey(EmptyCString(), mEnhanceID, mURI, aResult);
   1.224 +}
   1.225 +
   1.226 +// static
   1.227 +nsresult CacheEntry::HashingKey(nsCSubstring const& aStorageID,
   1.228 +                                nsCSubstring const& aEnhanceID,
   1.229 +                                nsIURI* aURI,
   1.230 +                                nsACString &aResult)
   1.231 +{
   1.232 +  nsAutoCString spec;
   1.233 +  nsresult rv = aURI->GetAsciiSpec(spec);
   1.234 +  NS_ENSURE_SUCCESS(rv, rv);
   1.235 +
   1.236 +  return HashingKey(aStorageID, aEnhanceID, spec, aResult);
   1.237 +}
   1.238 +
   1.239 +// static
   1.240 +nsresult CacheEntry::HashingKey(nsCSubstring const& aStorageID,
   1.241 +                                nsCSubstring const& aEnhanceID,
   1.242 +                                nsCSubstring const& aURISpec,
   1.243 +                                nsACString &aResult)
   1.244 +{
   1.245 +  /**
   1.246 +   * This key is used to salt hash that is a base for disk file name.
   1.247 +   * Changing it will cause we will not be able to find files on disk.
   1.248 +   */
   1.249 +
   1.250 +  aResult.Append(aStorageID);
   1.251 +
   1.252 +  if (!aEnhanceID.IsEmpty()) {
   1.253 +    CacheFileUtils::AppendTagWithValue(aResult, '~', aEnhanceID);
   1.254 +  }
   1.255 +
   1.256 +  // Appending directly
   1.257 +  aResult.Append(':');
   1.258 +  aResult.Append(aURISpec);
   1.259 +
   1.260 +  return NS_OK;
   1.261 +}
   1.262 +
   1.263 +void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
   1.264 +{
   1.265 +  LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
   1.266 +    this, StateString(mState), aFlags, aCallback));
   1.267 +
   1.268 +  bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
   1.269 +  bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
   1.270 +  bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
   1.271 +  bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
   1.272 +
   1.273 +  MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
   1.274 +  MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
   1.275 +
   1.276 +  Callback callback(this, aCallback, readonly, multithread);
   1.277 +
   1.278 +  mozilla::MutexAutoLock lock(mLock);
   1.279 +
   1.280 +  RememberCallback(callback);
   1.281 +
   1.282 +  // Load() opens the lock
   1.283 +  if (Load(truncate, priority)) {
   1.284 +    // Loading is in progress...
   1.285 +    return;
   1.286 +  }
   1.287 +
   1.288 +  InvokeCallbacks();
   1.289 +}
   1.290 +
   1.291 +bool CacheEntry::Load(bool aTruncate, bool aPriority)
   1.292 +{
   1.293 +  LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate));
   1.294 +
   1.295 +  mLock.AssertCurrentThreadOwns();
   1.296 +
   1.297 +  if (mState > LOADING) {
   1.298 +    LOG(("  already loaded"));
   1.299 +    return false;
   1.300 +  }
   1.301 +
   1.302 +  if (mState == LOADING) {
   1.303 +    LOG(("  already loading"));
   1.304 +    return true;
   1.305 +  }
   1.306 +
   1.307 +  mState = LOADING;
   1.308 +
   1.309 +  MOZ_ASSERT(!mFile);
   1.310 +
   1.311 +  bool directLoad = aTruncate || !mUseDisk;
   1.312 +  if (directLoad)
   1.313 +    mFileStatus = NS_OK;
   1.314 +  else
   1.315 +    mLoadStart = TimeStamp::Now();
   1.316 +
   1.317 +  mFile = new CacheFile();
   1.318 +
   1.319 +  BackgroundOp(Ops::REGISTER);
   1.320 +
   1.321 +  {
   1.322 +    mozilla::MutexAutoUnlock unlock(mLock);
   1.323 +
   1.324 +    nsresult rv;
   1.325 +
   1.326 +    nsAutoCString fileKey;
   1.327 +    rv = HashingKeyWithStorage(fileKey);
   1.328 +
   1.329 +    LOG(("  performing load, file=%p", mFile.get()));
   1.330 +    if (NS_SUCCEEDED(rv)) {
   1.331 +      rv = mFile->Init(fileKey,
   1.332 +                       aTruncate,
   1.333 +                       !mUseDisk,
   1.334 +                       aPriority,
   1.335 +                       directLoad ? nullptr : this);
   1.336 +    }
   1.337 +
   1.338 +    if (NS_FAILED(rv)) {
   1.339 +      mFileStatus = rv;
   1.340 +      AsyncDoom(nullptr);
   1.341 +      return false;
   1.342 +    }
   1.343 +  }
   1.344 +
   1.345 +  if (directLoad) {
   1.346 +    // Just fake the load has already been done as "new".
   1.347 +    mState = EMPTY;
   1.348 +  }
   1.349 +
   1.350 +  return mState == LOADING;
   1.351 +}
   1.352 +
   1.353 +NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
   1.354 +{
   1.355 +  LOG(("CacheEntry::OnFileReady [this=%p, rv=0x%08x, new=%d]",
   1.356 +      this, aResult, aIsNew));
   1.357 +
   1.358 +  MOZ_ASSERT(!mLoadStart.IsNull());
   1.359 +
   1.360 +  if (NS_SUCCEEDED(aResult)) {
   1.361 +    if (aIsNew) {
   1.362 +      mozilla::Telemetry::AccumulateTimeDelta(
   1.363 +        mozilla::Telemetry::NETWORK_CACHE_V2_MISS_TIME_MS,
   1.364 +        mLoadStart);
   1.365 +    }
   1.366 +    else {
   1.367 +      mozilla::Telemetry::AccumulateTimeDelta(
   1.368 +        mozilla::Telemetry::NETWORK_CACHE_V2_HIT_TIME_MS,
   1.369 +        mLoadStart);
   1.370 +    }
   1.371 +  }
   1.372 +
   1.373 +  // OnFileReady, that is the only code that can transit from LOADING
   1.374 +  // to any follow-on state, can only be invoked ones on an entry,
   1.375 +  // thus no need to lock.  Until this moment there is no consumer that
   1.376 +  // could manipulate the entry state.
   1.377 +  mozilla::MutexAutoLock lock(mLock);
   1.378 +
   1.379 +  MOZ_ASSERT(mState == LOADING);
   1.380 +
   1.381 +  mState = (aIsNew || NS_FAILED(aResult))
   1.382 +    ? EMPTY
   1.383 +    : READY;
   1.384 +
   1.385 +  mFileStatus = aResult;
   1.386 +
   1.387 +  if (mState == READY) {
   1.388 +    mHasData = true;
   1.389 +
   1.390 +    uint32_t frecency;
   1.391 +    mFile->GetFrecency(&frecency);
   1.392 +    // mFrecency is held in a double to increase computance precision.
   1.393 +    // It is ok to persist frecency only as a uint32 with some math involved.
   1.394 +    mFrecency = INT2FRECENCY(frecency);
   1.395 +  }
   1.396 +
   1.397 +  InvokeCallbacks();
   1.398 +  return NS_OK;
   1.399 +}
   1.400 +
   1.401 +NS_IMETHODIMP CacheEntry::OnFileDoomed(nsresult aResult)
   1.402 +{
   1.403 +  if (mDoomCallback) {
   1.404 +    nsRefPtr<DoomCallbackRunnable> event =
   1.405 +      new DoomCallbackRunnable(this, aResult);
   1.406 +    NS_DispatchToMainThread(event);
   1.407 +  }
   1.408 +
   1.409 +  return NS_OK;
   1.410 +}
   1.411 +
   1.412 +already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
   1.413 +                                                               nsICacheEntryOpenCallback* aCallback)
   1.414 +{
   1.415 +  LOG(("CacheEntry::ReopenTruncated [this=%p]", this));
   1.416 +
   1.417 +  mLock.AssertCurrentThreadOwns();
   1.418 +
   1.419 +  // Hold callbacks invocation, AddStorageEntry would invoke from doom prematurly
   1.420 +  mPreventCallbacks = true;
   1.421 +
   1.422 +  nsRefPtr<CacheEntryHandle> handle;
   1.423 +  nsRefPtr<CacheEntry> newEntry;
   1.424 +  {
   1.425 +    mozilla::MutexAutoUnlock unlock(mLock);
   1.426 +
   1.427 +    // The following call dooms this entry (calls DoomAlreadyRemoved on us)
   1.428 +    nsresult rv = CacheStorageService::Self()->AddStorageEntry(
   1.429 +      GetStorageID(), GetURI(), GetEnhanceID(),
   1.430 +      mUseDisk && !aMemoryOnly,
   1.431 +      true, // always create
   1.432 +      true, // truncate existing (this one)
   1.433 +      getter_AddRefs(handle));
   1.434 +
   1.435 +    if (NS_SUCCEEDED(rv)) {
   1.436 +      newEntry = handle->Entry();
   1.437 +      LOG(("  exchanged entry %p by entry %p, rv=0x%08x", this, newEntry.get(), rv));
   1.438 +      newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE);
   1.439 +    } else {
   1.440 +      LOG(("  exchanged of entry %p failed, rv=0x%08x", this, rv));
   1.441 +      AsyncDoom(nullptr);
   1.442 +    }
   1.443 +  }
   1.444 +
   1.445 +  mPreventCallbacks = false;
   1.446 +
   1.447 +  if (!newEntry)
   1.448 +    return nullptr;
   1.449 +
   1.450 +  newEntry->TransferCallbacks(*this);
   1.451 +  mCallbacks.Clear();
   1.452 +
   1.453 +  return handle.forget();
   1.454 +}
   1.455 +
   1.456 +void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
   1.457 +{
   1.458 +  mozilla::MutexAutoLock lock(mLock);
   1.459 +
   1.460 +  LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
   1.461 +    this, &aFromEntry));
   1.462 +
   1.463 +  if (!mCallbacks.Length())
   1.464 +    mCallbacks.SwapElements(aFromEntry.mCallbacks);
   1.465 +  else
   1.466 +    mCallbacks.AppendElements(aFromEntry.mCallbacks);
   1.467 +
   1.468 +  uint32_t callbacksLength = mCallbacks.Length();
   1.469 +  if (callbacksLength) {
   1.470 +    // Carry the entry reference (unfortunatelly, needs to be done manually...)
   1.471 +    for (uint32_t i = 0; i < callbacksLength; ++i)
   1.472 +      mCallbacks[i].ExchangeEntry(this);
   1.473 +
   1.474 +    BackgroundOp(Ops::CALLBACKS, true);
   1.475 +  }
   1.476 +}
   1.477 +
   1.478 +void CacheEntry::RememberCallback(Callback const& aCallback)
   1.479 +{
   1.480 +  LOG(("CacheEntry::RememberCallback [this=%p, cb=%p]", this, aCallback.mCallback.get()));
   1.481 +
   1.482 +  mLock.AssertCurrentThreadOwns();
   1.483 +
   1.484 +  mCallbacks.AppendElement(aCallback);
   1.485 +}
   1.486 +
   1.487 +void CacheEntry::InvokeCallbacksLock()
   1.488 +{
   1.489 +  mozilla::MutexAutoLock lock(mLock);
   1.490 +  InvokeCallbacks();
   1.491 +}
   1.492 +
   1.493 +void CacheEntry::InvokeCallbacks()
   1.494 +{
   1.495 +  mLock.AssertCurrentThreadOwns();
   1.496 +
   1.497 +  LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
   1.498 +
   1.499 +  // Invoke first all r/w callbacks, then all r/o callbacks.
   1.500 +  if (InvokeCallbacks(false))
   1.501 +    InvokeCallbacks(true);
   1.502 +
   1.503 +  LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
   1.504 +}
   1.505 +
   1.506 +bool CacheEntry::InvokeCallbacks(bool aReadOnly)
   1.507 +{
   1.508 +  mLock.AssertCurrentThreadOwns();
   1.509 +
   1.510 +  uint32_t i = 0;
   1.511 +  while (i < mCallbacks.Length()) {
   1.512 +    if (mPreventCallbacks) {
   1.513 +      LOG(("  callbacks prevented!"));
   1.514 +      return false;
   1.515 +    }
   1.516 +
   1.517 +    if (!mIsDoomed && (mState == WRITING || mState == REVALIDATING)) {
   1.518 +      LOG(("  entry is being written/revalidated"));
   1.519 +      return false;
   1.520 +    }
   1.521 +
   1.522 +    if (mCallbacks[i].mReadOnly != aReadOnly) {
   1.523 +      // Callback is not r/w or r/o, go to another one in line
   1.524 +      ++i;
   1.525 +      continue;
   1.526 +    }
   1.527 +
   1.528 +    bool onCheckThread;
   1.529 +    nsresult rv = mCallbacks[i].OnCheckThread(&onCheckThread);
   1.530 +
   1.531 +    if (NS_SUCCEEDED(rv) && !onCheckThread) {
   1.532 +      // Redispatch to the target thread
   1.533 +      nsRefPtr<nsRunnableMethod<CacheEntry> > event =
   1.534 +        NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock);
   1.535 +
   1.536 +      rv = mCallbacks[i].mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   1.537 +      if (NS_SUCCEEDED(rv)) {
   1.538 +        LOG(("  re-dispatching to target thread"));
   1.539 +        return false;
   1.540 +      }
   1.541 +    }
   1.542 +
   1.543 +    Callback callback = mCallbacks[i];
   1.544 +    mCallbacks.RemoveElementAt(i);
   1.545 +
   1.546 +    if (NS_SUCCEEDED(rv) && !InvokeCallback(callback)) {
   1.547 +      // Callback didn't fire, put it back and go to another one in line.
   1.548 +      // Only reason InvokeCallback returns false is that onCacheEntryCheck
   1.549 +      // returns RECHECK_AFTER_WRITE_FINISHED.  If we would stop the loop, other
   1.550 +      // readers or potential writers would be unnecessarily kept from being
   1.551 +      // invoked.
   1.552 +      mCallbacks.InsertElementAt(i, callback);
   1.553 +      ++i;
   1.554 +    }
   1.555 +  }
   1.556 +
   1.557 +  return true;
   1.558 +}
   1.559 +
   1.560 +bool CacheEntry::InvokeCallback(Callback & aCallback)
   1.561 +{
   1.562 +  LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
   1.563 +    this, StateString(mState), aCallback.mCallback.get()));
   1.564 +
   1.565 +  mLock.AssertCurrentThreadOwns();
   1.566 +
   1.567 +  // When this entry is doomed we want to notify the callback any time
   1.568 +  if (!mIsDoomed) {
   1.569 +    // When we are here, the entry must be loaded from disk
   1.570 +    MOZ_ASSERT(mState > LOADING);
   1.571 +
   1.572 +    if (mState == WRITING || mState == REVALIDATING) {
   1.573 +      // Prevent invoking other callbacks since one of them is now writing
   1.574 +      // or revalidating this entry.  No consumers should get this entry
   1.575 +      // until metadata are filled with values downloaded from the server
   1.576 +      // or the entry revalidated and output stream has been opened.
   1.577 +      LOG(("  entry is being written/revalidated, callback bypassed"));
   1.578 +      return false;
   1.579 +    }
   1.580 +
   1.581 +    // mRecheckAfterWrite flag already set means the callback has already passed
   1.582 +    // the onCacheEntryCheck call. Until the current write is not finished this
   1.583 +    // callback will be bypassed.
   1.584 +    if (!aCallback.mRecheckAfterWrite) {
   1.585 +
   1.586 +      if (!aCallback.mReadOnly) {
   1.587 +        if (mState == EMPTY) {
   1.588 +          // Advance to writing state, we expect to invoke the callback and let
   1.589 +          // it fill content of this entry.  Must set and check the state here
   1.590 +          // to prevent more then one
   1.591 +          mState = WRITING;
   1.592 +          LOG(("  advancing to WRITING state"));
   1.593 +        }
   1.594 +
   1.595 +        if (!aCallback.mCallback) {
   1.596 +          // We can be given no callback only in case of recreate, it is ok
   1.597 +          // to advance to WRITING state since the caller of recreate is expected
   1.598 +          // to write this entry now.
   1.599 +          return true;
   1.600 +        }
   1.601 +      }
   1.602 +
   1.603 +      if (mState == READY) {
   1.604 +        // Metadata present, validate the entry
   1.605 +        uint32_t checkResult;
   1.606 +        {
   1.607 +          // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
   1.608 +          mozilla::MutexAutoUnlock unlock(mLock);
   1.609 +
   1.610 +          nsresult rv = aCallback.mCallback->OnCacheEntryCheck(
   1.611 +            this, nullptr, &checkResult);
   1.612 +          LOG(("  OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));
   1.613 +
   1.614 +          if (NS_FAILED(rv))
   1.615 +            checkResult = ENTRY_NOT_WANTED;
   1.616 +        }
   1.617 +
   1.618 +        switch (checkResult) {
   1.619 +        case ENTRY_WANTED:
   1.620 +          // Nothing more to do here, the consumer is responsible to handle
   1.621 +          // the result of OnCacheEntryCheck it self.
   1.622 +          // Proceed to callback...
   1.623 +          break;
   1.624 +
   1.625 +        case RECHECK_AFTER_WRITE_FINISHED:
   1.626 +          LOG(("  consumer will check on the entry again after write is done"));
   1.627 +          // The consumer wants the entry to complete first.
   1.628 +          aCallback.mRecheckAfterWrite = true;
   1.629 +          break;
   1.630 +
   1.631 +        case ENTRY_NEEDS_REVALIDATION:
   1.632 +          LOG(("  will be holding callbacks until entry is revalidated"));
   1.633 +          // State is READY now and from that state entry cannot transit to any other
   1.634 +          // state then REVALIDATING for which cocurrency is not an issue.  Potentially
   1.635 +          // no need to lock here.
   1.636 +          mState = REVALIDATING;
   1.637 +          break;
   1.638 +
   1.639 +        case ENTRY_NOT_WANTED:
   1.640 +          LOG(("  consumer not interested in the entry"));
   1.641 +          // Do not give this entry to the consumer, it is not interested in us.
   1.642 +          aCallback.mNotWanted = true;
   1.643 +          break;
   1.644 +        }
   1.645 +      }
   1.646 +    }
   1.647 +  }
   1.648 +
   1.649 +  if (aCallback.mCallback) {
   1.650 +    if (!mIsDoomed && aCallback.mRecheckAfterWrite) {
   1.651 +      // If we don't have data and the callback wants a complete entry,
   1.652 +      // don't invoke now.
   1.653 +      bool bypass = !mHasData;
   1.654 +      if (!bypass) {
   1.655 +        int64_t _unused;
   1.656 +        bypass = !mFile->DataSize(&_unused);
   1.657 +      }
   1.658 +
   1.659 +      if (bypass) {
   1.660 +        LOG(("  bypassing, entry data still being written"));
   1.661 +        return false;
   1.662 +      }
   1.663 +
   1.664 +      // Entry is complete now, do the check+avail call again
   1.665 +      aCallback.mRecheckAfterWrite = false;
   1.666 +      return InvokeCallback(aCallback);
   1.667 +    }
   1.668 +
   1.669 +    mozilla::MutexAutoUnlock unlock(mLock);
   1.670 +    InvokeAvailableCallback(aCallback);
   1.671 +  }
   1.672 +
   1.673 +  return true;
   1.674 +}
   1.675 +
   1.676 +void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
   1.677 +{
   1.678 +  LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
   1.679 +    this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));
   1.680 +
   1.681 +  nsresult rv;
   1.682 +
   1.683 +  uint32_t const state = mState;
   1.684 +
   1.685 +  // When we are here, the entry must be loaded from disk
   1.686 +  MOZ_ASSERT(state > LOADING || mIsDoomed);
   1.687 +
   1.688 +  bool onAvailThread;
   1.689 +  rv = aCallback.OnAvailThread(&onAvailThread);
   1.690 +  if (NS_FAILED(rv)) {
   1.691 +    LOG(("  target thread dead?"));
   1.692 +    return;
   1.693 +  }
   1.694 +
   1.695 +  if (!onAvailThread) {
   1.696 +    // Dispatch to the right thread
   1.697 +    nsRefPtr<AvailableCallbackRunnable> event =
   1.698 +      new AvailableCallbackRunnable(this, aCallback);
   1.699 +
   1.700 +    rv = aCallback.mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   1.701 +    LOG(("  redispatched, (rv = 0x%08x)", rv));
   1.702 +    return;
   1.703 +  }
   1.704 +
   1.705 +  if (mIsDoomed || aCallback.mNotWanted) {
   1.706 +    LOG(("  doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
   1.707 +    aCallback.mCallback->OnCacheEntryAvailable(
   1.708 +      nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
   1.709 +    return;
   1.710 +  }
   1.711 +
   1.712 +  if (state == READY) {
   1.713 +    LOG(("  ready/has-meta, notifying OCEA with entry and NS_OK"));
   1.714 +    {
   1.715 +      mozilla::MutexAutoLock lock(mLock);
   1.716 +      BackgroundOp(Ops::FRECENCYUPDATE);
   1.717 +    }
   1.718 +
   1.719 +    nsRefPtr<CacheEntryHandle> handle = NewHandle();
   1.720 +    aCallback.mCallback->OnCacheEntryAvailable(
   1.721 +      handle, false, nullptr, NS_OK);
   1.722 +    return;
   1.723 +  }
   1.724 +
   1.725 +  if (aCallback.mReadOnly) {
   1.726 +    LOG(("  r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
   1.727 +    aCallback.mCallback->OnCacheEntryAvailable(
   1.728 +      nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
   1.729 +    return;
   1.730 +  }
   1.731 +
   1.732 +  // This is a new or potentially non-valid entry and needs to be fetched first.
   1.733 +  // The CacheEntryHandle blocks other consumers until the channel
   1.734 +  // either releases the entry or marks metadata as filled or whole entry valid,
   1.735 +  // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.
   1.736 +
   1.737 +  // Consumer will be responsible to fill or validate the entry metadata and data.
   1.738 +
   1.739 +  nsRefPtr<CacheEntryHandle> handle = NewWriteHandle();
   1.740 +  rv = aCallback.mCallback->OnCacheEntryAvailable(
   1.741 +    handle, state == WRITING, nullptr, NS_OK);
   1.742 +
   1.743 +  if (NS_FAILED(rv)) {
   1.744 +    LOG(("  writing/revalidating failed (0x%08x)", rv));
   1.745 +
   1.746 +    // Consumer given a new entry failed to take care of the entry.
   1.747 +    OnHandleClosed(handle);
   1.748 +    return;
   1.749 +  }
   1.750 +
   1.751 +  LOG(("  writing/revalidating"));
   1.752 +}
   1.753 +
   1.754 +CacheEntryHandle* CacheEntry::NewHandle()
   1.755 +{
   1.756 +  return new CacheEntryHandle(this);
   1.757 +}
   1.758 +
   1.759 +CacheEntryHandle* CacheEntry::NewWriteHandle()
   1.760 +{
   1.761 +  mozilla::MutexAutoLock lock(mLock);
   1.762 +
   1.763 +  BackgroundOp(Ops::FRECENCYUPDATE);
   1.764 +  return (mWriter = new CacheEntryHandle(this));
   1.765 +}
   1.766 +
   1.767 +void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle)
   1.768 +{
   1.769 +  LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle));
   1.770 +
   1.771 +  nsCOMPtr<nsIOutputStream> outputStream;
   1.772 +
   1.773 +  {
   1.774 +    mozilla::MutexAutoLock lock(mLock);
   1.775 +
   1.776 +    if (mWriter != aHandle) {
   1.777 +      LOG(("  not the writer"));
   1.778 +      return;
   1.779 +    }
   1.780 +
   1.781 +    if (mOutputStream) {
   1.782 +      // No one took our internal output stream, so there are no data
   1.783 +      // and output stream has to be open symultaneously with input stream
   1.784 +      // on this entry again.
   1.785 +      mHasData = false;
   1.786 +    }
   1.787 +
   1.788 +    outputStream.swap(mOutputStream);
   1.789 +    mWriter = nullptr;
   1.790 +
   1.791 +    if (mState == WRITING) {
   1.792 +      LOG(("  reverting to state EMPTY - write failed"));
   1.793 +      mState = EMPTY;
   1.794 +    }
   1.795 +    else if (mState == REVALIDATING) {
   1.796 +      LOG(("  reverting to state READY - reval failed"));
   1.797 +      mState = READY;
   1.798 +    }
   1.799 +
   1.800 +    InvokeCallbacks();
   1.801 +  }
   1.802 +
   1.803 +  if (outputStream) {
   1.804 +    LOG(("  abandoning phantom output stream"));
   1.805 +    outputStream->Close();
   1.806 +  }
   1.807 +}
   1.808 +
   1.809 +void CacheEntry::OnOutputClosed()
   1.810 +{
   1.811 +  // Called when the file's output stream is closed.  Invoke any callbacks
   1.812 +  // waiting for complete entry.
   1.813 +
   1.814 +  mozilla::MutexAutoLock lock(mLock);
   1.815 +  InvokeCallbacks();
   1.816 +}
   1.817 +
   1.818 +bool CacheEntry::IsUsingDiskLocked() const
   1.819 +{
   1.820 +  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
   1.821 +
   1.822 +  return IsUsingDisk();
   1.823 +}
   1.824 +
   1.825 +bool CacheEntry::SetUsingDisk(bool aUsingDisk)
   1.826 +{
   1.827 +  // Called by the service when this entry is reopen to reflect
   1.828 +  // demanded storage target.
   1.829 +
   1.830 +  if (mState >= READY) {
   1.831 +    // Don't modify after this entry has been filled.
   1.832 +    return false;
   1.833 +  }
   1.834 +
   1.835 +  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
   1.836 +
   1.837 +  bool changed = mUseDisk != aUsingDisk;
   1.838 +  mUseDisk = aUsingDisk;
   1.839 +  return changed;
   1.840 +}
   1.841 +
   1.842 +bool CacheEntry::IsReferenced() const
   1.843 +{
   1.844 +  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
   1.845 +
   1.846 +  // Increasing this counter from 0 to non-null and this check both happen only
   1.847 +  // under the service lock.
   1.848 +  return mHandlesCount > 0;
   1.849 +}
   1.850 +
   1.851 +bool CacheEntry::IsFileDoomed()
   1.852 +{
   1.853 +  mozilla::MutexAutoLock lock(mLock);
   1.854 +
   1.855 +  if (NS_SUCCEEDED(mFileStatus)) {
   1.856 +    return mFile->IsDoomed();
   1.857 +  }
   1.858 +
   1.859 +  return false;
   1.860 +}
   1.861 +
   1.862 +uint32_t CacheEntry::GetMetadataMemoryConsumption()
   1.863 +{
   1.864 +  NS_ENSURE_SUCCESS(mFileStatus, 0);
   1.865 +
   1.866 +  uint32_t size;
   1.867 +  if (NS_FAILED(mFile->ElementsSize(&size)))
   1.868 +    return 0;
   1.869 +
   1.870 +  return size;
   1.871 +}
   1.872 +
   1.873 +// nsICacheEntry
   1.874 +
   1.875 +NS_IMETHODIMP CacheEntry::GetPersistent(bool *aPersistToDisk)
   1.876 +{
   1.877 +  // No need to sync when only reading.
   1.878 +  // When consumer needs to be consistent with state of the memory storage entries
   1.879 +  // table, then let it use GetUseDisk getter that must be called under the service lock.
   1.880 +  *aPersistToDisk = mUseDisk;
   1.881 +  return NS_OK;
   1.882 +}
   1.883 +
   1.884 +NS_IMETHODIMP CacheEntry::GetKey(nsACString & aKey)
   1.885 +{
   1.886 +  return mURI->GetAsciiSpec(aKey);
   1.887 +}
   1.888 +
   1.889 +NS_IMETHODIMP CacheEntry::GetFetchCount(int32_t *aFetchCount)
   1.890 +{
   1.891 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.892 +
   1.893 +  return mFile->GetFetchCount(reinterpret_cast<uint32_t*>(aFetchCount));
   1.894 +}
   1.895 +
   1.896 +NS_IMETHODIMP CacheEntry::GetLastFetched(uint32_t *aLastFetched)
   1.897 +{
   1.898 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.899 +
   1.900 +  return mFile->GetLastFetched(aLastFetched);
   1.901 +}
   1.902 +
   1.903 +NS_IMETHODIMP CacheEntry::GetLastModified(uint32_t *aLastModified)
   1.904 +{
   1.905 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.906 +
   1.907 +  return mFile->GetLastModified(aLastModified);
   1.908 +}
   1.909 +
   1.910 +NS_IMETHODIMP CacheEntry::GetExpirationTime(uint32_t *aExpirationTime)
   1.911 +{
   1.912 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.913 +
   1.914 +  return mFile->GetExpirationTime(aExpirationTime);
   1.915 +}
   1.916 +
   1.917 +NS_IMETHODIMP CacheEntry::SetExpirationTime(uint32_t aExpirationTime)
   1.918 +{
   1.919 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.920 +
   1.921 +  nsresult rv = mFile->SetExpirationTime(aExpirationTime);
   1.922 +  NS_ENSURE_SUCCESS(rv, rv);
   1.923 +
   1.924 +  // Aligned assignment, thus atomic.
   1.925 +  mSortingExpirationTime = aExpirationTime;
   1.926 +  return NS_OK;
   1.927 +}
   1.928 +
   1.929 +NS_IMETHODIMP CacheEntry::OpenInputStream(int64_t offset, nsIInputStream * *_retval)
   1.930 +{
   1.931 +  LOG(("CacheEntry::OpenInputStream [this=%p]", this));
   1.932 +
   1.933 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.934 +
   1.935 +  nsresult rv;
   1.936 +
   1.937 +  nsCOMPtr<nsIInputStream> stream;
   1.938 +  rv = mFile->OpenInputStream(getter_AddRefs(stream));
   1.939 +  NS_ENSURE_SUCCESS(rv, rv);
   1.940 +
   1.941 +  nsCOMPtr<nsISeekableStream> seekable =
   1.942 +    do_QueryInterface(stream, &rv);
   1.943 +  NS_ENSURE_SUCCESS(rv, rv);
   1.944 +
   1.945 +  rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
   1.946 +  NS_ENSURE_SUCCESS(rv, rv);
   1.947 +
   1.948 +  mozilla::MutexAutoLock lock(mLock);
   1.949 +
   1.950 +  if (!mHasData) {
   1.951 +    // So far output stream on this new entry not opened, do it now.
   1.952 +    LOG(("  creating phantom output stream"));
   1.953 +    rv = OpenOutputStreamInternal(0, getter_AddRefs(mOutputStream));
   1.954 +    NS_ENSURE_SUCCESS(rv, rv);
   1.955 +  }
   1.956 +
   1.957 +  stream.forget(_retval);
   1.958 +  return NS_OK;
   1.959 +}
   1.960 +
   1.961 +NS_IMETHODIMP CacheEntry::OpenOutputStream(int64_t offset, nsIOutputStream * *_retval)
   1.962 +{
   1.963 +  LOG(("CacheEntry::OpenOutputStream [this=%p]", this));
   1.964 +
   1.965 +  nsresult rv;
   1.966 +
   1.967 +  mozilla::MutexAutoLock lock(mLock);
   1.968 +
   1.969 +  MOZ_ASSERT(mState > EMPTY);
   1.970 +
   1.971 +  if (mOutputStream && !mIsDoomed) {
   1.972 +    LOG(("  giving phantom output stream"));
   1.973 +    mOutputStream.forget(_retval);
   1.974 +  }
   1.975 +  else {
   1.976 +    rv = OpenOutputStreamInternal(offset, _retval);
   1.977 +    if (NS_FAILED(rv)) return rv;
   1.978 +  }
   1.979 +
   1.980 +  // Entry considered ready when writer opens output stream.
   1.981 +  if (mState < READY)
   1.982 +    mState = READY;
   1.983 +
   1.984 +  // Invoke any pending readers now.
   1.985 +  InvokeCallbacks();
   1.986 +
   1.987 +  return NS_OK;
   1.988 +}
   1.989 +
   1.990 +nsresult CacheEntry::OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval)
   1.991 +{
   1.992 +  LOG(("CacheEntry::OpenOutputStreamInternal [this=%p]", this));
   1.993 +
   1.994 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
   1.995 +
   1.996 +  mLock.AssertCurrentThreadOwns();
   1.997 +
   1.998 +  if (mIsDoomed) {
   1.999 +    LOG(("  doomed..."));
  1.1000 +    return NS_ERROR_NOT_AVAILABLE;
  1.1001 +  }
  1.1002 +
  1.1003 +  MOZ_ASSERT(mState > LOADING);
  1.1004 +
  1.1005 +  nsresult rv;
  1.1006 +
  1.1007 +  // No need to sync on mUseDisk here, we don't need to be consistent
  1.1008 +  // with content of the memory storage entries hash table.
  1.1009 +  if (!mUseDisk) {
  1.1010 +    rv = mFile->SetMemoryOnly();
  1.1011 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1012 +  }
  1.1013 +
  1.1014 +  nsRefPtr<CacheOutputCloseListener> listener =
  1.1015 +    new CacheOutputCloseListener(this);
  1.1016 +
  1.1017 +  nsCOMPtr<nsIOutputStream> stream;
  1.1018 +  rv = mFile->OpenOutputStream(listener, getter_AddRefs(stream));
  1.1019 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1020 +
  1.1021 +  nsCOMPtr<nsISeekableStream> seekable =
  1.1022 +    do_QueryInterface(stream, &rv);
  1.1023 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1024 +
  1.1025 +  rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
  1.1026 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1027 +
  1.1028 +  // Prevent opening output stream again.
  1.1029 +  mHasData = true;
  1.1030 +
  1.1031 +  stream.swap(*_retval);
  1.1032 +  return NS_OK;
  1.1033 +}
  1.1034 +
  1.1035 +NS_IMETHODIMP CacheEntry::GetPredictedDataSize(int64_t *aPredictedDataSize)
  1.1036 +{
  1.1037 +  *aPredictedDataSize = mPredictedDataSize;
  1.1038 +  return NS_OK;
  1.1039 +}
  1.1040 +NS_IMETHODIMP CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize)
  1.1041 +{
  1.1042 +  mPredictedDataSize = aPredictedDataSize;
  1.1043 +
  1.1044 +  if (CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) {
  1.1045 +    LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
  1.1046 +    AsyncDoom(nullptr);
  1.1047 +
  1.1048 +    return NS_ERROR_FILE_TOO_BIG;
  1.1049 +  }
  1.1050 +
  1.1051 +  return NS_OK;
  1.1052 +}
  1.1053 +
  1.1054 +NS_IMETHODIMP CacheEntry::GetSecurityInfo(nsISupports * *aSecurityInfo)
  1.1055 +{
  1.1056 +  {
  1.1057 +    mozilla::MutexAutoLock lock(mLock);
  1.1058 +    if (mSecurityInfoLoaded) {
  1.1059 +      NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
  1.1060 +      return NS_OK;
  1.1061 +    }
  1.1062 +  }
  1.1063 +
  1.1064 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
  1.1065 +
  1.1066 +  nsXPIDLCString info;
  1.1067 +  nsCOMPtr<nsISupports> secInfo;
  1.1068 +  nsresult rv;
  1.1069 +
  1.1070 +  rv = mFile->GetElement("security-info", getter_Copies(info));
  1.1071 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1072 +
  1.1073 +  if (info) {
  1.1074 +    rv = NS_DeserializeObject(info, getter_AddRefs(secInfo));
  1.1075 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1076 +  }
  1.1077 +
  1.1078 +  {
  1.1079 +    mozilla::MutexAutoLock lock(mLock);
  1.1080 +
  1.1081 +    mSecurityInfo.swap(secInfo);
  1.1082 +    mSecurityInfoLoaded = true;
  1.1083 +
  1.1084 +    NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
  1.1085 +  }
  1.1086 +
  1.1087 +  return NS_OK;
  1.1088 +}
  1.1089 +NS_IMETHODIMP CacheEntry::SetSecurityInfo(nsISupports *aSecurityInfo)
  1.1090 +{
  1.1091 +  nsresult rv;
  1.1092 +
  1.1093 +  NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
  1.1094 +
  1.1095 +  {
  1.1096 +    mozilla::MutexAutoLock lock(mLock);
  1.1097 +
  1.1098 +    mSecurityInfo = aSecurityInfo;
  1.1099 +    mSecurityInfoLoaded = true;
  1.1100 +  }
  1.1101 +
  1.1102 +  nsCOMPtr<nsISerializable> serializable =
  1.1103 +    do_QueryInterface(aSecurityInfo);
  1.1104 +  if (aSecurityInfo && !serializable)
  1.1105 +    return NS_ERROR_UNEXPECTED;
  1.1106 +
  1.1107 +  nsCString info;
  1.1108 +  if (serializable) {
  1.1109 +    rv = NS_SerializeToString(serializable, info);
  1.1110 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1111 +  }
  1.1112 +
  1.1113 +  rv = mFile->SetElement("security-info", info.Length() ? info.get() : nullptr);
  1.1114 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1115 +
  1.1116 +  return NS_OK;
  1.1117 +}
  1.1118 +
  1.1119 +NS_IMETHODIMP CacheEntry::GetStorageDataSize(uint32_t *aStorageDataSize)
  1.1120 +{
  1.1121 +  NS_ENSURE_ARG(aStorageDataSize);
  1.1122 +
  1.1123 +  int64_t dataSize;
  1.1124 +  nsresult rv = GetDataSize(&dataSize);
  1.1125 +  if (NS_FAILED(rv))
  1.1126 +    return rv;
  1.1127 +
  1.1128 +  *aStorageDataSize = (uint32_t)std::min(int64_t(uint32_t(-1)), dataSize);
  1.1129 +
  1.1130 +  return NS_OK;
  1.1131 +}
  1.1132 +
  1.1133 +NS_IMETHODIMP CacheEntry::AsyncDoom(nsICacheEntryDoomCallback *aCallback)
  1.1134 +{
  1.1135 +  LOG(("CacheEntry::AsyncDoom [this=%p]", this));
  1.1136 +
  1.1137 +  {
  1.1138 +    mozilla::MutexAutoLock lock(mLock);
  1.1139 +
  1.1140 +    if (mIsDoomed || mDoomCallback)
  1.1141 +      return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state
  1.1142 +
  1.1143 +    mIsDoomed = true;
  1.1144 +    mDoomCallback = aCallback;
  1.1145 +  }
  1.1146 +
  1.1147 +  // This immediately removes the entry from the master hashtable and also
  1.1148 +  // immediately dooms the file.  This way we make sure that any consumer
  1.1149 +  // after this point asking for the same entry won't get
  1.1150 +  //   a) this entry
  1.1151 +  //   b) a new entry with the same file
  1.1152 +  PurgeAndDoom();
  1.1153 +
  1.1154 +  return NS_OK;
  1.1155 +}
  1.1156 +
  1.1157 +NS_IMETHODIMP CacheEntry::GetMetaDataElement(const char * aKey, char * *aRetval)
  1.1158 +{
  1.1159 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
  1.1160 +
  1.1161 +  return mFile->GetElement(aKey, aRetval);
  1.1162 +}
  1.1163 +
  1.1164 +NS_IMETHODIMP CacheEntry::SetMetaDataElement(const char * aKey, const char * aValue)
  1.1165 +{
  1.1166 +  NS_ENSURE_SUCCESS(mFileStatus, NS_ERROR_NOT_AVAILABLE);
  1.1167 +
  1.1168 +  return mFile->SetElement(aKey, aValue);
  1.1169 +}
  1.1170 +
  1.1171 +NS_IMETHODIMP CacheEntry::MetaDataReady()
  1.1172 +{
  1.1173 +  mozilla::MutexAutoLock lock(mLock);
  1.1174 +
  1.1175 +  LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState)));
  1.1176 +
  1.1177 +  MOZ_ASSERT(mState > EMPTY);
  1.1178 +
  1.1179 +  if (mState == WRITING)
  1.1180 +    mState = READY;
  1.1181 +
  1.1182 +  InvokeCallbacks();
  1.1183 +
  1.1184 +  return NS_OK;
  1.1185 +}
  1.1186 +
  1.1187 +NS_IMETHODIMP CacheEntry::SetValid()
  1.1188 +{
  1.1189 +  LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState)));
  1.1190 +
  1.1191 +  nsCOMPtr<nsIOutputStream> outputStream;
  1.1192 +
  1.1193 +  {
  1.1194 +    mozilla::MutexAutoLock lock(mLock);
  1.1195 +
  1.1196 +    MOZ_ASSERT(mState > EMPTY);
  1.1197 +
  1.1198 +    mState = READY;
  1.1199 +    mHasData = true;
  1.1200 +
  1.1201 +    InvokeCallbacks();
  1.1202 +
  1.1203 +    outputStream.swap(mOutputStream);
  1.1204 +  }
  1.1205 +
  1.1206 +  if (outputStream) {
  1.1207 +    LOG(("  abandoning phantom output stream"));
  1.1208 +    outputStream->Close();
  1.1209 +  }
  1.1210 +
  1.1211 +  return NS_OK;
  1.1212 +}
  1.1213 +
  1.1214 +NS_IMETHODIMP CacheEntry::Recreate(bool aMemoryOnly,
  1.1215 +                                   nsICacheEntry **_retval)
  1.1216 +{
  1.1217 +  LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));
  1.1218 +
  1.1219 +  mozilla::MutexAutoLock lock(mLock);
  1.1220 +
  1.1221 +  nsRefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
  1.1222 +  if (handle) {
  1.1223 +    handle.forget(_retval);
  1.1224 +    return NS_OK;
  1.1225 +  }
  1.1226 +
  1.1227 +  BackgroundOp(Ops::CALLBACKS, true);
  1.1228 +  return NS_OK;
  1.1229 +}
  1.1230 +
  1.1231 +NS_IMETHODIMP CacheEntry::GetDataSize(int64_t *aDataSize)
  1.1232 +{
  1.1233 +  LOG(("CacheEntry::GetDataSize [this=%p]", this));
  1.1234 +  *aDataSize = 0;
  1.1235 +
  1.1236 +  {
  1.1237 +    mozilla::MutexAutoLock lock(mLock);
  1.1238 +
  1.1239 +    if (!mHasData) {
  1.1240 +      LOG(("  write in progress (no data)"));
  1.1241 +      return NS_ERROR_IN_PROGRESS;
  1.1242 +    }
  1.1243 +  }
  1.1244 +
  1.1245 +  NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
  1.1246 +
  1.1247 +  // mayhemer: TODO Problem with compression?
  1.1248 +  if (!mFile->DataSize(aDataSize)) {
  1.1249 +    LOG(("  write in progress (stream active)"));
  1.1250 +    return NS_ERROR_IN_PROGRESS;
  1.1251 +  }
  1.1252 +
  1.1253 +  LOG(("  size=%lld", *aDataSize));
  1.1254 +  return NS_OK;
  1.1255 +}
  1.1256 +
  1.1257 +NS_IMETHODIMP CacheEntry::MarkValid()
  1.1258 +{
  1.1259 +  // NOT IMPLEMENTED ACTUALLY
  1.1260 +  return NS_OK;
  1.1261 +}
  1.1262 +
  1.1263 +NS_IMETHODIMP CacheEntry::MaybeMarkValid()
  1.1264 +{
  1.1265 +  // NOT IMPLEMENTED ACTUALLY
  1.1266 +  return NS_OK;
  1.1267 +}
  1.1268 +
  1.1269 +NS_IMETHODIMP CacheEntry::HasWriteAccess(bool aWriteAllowed, bool *aWriteAccess)
  1.1270 +{
  1.1271 +  *aWriteAccess = aWriteAllowed;
  1.1272 +  return NS_OK;
  1.1273 +}
  1.1274 +
  1.1275 +NS_IMETHODIMP CacheEntry::Close()
  1.1276 +{
  1.1277 +  // NOT IMPLEMENTED ACTUALLY
  1.1278 +  return NS_OK;
  1.1279 +}
  1.1280 +
  1.1281 +// nsIRunnable
  1.1282 +
  1.1283 +NS_IMETHODIMP CacheEntry::Run()
  1.1284 +{
  1.1285 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1286 +
  1.1287 +  mozilla::MutexAutoLock lock(mLock);
  1.1288 +
  1.1289 +  BackgroundOp(mBackgroundOperations.Grab());
  1.1290 +  return NS_OK;
  1.1291 +}
  1.1292 +
  1.1293 +// Management methods
  1.1294 +
  1.1295 +double CacheEntry::GetFrecency() const
  1.1296 +{
  1.1297 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1298 +  return mFrecency;
  1.1299 +}
  1.1300 +
  1.1301 +uint32_t CacheEntry::GetExpirationTime() const
  1.1302 +{
  1.1303 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1304 +  return mSortingExpirationTime;
  1.1305 +}
  1.1306 +
  1.1307 +bool CacheEntry::IsRegistered() const
  1.1308 +{
  1.1309 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1310 +  return mRegistration == REGISTERED;
  1.1311 +}
  1.1312 +
  1.1313 +bool CacheEntry::CanRegister() const
  1.1314 +{
  1.1315 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1316 +  return mRegistration == NEVERREGISTERED;
  1.1317 +}
  1.1318 +
  1.1319 +void CacheEntry::SetRegistered(bool aRegistered)
  1.1320 +{
  1.1321 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1322 +
  1.1323 +  if (aRegistered) {
  1.1324 +    MOZ_ASSERT(mRegistration == NEVERREGISTERED);
  1.1325 +    mRegistration = REGISTERED;
  1.1326 +  }
  1.1327 +  else {
  1.1328 +    MOZ_ASSERT(mRegistration == REGISTERED);
  1.1329 +    mRegistration = DEREGISTERED;
  1.1330 +  }
  1.1331 +}
  1.1332 +
  1.1333 +bool CacheEntry::Purge(uint32_t aWhat)
  1.1334 +{
  1.1335 +  LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat));
  1.1336 +
  1.1337 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1338 +
  1.1339 +  switch (aWhat) {
  1.1340 +  case PURGE_DATA_ONLY_DISK_BACKED:
  1.1341 +  case PURGE_WHOLE_ONLY_DISK_BACKED:
  1.1342 +    // This is an in-memory only entry, don't purge it
  1.1343 +    if (!mUseDisk) {
  1.1344 +      LOG(("  not using disk"));
  1.1345 +      return false;
  1.1346 +    }
  1.1347 +  }
  1.1348 +
  1.1349 +  if (mState == WRITING || mState == LOADING || mFrecency == 0) {
  1.1350 +    // In-progress (write or load) entries should (at least for consistency and from
  1.1351 +    // the logical point of view) stay in memory.
  1.1352 +    // Zero-frecency entries are those which have never been given to any consumer, those
  1.1353 +    // are actually very fresh and should not go just because frecency had not been set
  1.1354 +    // so far.
  1.1355 +    LOG(("  state=%s, frecency=%1.10f", StateString(mState), mFrecency));
  1.1356 +    return false;
  1.1357 +  }
  1.1358 +
  1.1359 +  if (NS_SUCCEEDED(mFileStatus) && mFile->IsWriteInProgress()) {
  1.1360 +    // The file is used when there are open streams or chunks/metadata still waiting for
  1.1361 +    // write.  In this case, this entry cannot be purged, otherwise reopenned entry
  1.1362 +    // would may not even find the data on disk - CacheFile is not shared and cannot be
  1.1363 +    // left orphan when its job is not done, hence keep the whole entry.
  1.1364 +    LOG(("  file still under use"));
  1.1365 +    return false;
  1.1366 +  }
  1.1367 +
  1.1368 +  switch (aWhat) {
  1.1369 +  case PURGE_WHOLE_ONLY_DISK_BACKED:
  1.1370 +  case PURGE_WHOLE:
  1.1371 +    {
  1.1372 +      if (!CacheStorageService::Self()->RemoveEntry(this, true)) {
  1.1373 +        LOG(("  not purging, still referenced"));
  1.1374 +        return false;
  1.1375 +      }
  1.1376 +
  1.1377 +      CacheStorageService::Self()->UnregisterEntry(this);
  1.1378 +
  1.1379 +      // Entry removed it self from control arrays, return true
  1.1380 +      return true;
  1.1381 +    }
  1.1382 +
  1.1383 +  case PURGE_DATA_ONLY_DISK_BACKED:
  1.1384 +    {
  1.1385 +      NS_ENSURE_SUCCESS(mFileStatus, false);
  1.1386 +
  1.1387 +      mFile->ThrowMemoryCachedData();
  1.1388 +
  1.1389 +      // Entry has been left in control arrays, return false (not purged)
  1.1390 +      return false;
  1.1391 +    }
  1.1392 +  }
  1.1393 +
  1.1394 +  LOG(("  ?"));
  1.1395 +  return false;
  1.1396 +}
  1.1397 +
  1.1398 +void CacheEntry::PurgeAndDoom()
  1.1399 +{
  1.1400 +  LOG(("CacheEntry::PurgeAndDoom [this=%p]", this));
  1.1401 +
  1.1402 +  CacheStorageService::Self()->RemoveEntry(this);
  1.1403 +  DoomAlreadyRemoved();
  1.1404 +}
  1.1405 +
  1.1406 +void CacheEntry::DoomAlreadyRemoved()
  1.1407 +{
  1.1408 +  LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
  1.1409 +
  1.1410 +  mozilla::MutexAutoLock lock(mLock);
  1.1411 +
  1.1412 +  mIsDoomed = true;
  1.1413 +
  1.1414 +  // This schedules dooming of the file, dooming is ensured to happen
  1.1415 +  // sooner than demand to open the same file made after this point
  1.1416 +  // so that we don't get this file for any newer opened entry(s).
  1.1417 +  DoomFile();
  1.1418 +
  1.1419 +  // Must force post here since may be indirectly called from
  1.1420 +  // InvokeCallbacks of this entry and we don't want reentrancy here.
  1.1421 +  BackgroundOp(Ops::CALLBACKS, true);
  1.1422 +  // Process immediately when on the management thread.
  1.1423 +  BackgroundOp(Ops::UNREGISTER);
  1.1424 +}
  1.1425 +
  1.1426 +void CacheEntry::DoomFile()
  1.1427 +{
  1.1428 +  nsresult rv = NS_ERROR_NOT_AVAILABLE;
  1.1429 +
  1.1430 +  if (NS_SUCCEEDED(mFileStatus)) {
  1.1431 +    // Always calls the callback asynchronously.
  1.1432 +    rv = mFile->Doom(mDoomCallback ? this : nullptr);
  1.1433 +    if (NS_SUCCEEDED(rv)) {
  1.1434 +      LOG(("  file doomed"));
  1.1435 +      return;
  1.1436 +    }
  1.1437 +    
  1.1438 +    if (NS_ERROR_FILE_NOT_FOUND == rv) {
  1.1439 +      // File is set to be just memory-only, notify the callbacks
  1.1440 +      // and pretend dooming has succeeded.  From point of view of
  1.1441 +      // the entry it actually did - the data is gone and cannot be
  1.1442 +      // reused.
  1.1443 +      rv = NS_OK;
  1.1444 +    }
  1.1445 +  }
  1.1446 +
  1.1447 +  // Always posts to the main thread.
  1.1448 +  OnFileDoomed(rv);
  1.1449 +}
  1.1450 +
  1.1451 +void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync)
  1.1452 +{
  1.1453 +  mLock.AssertCurrentThreadOwns();
  1.1454 +
  1.1455 +  if (!CacheStorageService::IsOnManagementThread() || aForceAsync) {
  1.1456 +    if (mBackgroundOperations.Set(aOperations))
  1.1457 +      CacheStorageService::Self()->Dispatch(this);
  1.1458 +
  1.1459 +    LOG(("CacheEntry::BackgroundOp this=%p dipatch of %x", this, aOperations));
  1.1460 +    return;
  1.1461 +  }
  1.1462 +
  1.1463 +  mozilla::MutexAutoUnlock unlock(mLock);
  1.1464 +
  1.1465 +  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
  1.1466 +
  1.1467 +  if (aOperations & Ops::FRECENCYUPDATE) {
  1.1468 +    #ifndef M_LN2
  1.1469 +    #define M_LN2 0.69314718055994530942
  1.1470 +    #endif
  1.1471 +
  1.1472 +    // Half-life is dynamic, in seconds.
  1.1473 +     static double half_life = CacheObserver::HalfLifeSeconds();
  1.1474 +    // Must convert from seconds to milliseconds since PR_Now() gives usecs.
  1.1475 +    static double const decay = (M_LN2 / half_life) / static_cast<double>(PR_USEC_PER_SEC);
  1.1476 +
  1.1477 +    double now_decay = static_cast<double>(PR_Now()) * decay;
  1.1478 +
  1.1479 +    if (mFrecency == 0) {
  1.1480 +      mFrecency = now_decay;
  1.1481 +    }
  1.1482 +    else {
  1.1483 +      // TODO: when C++11 enabled, use std::log1p(n) which is equal to log(n + 1) but
  1.1484 +      // more precise.
  1.1485 +      mFrecency = log(exp(mFrecency - now_decay) + 1) + now_decay;
  1.1486 +    }
  1.1487 +    LOG(("CacheEntry FRECENCYUPDATE [this=%p, frecency=%1.10f]", this, mFrecency));
  1.1488 +
  1.1489 +    // Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that
  1.1490 +    // is not thread-safe) we must post to the main thread...
  1.1491 +    nsRefPtr<nsRunnableMethod<CacheEntry> > event =
  1.1492 +      NS_NewRunnableMethod(this, &CacheEntry::StoreFrecency);
  1.1493 +    NS_DispatchToMainThread(event);
  1.1494 +  }
  1.1495 +
  1.1496 +  if (aOperations & Ops::REGISTER) {
  1.1497 +    LOG(("CacheEntry REGISTER [this=%p]", this));
  1.1498 +
  1.1499 +    CacheStorageService::Self()->RegisterEntry(this);
  1.1500 +  }
  1.1501 +
  1.1502 +  if (aOperations & Ops::UNREGISTER) {
  1.1503 +    LOG(("CacheEntry UNREGISTER [this=%p]", this));
  1.1504 +
  1.1505 +    CacheStorageService::Self()->UnregisterEntry(this);
  1.1506 +  }
  1.1507 +
  1.1508 +  if (aOperations & Ops::CALLBACKS) {
  1.1509 +    LOG(("CacheEntry CALLBACKS (invoke) [this=%p]", this));
  1.1510 +
  1.1511 +    mozilla::MutexAutoLock lock(mLock);
  1.1512 +    InvokeCallbacks();
  1.1513 +  }
  1.1514 +}
  1.1515 +
  1.1516 +void CacheEntry::StoreFrecency()
  1.1517 +{
  1.1518 +  // No need for thread safety over mFrecency, it will be rewriten
  1.1519 +  // correctly on following invocation if broken by concurrency.
  1.1520 +  MOZ_ASSERT(NS_IsMainThread());
  1.1521 +  mFile->SetFrecency(FRECENCY2INT(mFrecency));
  1.1522 +}
  1.1523 +
  1.1524 +// CacheOutputCloseListener
  1.1525 +
  1.1526 +CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry* aEntry)
  1.1527 +: mEntry(aEntry)
  1.1528 +{
  1.1529 +  MOZ_COUNT_CTOR(CacheOutputCloseListener);
  1.1530 +}
  1.1531 +
  1.1532 +CacheOutputCloseListener::~CacheOutputCloseListener()
  1.1533 +{
  1.1534 +  MOZ_COUNT_DTOR(CacheOutputCloseListener);
  1.1535 +}
  1.1536 +
  1.1537 +void CacheOutputCloseListener::OnOutputClosed()
  1.1538 +{
  1.1539 +  // We need this class and to redispatch since this callback is invoked
  1.1540 +  // under the file's lock and to do the job we need to enter the entry's
  1.1541 +  // lock too.  That would lead to potential deadlocks.
  1.1542 +  NS_DispatchToCurrentThread(this);
  1.1543 +}
  1.1544 +
  1.1545 +NS_IMETHODIMP CacheOutputCloseListener::Run()
  1.1546 +{
  1.1547 +  mEntry->OnOutputClosed();
  1.1548 +  return NS_OK;
  1.1549 +}
  1.1550 +
  1.1551 +// Memory reporting
  1.1552 +
  1.1553 +size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1.1554 +{
  1.1555 +  size_t n = 0;
  1.1556 +  nsCOMPtr<nsISizeOf> sizeOf;
  1.1557 +
  1.1558 +  n += mCallbacks.SizeOfExcludingThis(mallocSizeOf);
  1.1559 +  if (mFile) {
  1.1560 +    n += mFile->SizeOfIncludingThis(mallocSizeOf);
  1.1561 +  }
  1.1562 +
  1.1563 +  sizeOf = do_QueryInterface(mURI);
  1.1564 +  if (sizeOf) {
  1.1565 +    n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
  1.1566 +  }
  1.1567 +
  1.1568 +  n += mEnhanceID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1.1569 +  n += mStorageID.SizeOfExcludingThisIfUnshared(mallocSizeOf);
  1.1570 +
  1.1571 +  // mDoomCallback is an arbitrary class that is probably reported elsewhere.
  1.1572 +  // mOutputStream is reported in mFile.
  1.1573 +  // mWriter is one of many handles we create, but (intentionally) not keep
  1.1574 +  // any reference to, so those unfortunatelly cannot be reported.  Handles are
  1.1575 +  // small, though.
  1.1576 +  // mSecurityInfo doesn't impl nsISizeOf.
  1.1577 +
  1.1578 +  return n;
  1.1579 +}
  1.1580 +
  1.1581 +size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  1.1582 +{
  1.1583 +  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
  1.1584 +}
  1.1585 +
  1.1586 +} // net
  1.1587 +} // mozilla

mercurial