netwerk/cache/nsCacheEntryDescriptor.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsICache.h"
     8 #include "nsCache.h"
     9 #include "nsCacheService.h"
    10 #include "nsCacheEntryDescriptor.h"
    11 #include "nsCacheEntry.h"
    12 #include "nsReadableUtils.h"
    13 #include "nsIOutputStream.h"
    14 #include "nsCRT.h"
    15 #include "nsThreadUtils.h"
    16 #include <algorithm>
    18 #define kMinDecompressReadBufLen 1024
    19 #define kMinCompressWriteBufLen  1024
    22 /******************************************************************************
    23  * nsAsyncDoomEvent
    24  *****************************************************************************/
    26 class nsAsyncDoomEvent : public nsRunnable {
    27 public:
    28     nsAsyncDoomEvent(nsCacheEntryDescriptor *descriptor,
    29                      nsICacheListener *listener)
    30     {
    31         mDescriptor = descriptor;
    32         mListener = listener;
    33         mThread = do_GetCurrentThread();
    34         // We addref the listener here and release it in nsNotifyDoomListener
    35         // on the callers thread. If posting of nsNotifyDoomListener event fails
    36         // we leak the listener which is better than releasing it on a wrong
    37         // thread.
    38         NS_IF_ADDREF(mListener);
    39     }
    41     NS_IMETHOD Run()
    42     {
    43         nsresult status = NS_OK;
    45         {
    46             nsCacheServiceAutoLock lock(LOCK_TELEM(NSASYNCDOOMEVENT_RUN));
    48             if (mDescriptor->mCacheEntry) {
    49                 status = nsCacheService::gService->DoomEntry_Internal(
    50                              mDescriptor->mCacheEntry, true);
    51             } else if (!mDescriptor->mDoomedOnClose) {
    52                 status = NS_ERROR_NOT_AVAILABLE;
    53             }
    54         }
    56         if (mListener) {
    57             mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
    58                               NS_DISPATCH_NORMAL);
    59             // posted event will release the reference on the correct thread
    60             mListener = nullptr;
    61         }
    63         return NS_OK;
    64     }
    66 private:
    67     nsRefPtr<nsCacheEntryDescriptor> mDescriptor;
    68     nsICacheListener                *mListener;
    69     nsCOMPtr<nsIThread>              mThread;
    70 };
    73 NS_IMPL_ISUPPORTS(nsCacheEntryDescriptor,
    74                   nsICacheEntryDescriptor,
    75                   nsICacheEntryInfo)
    77 nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry,
    78                                                nsCacheAccessMode accessGranted)
    79     : mCacheEntry(entry),
    80       mAccessGranted(accessGranted),
    81       mOutputWrapper(nullptr),
    82       mLock("nsCacheEntryDescriptor.mLock"),
    83       mAsyncDoomPending(false),
    84       mDoomedOnClose(false),
    85       mClosingDescriptor(false)
    86 {
    87     PR_INIT_CLIST(this);
    88     NS_ADDREF(nsCacheService::GlobalInstance());  // ensure it lives for the lifetime of the descriptor
    89 }
    92 nsCacheEntryDescriptor::~nsCacheEntryDescriptor()
    93 {
    94     // No need to close if the cache entry has already been severed.  This
    95     // helps avoid a shutdown assertion (bug 285519) that is caused when
    96     // consumers end up holding onto these objects past xpcom-shutdown.  It's
    97     // okay for them to do that because the cache service calls our Close
    98     // method during xpcom-shutdown, so we don't need to complain about it.
    99     if (mCacheEntry)
   100         Close();
   102     NS_ASSERTION(mInputWrappers.Count() == 0,
   103                  "We have still some input wrapper!");
   104     NS_ASSERTION(!mOutputWrapper, "We have still an output wrapper!");
   106     nsCacheService * service = nsCacheService::GlobalInstance();
   107     NS_RELEASE(service);
   108 }
   111 NS_IMETHODIMP
   112 nsCacheEntryDescriptor::GetClientID(char ** result)
   113 {
   114     NS_ENSURE_ARG_POINTER(result);
   116     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID));
   117     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   119     return ClientIDFromCacheKey(*(mCacheEntry->Key()), result);
   120 }
   123 NS_IMETHODIMP
   124 nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID)
   125 {
   126     NS_ENSURE_ARG_POINTER(aDeviceID);
   127     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDEVICEID));
   128     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   130     const char* deviceID = mCacheEntry->GetDeviceID();
   131     if (!deviceID) {
   132         *aDeviceID = nullptr;
   133         return NS_OK;
   134     }
   136     *aDeviceID = NS_strdup(deviceID);
   137     return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   138 }
   141 NS_IMETHODIMP
   142 nsCacheEntryDescriptor::GetKey(nsACString &result)
   143 {
   144     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY));
   145     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   147     return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result);
   148 }
   151 NS_IMETHODIMP
   152 nsCacheEntryDescriptor::GetFetchCount(int32_t *result)
   153 {
   154     NS_ENSURE_ARG_POINTER(result);
   155     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFETCHCOUNT));
   156     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   158     *result = mCacheEntry->FetchCount();
   159     return NS_OK;
   160 }
   163 NS_IMETHODIMP
   164 nsCacheEntryDescriptor::GetLastFetched(uint32_t *result)
   165 {
   166     NS_ENSURE_ARG_POINTER(result);
   167     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTFETCHED));
   168     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   170     *result = mCacheEntry->LastFetched();
   171     return NS_OK;
   172 }
   175 NS_IMETHODIMP
   176 nsCacheEntryDescriptor::GetLastModified(uint32_t *result)
   177 {
   178     NS_ENSURE_ARG_POINTER(result);
   179     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTMODIFIED));
   180     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   182     *result = mCacheEntry->LastModified();
   183     return NS_OK;
   184 }
   187 NS_IMETHODIMP
   188 nsCacheEntryDescriptor::GetExpirationTime(uint32_t *result)
   189 {
   190     NS_ENSURE_ARG_POINTER(result);
   191     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETEXPIRATIONTIME));
   192     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   194     *result = mCacheEntry->ExpirationTime();
   195     return NS_OK;
   196 }
   199 NS_IMETHODIMP
   200 nsCacheEntryDescriptor::SetExpirationTime(uint32_t expirationTime)
   201 {
   202     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETEXPIRATIONTIME));
   203     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   205     mCacheEntry->SetExpirationTime(expirationTime);
   206     mCacheEntry->MarkEntryDirty();
   207     return NS_OK;
   208 }
   211 NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(bool *result)
   212 {
   213     NS_ENSURE_ARG_POINTER(result);
   214     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_ISSTREAMBASED));
   215     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   217     *result = mCacheEntry->IsStreamData();
   218     return NS_OK;
   219 }
   221 NS_IMETHODIMP nsCacheEntryDescriptor::GetPredictedDataSize(int64_t *result)
   222 {
   223     NS_ENSURE_ARG_POINTER(result);
   224     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETPREDICTEDDATASIZE));
   225     if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
   227     *result = mCacheEntry->PredictedDataSize();
   228     return NS_OK;
   229 }
   231 NS_IMETHODIMP nsCacheEntryDescriptor::SetPredictedDataSize(int64_t
   232                                                            predictedSize)
   233 {
   234     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETPREDICTEDDATASIZE));
   235     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   237     mCacheEntry->SetPredictedDataSize(predictedSize);
   238     return NS_OK;
   239 }
   241 NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(uint32_t *result)
   242 {
   243     NS_ENSURE_ARG_POINTER(result);
   244     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDATASIZE));
   245     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   247     const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len");
   248     if (!val) {
   249         *result = mCacheEntry->DataSize();
   250     } else {
   251         *result = atol(val);
   252     }
   254     return NS_OK;
   255 }
   258 NS_IMETHODIMP nsCacheEntryDescriptor::GetStorageDataSize(uint32_t *result)
   259 {
   260     NS_ENSURE_ARG_POINTER(result);
   261     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEDATASIZE));
   262     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   264     *result = mCacheEntry->DataSize();
   266     return NS_OK;
   267 }
   270 nsresult
   271 nsCacheEntryDescriptor::RequestDataSizeChange(int32_t deltaSize)
   272 {
   273     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE));
   274     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   276     nsresult  rv;
   277     rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
   278     if (NS_SUCCEEDED(rv)) {
   279         // XXX review for signed/unsigned math errors
   280         uint32_t  newDataSize = mCacheEntry->DataSize() + deltaSize;
   281         mCacheEntry->SetDataSize(newDataSize);
   282         mCacheEntry->TouchData();
   283     }
   284     return rv;
   285 }
   288 NS_IMETHODIMP
   289 nsCacheEntryDescriptor::SetDataSize(uint32_t dataSize)
   290 {
   291     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETDATASIZE));
   292     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   294     // XXX review for signed/unsigned math errors
   295     int32_t  deltaSize = dataSize - mCacheEntry->DataSize();
   297     nsresult  rv;
   298     rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
   299     // this had better be NS_OK, this call instance is advisory for memory cache objects
   300     if (NS_SUCCEEDED(rv)) {
   301         // XXX review for signed/unsigned math errors
   302         uint32_t  newDataSize = mCacheEntry->DataSize() + deltaSize;
   303         mCacheEntry->SetDataSize(newDataSize);
   304         mCacheEntry->TouchData();
   305     } else {
   306         NS_WARNING("failed SetDataSize() on memory cache object!");
   307     }
   309     return rv;
   310 }
   313 NS_IMETHODIMP
   314 nsCacheEntryDescriptor::OpenInputStream(uint32_t offset, nsIInputStream ** result)
   315 {
   316     NS_ENSURE_ARG_POINTER(result);
   318     nsInputStreamWrapper* cacheInput = nullptr;
   319     {
   320         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENINPUTSTREAM));
   321         if (!mCacheEntry)                  return NS_ERROR_NOT_AVAILABLE;
   322         if (!mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
   324         // Don't open any new stream when closing descriptor or clearing entries
   325         if (mClosingDescriptor || nsCacheService::GetClearingEntries())
   326             return NS_ERROR_NOT_AVAILABLE;
   328         // ensure valid permissions
   329         if (!(mAccessGranted & nsICache::ACCESS_READ))
   330             return NS_ERROR_CACHE_READ_ACCESS_DENIED;
   332         const char *val;
   333         val = mCacheEntry->GetMetaDataElement("uncompressed-len");
   334         if (val) {
   335             cacheInput = new nsDecompressInputStreamWrapper(this, offset);
   336         } else {
   337             cacheInput = new nsInputStreamWrapper(this, offset);
   338         }
   339         if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY;
   341         mInputWrappers.AppendElement(cacheInput);
   342     }
   344     NS_ADDREF(*result = cacheInput);
   345     return NS_OK;
   346 }
   348 NS_IMETHODIMP
   349 nsCacheEntryDescriptor::OpenOutputStream(uint32_t offset, nsIOutputStream ** result)
   350 {
   351     NS_ENSURE_ARG_POINTER(result);
   353     nsOutputStreamWrapper* cacheOutput = nullptr;
   354     {
   355         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENOUTPUTSTREAM));
   356         if (!mCacheEntry)                  return NS_ERROR_NOT_AVAILABLE;
   357         if (!mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
   359         // Don't open any new stream when closing descriptor or clearing entries
   360         if (mClosingDescriptor || nsCacheService::GetClearingEntries())
   361             return NS_ERROR_NOT_AVAILABLE;
   363         // ensure valid permissions
   364         if (!(mAccessGranted & nsICache::ACCESS_WRITE))
   365             return NS_ERROR_CACHE_WRITE_ACCESS_DENIED;
   367         int32_t compressionLevel = nsCacheService::CacheCompressionLevel();
   368         const char *val;
   369         val = mCacheEntry->GetMetaDataElement("uncompressed-len");
   370         if ((compressionLevel > 0) && val) {
   371             cacheOutput = new nsCompressOutputStreamWrapper(this, offset);
   372         } else {
   373             // clear compression flag when compression disabled - see bug 715198
   374             if (val) {
   375                 mCacheEntry->SetMetaDataElement("uncompressed-len", nullptr);
   376             }
   377             cacheOutput = new nsOutputStreamWrapper(this, offset);
   378         }
   379         if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY;
   381         mOutputWrapper = cacheOutput;
   382     }
   384     NS_ADDREF(*result = cacheOutput);
   385     return NS_OK;
   386 }
   389 NS_IMETHODIMP
   390 nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result)
   391 {
   392     NS_ENSURE_ARG_POINTER(result);
   393     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCACHEELEMENT));
   394     if (!mCacheEntry)                 return NS_ERROR_NOT_AVAILABLE;
   395     if (mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_STREAM;
   397     NS_IF_ADDREF(*result = mCacheEntry->Data());
   398     return NS_OK;
   399 }
   402 NS_IMETHODIMP
   403 nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement)
   404 {
   405     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT));
   406     if (!mCacheEntry)                 return NS_ERROR_NOT_AVAILABLE;
   407     if (mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_STREAM;
   409     return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
   410 }
   413 NS_IMETHODIMP
   414 nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result)
   415 {
   416     NS_ENSURE_ARG_POINTER(result);
   417     *result = mAccessGranted;
   418     return NS_OK;
   419 }
   422 NS_IMETHODIMP
   423 nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result)
   424 {
   425     NS_ENSURE_ARG_POINTER(result);
   426     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEPOLICY));
   427     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   429     *result = mCacheEntry->StoragePolicy();
   430     return NS_OK;
   431 }
   434 NS_IMETHODIMP
   435 nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy)
   436 {
   437     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSTORAGEPOLICY));
   438     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   439     // XXX validate policy against session?
   441     bool        storageEnabled = false;
   442     storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy);
   443     if (!storageEnabled)    return NS_ERROR_FAILURE;
   445     // Don't change the storage policy of entries we can't write
   446     if (!(mAccessGranted & nsICache::ACCESS_WRITE))
   447         return NS_ERROR_NOT_AVAILABLE;
   449     // Don't allow a cache entry to move from memory-only to anything else
   450     if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY &&
   451         policy != nsICache::STORE_IN_MEMORY)
   452         return NS_ERROR_NOT_AVAILABLE;
   454     mCacheEntry->SetStoragePolicy(policy);
   455     mCacheEntry->MarkEntryDirty();
   456     return NS_OK;
   457 }
   460 NS_IMETHODIMP
   461 nsCacheEntryDescriptor::GetFile(nsIFile ** result)
   462 {
   463     NS_ENSURE_ARG_POINTER(result);
   464     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFILE));
   465     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   467     return nsCacheService::GetFileForEntry(mCacheEntry, result);
   468 }
   471 NS_IMETHODIMP
   472 nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result)
   473 {
   474     NS_ENSURE_ARG_POINTER(result);
   475     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSECURITYINFO));
   476     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   478     *result = mCacheEntry->SecurityInfo();
   479     NS_IF_ADDREF(*result);
   480     return NS_OK;
   481 }
   484 NS_IMETHODIMP
   485 nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo)
   486 {
   487     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO));
   488     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   490     mCacheEntry->SetSecurityInfo(securityInfo);
   491     mCacheEntry->MarkEntryDirty();
   492     return NS_OK;
   493 }
   496 NS_IMETHODIMP
   497 nsCacheEntryDescriptor::Doom()
   498 {
   499     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM));
   500     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   502     return nsCacheService::DoomEntry(mCacheEntry);
   503 }
   506 NS_IMETHODIMP
   507 nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status)
   508 {
   509     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS));
   510     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   512     return NS_ERROR_NOT_IMPLEMENTED;
   513 }
   516 NS_IMETHODIMP
   517 nsCacheEntryDescriptor::AsyncDoom(nsICacheListener *listener)
   518 {
   519     bool asyncDoomPending;
   520     {
   521         mozilla::MutexAutoLock lock(mLock);
   522         asyncDoomPending = mAsyncDoomPending;
   523         mAsyncDoomPending = true;
   524     }
   526     if (asyncDoomPending) {
   527         // AsyncDoom was already called. Notify listener if it is non-null,
   528         // otherwise just return success.
   529         if (listener) {
   530             nsresult rv = NS_DispatchToCurrentThread(
   531                 new nsNotifyDoomListener(listener, NS_ERROR_NOT_AVAILABLE));
   532             if (NS_SUCCEEDED(rv))
   533                 NS_IF_ADDREF(listener);
   534             return rv;
   535         }
   536         return NS_OK;
   537     }
   539     nsRefPtr<nsIRunnable> event = new nsAsyncDoomEvent(this, listener);
   540     return nsCacheService::DispatchToCacheIOThread(event);
   541 }
   544 NS_IMETHODIMP
   545 nsCacheEntryDescriptor::MarkValid()
   546 {
   547     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID));
   548     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   550     nsresult  rv = nsCacheService::ValidateEntry(mCacheEntry);
   551     return rv;
   552 }
   555 NS_IMETHODIMP
   556 nsCacheEntryDescriptor::Close()
   557 {
   558     nsRefPtr<nsOutputStreamWrapper> outputWrapper;
   559     nsTArray<nsRefPtr<nsInputStreamWrapper> > inputWrappers;
   561     {
   562         nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE));
   563         if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   565         // Make sure no other stream can be opened
   566         mClosingDescriptor = true;
   567         outputWrapper = mOutputWrapper;
   568         for (int32_t i = 0 ; i < mInputWrappers.Count() ; i++)
   569             inputWrappers.AppendElement(static_cast<nsInputStreamWrapper *>(
   570                         mInputWrappers[i]));
   571     }
   573     // Call Close() on the streams outside the lock since it might need to call
   574     // methods that grab the cache service lock, e.g. compressed output stream
   575     // when it finalizes the entry
   576     if (outputWrapper) {
   577         if (NS_FAILED(outputWrapper->Close())) {
   578             NS_WARNING("Dooming entry because Close() failed!!!");
   579             Doom();
   580         }
   581         outputWrapper = nullptr;
   582     }
   584     for (uint32_t i = 0 ; i < inputWrappers.Length() ; i++)
   585         inputWrappers[i]->Close();
   587     inputWrappers.Clear();
   589     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE));
   590     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   592     // XXX perhaps closing descriptors should clear/sever transports
   594     // tell nsCacheService we're going away
   595     nsCacheService::CloseDescriptor(this);
   596     NS_ASSERTION(mCacheEntry == nullptr, "mCacheEntry not null");
   598     return NS_OK;
   599 }
   602 NS_IMETHODIMP
   603 nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result)
   604 {
   605     NS_ENSURE_ARG_POINTER(key);
   606     *result = nullptr;
   608     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT));
   609     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
   611     const char *value;
   613     value = mCacheEntry->GetMetaDataElement(key);
   614     if (!value) return NS_ERROR_NOT_AVAILABLE;
   616     *result = NS_strdup(value);
   617     if (!*result) return NS_ERROR_OUT_OF_MEMORY;
   619     return NS_OK;
   620 }
   623 NS_IMETHODIMP
   624 nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value)
   625 {
   626     NS_ENSURE_ARG_POINTER(key);
   628     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT));
   629     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
   631     // XXX allow null value, for clearing key?
   633     nsresult rv = mCacheEntry->SetMetaDataElement(key, value);
   634     if (NS_SUCCEEDED(rv))
   635         mCacheEntry->TouchMetaData();
   636     return rv;
   637 }
   640 NS_IMETHODIMP
   641 nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor)
   642 {
   643     nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_VISITMETADATA)); 
   644     // XXX check callers, we're calling out of module
   645     NS_ENSURE_ARG_POINTER(visitor);
   646     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
   648     return mCacheEntry->VisitMetaDataElements(visitor);
   649 }
   652 /******************************************************************************
   653  * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry
   654  *                      open while referenced.
   655  ******************************************************************************/
   657 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsInputStreamWrapper)
   658 NS_IMETHODIMP_(MozExternalRefCountType)
   659 nsCacheEntryDescriptor::nsInputStreamWrapper::Release()
   660 {
   661     // Holding a reference to descriptor ensures that cache service won't go
   662     // away. Do not grab cache service lock if there is no descriptor.
   663     nsRefPtr<nsCacheEntryDescriptor> desc;
   665     {
   666         mozilla::MutexAutoLock lock(mLock);
   667         desc = mDescriptor;
   668     }
   670     if (desc)
   671         nsCacheService::Lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_RELEASE));
   673     nsrefcnt count;
   674     NS_PRECONDITION(0 != mRefCnt, "dup release");
   675     count = --mRefCnt;
   676     NS_LOG_RELEASE(this, count, "nsCacheEntryDescriptor::nsInputStreamWrapper");
   678     if (0 == count) {
   679         // don't use desc here since mDescriptor might be already nulled out
   680         if (mDescriptor) {
   681             NS_ASSERTION(mDescriptor->mInputWrappers.IndexOf(this) != -1,
   682                          "Wrapper not found in array!");
   683             mDescriptor->mInputWrappers.RemoveElement(this);
   684         }
   686         if (desc)
   687             nsCacheService::Unlock();
   689         mRefCnt = 1;
   690         delete (this);
   691         return 0;
   692     }
   694     if (desc)
   695         nsCacheService::Unlock();
   697     return count;
   698 }
   700 NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsInputStreamWrapper)
   701   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   702   NS_INTERFACE_MAP_ENTRY(nsISupports)
   703 NS_INTERFACE_MAP_END_THREADSAFE
   705 nsresult nsCacheEntryDescriptor::
   706 nsInputStreamWrapper::LazyInit()
   707 {
   708     // Check if we have the descriptor. If not we can't even grab the cache
   709     // lock since it is not ensured that the cache service still exists.
   710     if (!mDescriptor)
   711         return NS_ERROR_NOT_AVAILABLE;
   713     nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT));
   715     nsCacheAccessMode mode;
   716     nsresult rv = mDescriptor->GetAccessGranted(&mode);
   717     if (NS_FAILED(rv)) return rv;
   719     NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
   721     nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
   722     if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
   724     rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
   725                                                  mStartOffset,
   726                                                  getter_AddRefs(mInput));
   728     CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
   729                       "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
   730                       mDescriptor, this, mInput.get(), int(rv)));
   732     if (NS_FAILED(rv)) return rv;
   734     mInitialized = true;
   735     return NS_OK;
   736 }
   738 nsresult nsCacheEntryDescriptor::
   739 nsInputStreamWrapper::EnsureInit()
   740 {
   741     if (mInitialized) {
   742         NS_ASSERTION(mDescriptor, "Bad state");
   743         return NS_OK;
   744     }
   746     return LazyInit();
   747 }
   749 void nsCacheEntryDescriptor::
   750 nsInputStreamWrapper::CloseInternal()
   751 {
   752     mLock.AssertCurrentThreadOwns();
   753     if (!mDescriptor) {
   754         NS_ASSERTION(!mInitialized, "Bad state");
   755         NS_ASSERTION(!mInput, "Bad state");
   756         return;
   757     }
   759     nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_CLOSEINTERNAL));
   761     if (mDescriptor) {
   762         mDescriptor->mInputWrappers.RemoveElement(this);
   763         nsCacheService::ReleaseObject_Locked(mDescriptor);
   764         mDescriptor = nullptr;
   765     }
   766     mInitialized = false;
   767     mInput = nullptr;
   768 }
   770 nsresult nsCacheEntryDescriptor::
   771 nsInputStreamWrapper::Close()
   772 {
   773     mozilla::MutexAutoLock lock(mLock);
   775     return Close_Locked();
   776 }
   778 nsresult nsCacheEntryDescriptor::
   779 nsInputStreamWrapper::Close_Locked()
   780 {
   781     nsresult rv = EnsureInit();
   782     if (NS_SUCCEEDED(rv)) {
   783         rv = mInput->Close();
   784     } else {
   785         NS_ASSERTION(!mInput,
   786                      "Shouldn't have mInput when EnsureInit() failed");
   787     }
   789     // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are
   790     // closing streams with nsCacheService::CloseAllStream()
   791     CloseInternal();
   792     return rv;
   793 }
   795 nsresult nsCacheEntryDescriptor::
   796 nsInputStreamWrapper::Available(uint64_t *avail)
   797 {
   798     mozilla::MutexAutoLock lock(mLock);
   800     nsresult rv = EnsureInit();
   801     if (NS_FAILED(rv)) return rv;
   803     return mInput->Available(avail);
   804 }
   806 nsresult nsCacheEntryDescriptor::
   807 nsInputStreamWrapper::Read(char *buf, uint32_t count, uint32_t *countRead)
   808 {
   809     mozilla::MutexAutoLock lock(mLock);
   811     return Read_Locked(buf, count, countRead);
   812 }
   814 nsresult nsCacheEntryDescriptor::
   815 nsInputStreamWrapper::Read_Locked(char *buf, uint32_t count, uint32_t *countRead)
   816 {
   817     nsresult rv = EnsureInit();
   818     if (NS_SUCCEEDED(rv))
   819         rv = mInput->Read(buf, count, countRead);
   821     CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
   822                       "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
   823                       mDescriptor, this, mInput.get(), rv));
   825     return rv;
   826 }
   828 nsresult nsCacheEntryDescriptor::
   829 nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
   830                                    uint32_t count, uint32_t *countRead)
   831 {
   832     // cache stream not buffered
   833     return NS_ERROR_NOT_IMPLEMENTED;
   834 }
   836 nsresult nsCacheEntryDescriptor::
   837 nsInputStreamWrapper::IsNonBlocking(bool *result)
   838 {
   839     // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
   840     *result = false;
   841     return NS_OK;
   842 }
   845 /******************************************************************************
   846  * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses
   847  ******************************************************************************/
   849 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper)
   850 NS_IMETHODIMP_(MozExternalRefCountType)
   851 nsCacheEntryDescriptor::nsDecompressInputStreamWrapper::Release()
   852 {
   853     // Holding a reference to descriptor ensures that cache service won't go
   854     // away. Do not grab cache service lock if there is no descriptor.
   855     nsRefPtr<nsCacheEntryDescriptor> desc;
   857     {
   858         mozilla::MutexAutoLock lock(mLock);
   859         desc = mDescriptor;
   860     }
   862     if (desc)
   863         nsCacheService::Lock(LOCK_TELEM(
   864                              NSDECOMPRESSINPUTSTREAMWRAPPER_RELEASE));
   866     nsrefcnt count;
   867     NS_PRECONDITION(0 != mRefCnt, "dup release");
   868     count = --mRefCnt;
   869     NS_LOG_RELEASE(this, count,
   870                    "nsCacheEntryDescriptor::nsDecompressInputStreamWrapper");
   872     if (0 == count) {
   873         // don't use desc here since mDescriptor might be already nulled out
   874         if (mDescriptor) {
   875             NS_ASSERTION(mDescriptor->mInputWrappers.IndexOf(this) != -1,
   876                          "Wrapper not found in array!");
   877             mDescriptor->mInputWrappers.RemoveElement(this);
   878         }
   880         if (desc)
   881             nsCacheService::Unlock();
   883         mRefCnt = 1;
   884         delete (this);
   885         return 0;
   886     }
   888     if (desc)
   889         nsCacheService::Unlock();
   891     return count;
   892 }
   894 NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper)
   895   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   896   NS_INTERFACE_MAP_ENTRY(nsISupports)
   897 NS_INTERFACE_MAP_END_THREADSAFE
   899 NS_IMETHODIMP nsCacheEntryDescriptor::
   900 nsDecompressInputStreamWrapper::Read(char *    buf, 
   901                                      uint32_t  count, 
   902                                      uint32_t *countRead)
   903 {
   904     mozilla::MutexAutoLock lock(mLock);
   906     int zerr = Z_OK;
   907     nsresult rv = NS_OK;
   909     if (!mStreamInitialized) {
   910         rv = InitZstream();
   911         if (NS_FAILED(rv)) {
   912             return rv;
   913         }
   914     }
   916     mZstream.next_out = (Bytef*)buf;
   917     mZstream.avail_out = count;
   919     if (mReadBufferLen < count) {
   920         // Allocate a buffer for reading from the input stream. This will
   921         // determine the max number of compressed bytes read from the
   922         // input stream at one time. Making the buffer size proportional
   923         // to the request size is not necessary, but helps minimize the
   924         // number of read requests to the input stream.
   925         uint32_t newBufLen = std::max(count, (uint32_t)kMinDecompressReadBufLen);
   926         unsigned char* newBuf;
   927         newBuf = (unsigned char*)nsMemory::Realloc(mReadBuffer, 
   928             newBufLen);
   929         if (newBuf) {
   930             mReadBuffer = newBuf;
   931             mReadBufferLen = newBufLen;
   932         }
   933         if (!mReadBuffer) {
   934             mReadBufferLen = 0;
   935             return NS_ERROR_OUT_OF_MEMORY;
   936         }
   937     }
   939     // read and inflate data until the output buffer is full, or
   940     // there is no more data to read
   941     while (NS_SUCCEEDED(rv) &&
   942            zerr == Z_OK && 
   943            mZstream.avail_out > 0 &&
   944            count > 0) {
   945         if (mZstream.avail_in == 0) {
   946             rv = nsInputStreamWrapper::Read_Locked((char*)mReadBuffer,
   947                                                    mReadBufferLen,
   948                                                    &mZstream.avail_in);
   949             if (NS_FAILED(rv) || !mZstream.avail_in) {
   950                 break;
   951             }
   952             mZstream.next_in = mReadBuffer;
   953         }
   954         zerr = inflate(&mZstream, Z_NO_FLUSH);
   955         if (zerr == Z_STREAM_END) {
   956             // The compressed data may have been stored in multiple
   957             // chunks/streams. To allow for this case, re-initialize 
   958             // the inflate stream and continue decompressing from 
   959             // the next byte.
   960             Bytef * saveNextIn = mZstream.next_in;
   961             unsigned int saveAvailIn = mZstream.avail_in;
   962             Bytef * saveNextOut = mZstream.next_out;
   963             unsigned int saveAvailOut = mZstream.avail_out;
   964             inflateReset(&mZstream);
   965             mZstream.next_in = saveNextIn;
   966             mZstream.avail_in = saveAvailIn;
   967             mZstream.next_out = saveNextOut;
   968             mZstream.avail_out = saveAvailOut;
   969             zerr = Z_OK;
   970         } else if (zerr != Z_OK) {
   971             rv = NS_ERROR_INVALID_CONTENT_ENCODING;
   972         }
   973     }
   974     if (NS_SUCCEEDED(rv)) {
   975         *countRead = count - mZstream.avail_out;
   976     }
   977     return rv;
   978 }
   980 nsresult nsCacheEntryDescriptor::
   981 nsDecompressInputStreamWrapper::Close()
   982 {
   983     mozilla::MutexAutoLock lock(mLock);
   985     if (!mDescriptor)
   986         return NS_ERROR_NOT_AVAILABLE;
   988     EndZstream();
   989     if (mReadBuffer) {
   990         nsMemory::Free(mReadBuffer);
   991         mReadBuffer = 0;
   992         mReadBufferLen = 0;
   993     }
   994     return nsInputStreamWrapper::Close_Locked();
   995 }
   997 nsresult nsCacheEntryDescriptor::
   998 nsDecompressInputStreamWrapper::InitZstream()
   999 {
  1000     if (!mDescriptor)
  1001         return NS_ERROR_NOT_AVAILABLE;
  1003     if (mStreamEnded)
  1004         return NS_ERROR_FAILURE;
  1006     // Initialize zlib inflate stream
  1007     mZstream.zalloc = Z_NULL;
  1008     mZstream.zfree = Z_NULL;
  1009     mZstream.opaque = Z_NULL;
  1010     mZstream.next_out = Z_NULL;
  1011     mZstream.avail_out = 0;
  1012     mZstream.next_in = Z_NULL;
  1013     mZstream.avail_in = 0;
  1014     if (inflateInit(&mZstream) != Z_OK) {
  1015         return NS_ERROR_FAILURE;
  1017     mStreamInitialized = true;
  1018     return NS_OK;
  1021 nsresult nsCacheEntryDescriptor::
  1022 nsDecompressInputStreamWrapper::EndZstream()
  1024     if (mStreamInitialized && !mStreamEnded) {
  1025         inflateEnd(&mZstream);
  1026         mStreamInitialized = false;
  1027         mStreamEnded = true;
  1029     return NS_OK;
  1033 /******************************************************************************
  1034  * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of
  1035  *                         data written to a cache entry.
  1036  *                       - also keeps the cache entry open while referenced.
  1037  ******************************************************************************/
  1039 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsOutputStreamWrapper)
  1040 NS_IMETHODIMP_(MozExternalRefCountType)
  1041 nsCacheEntryDescriptor::nsOutputStreamWrapper::Release()
  1043     // Holding a reference to descriptor ensures that cache service won't go
  1044     // away. Do not grab cache service lock if there is no descriptor.
  1045     nsRefPtr<nsCacheEntryDescriptor> desc;
  1048         mozilla::MutexAutoLock lock(mLock);
  1049         desc = mDescriptor;
  1052     if (desc)
  1053         nsCacheService::Lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_RELEASE));
  1055     nsrefcnt count;
  1056     NS_PRECONDITION(0 != mRefCnt, "dup release");
  1057     count = --mRefCnt;
  1058     NS_LOG_RELEASE(this, count,
  1059                    "nsCacheEntryDescriptor::nsOutputStreamWrapper");
  1061     if (0 == count) {
  1062         // don't use desc here since mDescriptor might be already nulled out
  1063         if (mDescriptor)
  1064             mDescriptor->mOutputWrapper = nullptr;
  1066         if (desc)
  1067             nsCacheService::Unlock();
  1069         mRefCnt = 1;
  1070         delete (this);
  1071         return 0;
  1074     if (desc)
  1075         nsCacheService::Unlock();
  1077     return count;
  1080 NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsOutputStreamWrapper)
  1081   NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
  1082   NS_INTERFACE_MAP_ENTRY(nsISupports)
  1083 NS_INTERFACE_MAP_END_THREADSAFE
  1085 nsresult nsCacheEntryDescriptor::
  1086 nsOutputStreamWrapper::LazyInit()
  1088     // Check if we have the descriptor. If not we can't even grab the cache
  1089     // lock since it is not ensured that the cache service still exists.
  1090     if (!mDescriptor)
  1091         return NS_ERROR_NOT_AVAILABLE;
  1093     nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT));
  1095     nsCacheAccessMode mode;
  1096     nsresult rv = mDescriptor->GetAccessGranted(&mode);
  1097     if (NS_FAILED(rv)) return rv;
  1099     NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED);
  1101     nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
  1102     if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
  1104     NS_ASSERTION(mOutput == nullptr, "mOutput set in LazyInit");
  1106     nsCOMPtr<nsIOutputStream> stream;
  1107     rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset,
  1108                                                   getter_AddRefs(stream));
  1109     if (NS_FAILED(rv))
  1110         return rv;
  1112     nsCacheDevice* device = cacheEntry->CacheDevice();
  1113     if (device) {
  1114         // the entry has been truncated to mStartOffset bytes, inform device
  1115         int32_t size = cacheEntry->DataSize();
  1116         rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size);
  1117         if (NS_SUCCEEDED(rv))
  1118             cacheEntry->SetDataSize(mStartOffset);
  1119     } else {
  1120         rv = NS_ERROR_NOT_AVAILABLE;
  1123     // If anything above failed, clean up internal state and get out of here
  1124     // (see bug #654926)...
  1125     if (NS_FAILED(rv)) {
  1126         nsCacheService::ReleaseObject_Locked(stream.forget().take());
  1127         mDescriptor->mOutputWrapper = nullptr;
  1128         nsCacheService::ReleaseObject_Locked(mDescriptor);
  1129         mDescriptor = nullptr;
  1130         mInitialized = false;
  1131         return rv;
  1134     mOutput = stream;
  1135     mInitialized = true;
  1136     return NS_OK;
  1139 nsresult nsCacheEntryDescriptor::
  1140 nsOutputStreamWrapper::EnsureInit()
  1142     if (mInitialized) {
  1143         NS_ASSERTION(mDescriptor, "Bad state");
  1144         return NS_OK;
  1147     return LazyInit();
  1150 nsresult nsCacheEntryDescriptor::
  1151 nsOutputStreamWrapper::OnWrite(uint32_t count)
  1153     if (count > INT32_MAX)  return NS_ERROR_UNEXPECTED;
  1154     return mDescriptor->RequestDataSizeChange((int32_t)count);
  1157 void nsCacheEntryDescriptor::
  1158 nsOutputStreamWrapper::CloseInternal()
  1160     mLock.AssertCurrentThreadOwns();
  1161     if (!mDescriptor) {
  1162         NS_ASSERTION(!mInitialized, "Bad state");
  1163         NS_ASSERTION(!mOutput, "Bad state");
  1164         return;
  1167     nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_CLOSEINTERNAL));
  1169     if (mDescriptor) {
  1170         mDescriptor->mOutputWrapper = nullptr;
  1171         nsCacheService::ReleaseObject_Locked(mDescriptor);
  1172         mDescriptor = nullptr;
  1174     mInitialized = false;
  1175     mOutput = nullptr;
  1179 NS_IMETHODIMP nsCacheEntryDescriptor::
  1180 nsOutputStreamWrapper::Close()
  1182     mozilla::MutexAutoLock lock(mLock);
  1184     return Close_Locked();
  1187 nsresult nsCacheEntryDescriptor::
  1188 nsOutputStreamWrapper::Close_Locked()
  1190     nsresult rv = EnsureInit();
  1191     if (NS_SUCCEEDED(rv)) {
  1192         rv = mOutput->Close();
  1193     } else {
  1194         NS_ASSERTION(!mOutput,
  1195                      "Shouldn't have mOutput when EnsureInit() failed");
  1198     // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are
  1199     // closing streams with nsCacheService::CloseAllStream()
  1200     CloseInternal();
  1201     return rv;
  1204 NS_IMETHODIMP nsCacheEntryDescriptor::
  1205 nsOutputStreamWrapper::Flush()
  1207     mozilla::MutexAutoLock lock(mLock);
  1209     nsresult rv = EnsureInit();
  1210     if (NS_FAILED(rv)) return rv;
  1212     return mOutput->Flush();
  1215 NS_IMETHODIMP nsCacheEntryDescriptor::
  1216 nsOutputStreamWrapper::Write(const char * buf,
  1217                              uint32_t     count,
  1218                              uint32_t *   result)
  1220     mozilla::MutexAutoLock lock(mLock);
  1221     return Write_Locked(buf, count, result);
  1224 nsresult nsCacheEntryDescriptor::
  1225 nsOutputStreamWrapper::Write_Locked(const char * buf,
  1226                                     uint32_t count,
  1227                                     uint32_t * result)
  1229     nsresult rv = EnsureInit();
  1230     if (NS_FAILED(rv)) return rv;
  1232     rv = OnWrite(count);
  1233     if (NS_FAILED(rv)) return rv;
  1235     return mOutput->Write(buf, count, result);
  1238 NS_IMETHODIMP nsCacheEntryDescriptor::
  1239 nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr,
  1240                                  uint32_t         count,
  1241                                  uint32_t *       result)
  1243     return NS_ERROR_NOT_IMPLEMENTED;
  1246 NS_IMETHODIMP nsCacheEntryDescriptor::
  1247 nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun  reader,
  1248                                      void *            closure,
  1249                                      uint32_t          count,
  1250                                      uint32_t *        result)
  1252     return NS_ERROR_NOT_IMPLEMENTED;
  1255 NS_IMETHODIMP nsCacheEntryDescriptor::
  1256 nsOutputStreamWrapper::IsNonBlocking(bool *result)
  1258     // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
  1259     *result = false;
  1260     return NS_OK;
  1264 /******************************************************************************
  1265  * nsCompressOutputStreamWrapper - an output stream wrapper that compresses
  1266  *   data before it is written
  1267  ******************************************************************************/
  1269 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper)
  1270 NS_IMETHODIMP_(MozExternalRefCountType)
  1271 nsCacheEntryDescriptor::nsCompressOutputStreamWrapper::Release()
  1273     // Holding a reference to descriptor ensures that cache service won't go
  1274     // away. Do not grab cache service lock if there is no descriptor.
  1275     nsRefPtr<nsCacheEntryDescriptor> desc;
  1278         mozilla::MutexAutoLock lock(mLock);
  1279         desc = mDescriptor;
  1282     if (desc)
  1283         nsCacheService::Lock(LOCK_TELEM(NSCOMPRESSOUTPUTSTREAMWRAPPER_RELEASE));
  1285     nsrefcnt count;
  1286     NS_PRECONDITION(0 != mRefCnt, "dup release");
  1287     count = --mRefCnt;
  1288     NS_LOG_RELEASE(this, count,
  1289                    "nsCacheEntryDescriptor::nsCompressOutputStreamWrapper");
  1291     if (0 == count) {
  1292         // don't use desc here since mDescriptor might be already nulled out
  1293         if (mDescriptor)
  1294             mDescriptor->mOutputWrapper = nullptr;
  1296         if (desc)
  1297             nsCacheService::Unlock();
  1299         mRefCnt = 1;
  1300         delete (this);
  1301         return 0;
  1304     if (desc)
  1305         nsCacheService::Unlock();
  1307     return count;
  1310 NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper)
  1311   NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
  1312   NS_INTERFACE_MAP_ENTRY(nsISupports)
  1313 NS_INTERFACE_MAP_END_THREADSAFE
  1315 NS_IMETHODIMP nsCacheEntryDescriptor::
  1316 nsCompressOutputStreamWrapper::Write(const char * buf,
  1317                                      uint32_t     count,
  1318                                      uint32_t *   result)
  1320     mozilla::MutexAutoLock lock(mLock);
  1322     int zerr = Z_OK;
  1323     nsresult rv = NS_OK;
  1325     if (!mStreamInitialized) {
  1326         rv = InitZstream();
  1327         if (NS_FAILED(rv)) {
  1328             return rv;
  1332     if (!mWriteBuffer) {
  1333         // Once allocated, this buffer is referenced by the zlib stream and
  1334         // cannot be grown. We use 2x(initial write request) to approximate
  1335         // a stream buffer size proportional to request buffers.
  1336         mWriteBufferLen = std::max(count*2, (uint32_t)kMinCompressWriteBufLen);
  1337         mWriteBuffer = (unsigned char*)nsMemory::Alloc(mWriteBufferLen);
  1338         if (!mWriteBuffer) {
  1339             mWriteBufferLen = 0;
  1340             return NS_ERROR_OUT_OF_MEMORY;
  1342         mZstream.next_out = mWriteBuffer;
  1343         mZstream.avail_out = mWriteBufferLen;
  1346     // Compress (deflate) the requested buffer. Keep going
  1347     // until the entire buffer has been deflated.
  1348     mZstream.avail_in = count;
  1349     mZstream.next_in = (Bytef*)buf;
  1350     while (mZstream.avail_in > 0) {
  1351         zerr = deflate(&mZstream, Z_NO_FLUSH);
  1352         if (zerr == Z_STREAM_ERROR) {
  1353             deflateEnd(&mZstream);
  1354             mStreamEnded = true;
  1355             mStreamInitialized = false;
  1356             return NS_ERROR_FAILURE;
  1358         // Note: Z_BUF_ERROR is non-fatal and sometimes expected here.
  1360         // If the compression stream output buffer is filled, write
  1361         // it out to the underlying stream wrapper.
  1362         if (mZstream.avail_out == 0) {
  1363             rv = WriteBuffer();
  1364             if (NS_FAILED(rv)) {
  1365                 deflateEnd(&mZstream);
  1366                 mStreamEnded = true;
  1367                 mStreamInitialized = false;
  1368                 return rv;
  1372     *result = count;
  1373     mUncompressedCount += *result;
  1374     return NS_OK;
  1377 NS_IMETHODIMP nsCacheEntryDescriptor::
  1378 nsCompressOutputStreamWrapper::Close()
  1380     mozilla::MutexAutoLock lock(mLock);
  1382     if (!mDescriptor)
  1383         return NS_ERROR_NOT_AVAILABLE;
  1385     nsresult retval = NS_OK;
  1386     nsresult rv;
  1387     int zerr = 0;
  1389     if (mStreamInitialized) {
  1390         // complete compression of any data remaining in the zlib stream
  1391         do {
  1392             zerr = deflate(&mZstream, Z_FINISH);
  1393             rv = WriteBuffer();
  1394             if (NS_FAILED(rv))
  1395                 retval = rv;
  1396         } while (zerr == Z_OK && rv == NS_OK);
  1397         deflateEnd(&mZstream);
  1398         mStreamInitialized = false;
  1400     // Do not allow to initialize stream after calling Close().
  1401     mStreamEnded = true;
  1403     if (mDescriptor->CacheEntry()) {
  1404         nsAutoCString uncompressedLenStr;
  1405         rv = mDescriptor->GetMetaDataElement("uncompressed-len",
  1406                                              getter_Copies(uncompressedLenStr));
  1407         if (NS_SUCCEEDED(rv)) {
  1408             int32_t oldCount = uncompressedLenStr.ToInteger(&rv);
  1409             if (NS_SUCCEEDED(rv)) {
  1410                 mUncompressedCount += oldCount;
  1413         uncompressedLenStr.Adopt(0);
  1414         uncompressedLenStr.AppendInt(mUncompressedCount);
  1415         rv = mDescriptor->SetMetaDataElement("uncompressed-len",
  1416             uncompressedLenStr.get());
  1417         if (NS_FAILED(rv))
  1418             retval = rv;
  1421     if (mWriteBuffer) {
  1422         nsMemory::Free(mWriteBuffer);
  1423         mWriteBuffer = 0;
  1424         mWriteBufferLen = 0;
  1427     rv = nsOutputStreamWrapper::Close_Locked();
  1428     if (NS_FAILED(rv))
  1429         retval = rv;
  1431     return retval;
  1434 nsresult nsCacheEntryDescriptor::
  1435 nsCompressOutputStreamWrapper::InitZstream()
  1437     if (!mDescriptor)
  1438         return NS_ERROR_NOT_AVAILABLE;
  1440     if (mStreamEnded)
  1441         return NS_ERROR_FAILURE;
  1443     // Determine compression level: Aggressive compression
  1444     // may impact performance on mobile devices, while a
  1445     // lower compression level still provides substantial
  1446     // space savings for many text streams.
  1447     int32_t compressionLevel = nsCacheService::CacheCompressionLevel();
  1449     // Initialize zlib deflate stream
  1450     mZstream.zalloc = Z_NULL;
  1451     mZstream.zfree = Z_NULL;
  1452     mZstream.opaque = Z_NULL;
  1453     if (deflateInit2(&mZstream, compressionLevel, Z_DEFLATED,
  1454                      MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
  1455         return NS_ERROR_FAILURE;
  1457     mZstream.next_in = Z_NULL;
  1458     mZstream.avail_in = 0;
  1460     mStreamInitialized = true;
  1462     return NS_OK;
  1465 nsresult nsCacheEntryDescriptor::
  1466 nsCompressOutputStreamWrapper::WriteBuffer()
  1468     uint32_t bytesToWrite = mWriteBufferLen - mZstream.avail_out;
  1469     uint32_t result = 0;
  1470     nsresult rv = nsCacheEntryDescriptor::nsOutputStreamWrapper::Write_Locked(
  1471         (const char *)mWriteBuffer, bytesToWrite, &result);
  1472     mZstream.next_out = mWriteBuffer;
  1473     mZstream.avail_out = mWriteBufferLen;
  1474     return rv;

mercurial