diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /netwerk/cache/nsCacheEntryDescriptor.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'netwerk/cache/nsCacheEntryDescriptor.cpp')
-rw-r--r-- | netwerk/cache/nsCacheEntryDescriptor.cpp | 1475 |
1 files changed, 1475 insertions, 0 deletions
diff --git a/netwerk/cache/nsCacheEntryDescriptor.cpp b/netwerk/cache/nsCacheEntryDescriptor.cpp new file mode 100644 index 000000000..b53ee3058 --- /dev/null +++ b/netwerk/cache/nsCacheEntryDescriptor.cpp @@ -0,0 +1,1475 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsICache.h" +#include "nsCache.h" +#include "nsCacheService.h" +#include "nsCacheEntryDescriptor.h" +#include "nsCacheEntry.h" +#include "nsReadableUtils.h" +#include "nsIOutputStream.h" +#include "nsCRT.h" +#include "nsThreadUtils.h" +#include <algorithm> + +#define kMinDecompressReadBufLen 1024 +#define kMinCompressWriteBufLen 1024 + + +/****************************************************************************** + * nsAsyncDoomEvent + *****************************************************************************/ + +class nsAsyncDoomEvent : public mozilla::Runnable { +public: + nsAsyncDoomEvent(nsCacheEntryDescriptor *descriptor, + nsICacheListener *listener) + { + mDescriptor = descriptor; + mListener = listener; + mThread = do_GetCurrentThread(); + // We addref the listener here and release it in nsNotifyDoomListener + // on the callers thread. If posting of nsNotifyDoomListener event fails + // we leak the listener which is better than releasing it on a wrong + // thread. + NS_IF_ADDREF(mListener); + } + + NS_IMETHOD Run() override + { + nsresult status = NS_OK; + + { + nsCacheServiceAutoLock lock(LOCK_TELEM(NSASYNCDOOMEVENT_RUN)); + + if (mDescriptor->mCacheEntry) { + status = nsCacheService::gService->DoomEntry_Internal( + mDescriptor->mCacheEntry, true); + } else if (!mDescriptor->mDoomedOnClose) { + status = NS_ERROR_NOT_AVAILABLE; + } + } + + if (mListener) { + mThread->Dispatch(new nsNotifyDoomListener(mListener, status), + NS_DISPATCH_NORMAL); + // posted event will release the reference on the correct thread + mListener = nullptr; + } + + return NS_OK; + } + +private: + RefPtr<nsCacheEntryDescriptor> mDescriptor; + nsICacheListener *mListener; + nsCOMPtr<nsIThread> mThread; +}; + + +NS_IMPL_ISUPPORTS(nsCacheEntryDescriptor, + nsICacheEntryDescriptor, + nsICacheEntryInfo) + +nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry, + nsCacheAccessMode accessGranted) + : mCacheEntry(entry), + mAccessGranted(accessGranted), + mOutputWrapper(nullptr), + mLock("nsCacheEntryDescriptor.mLock"), + mAsyncDoomPending(false), + mDoomedOnClose(false), + mClosingDescriptor(false) +{ + PR_INIT_CLIST(this); + NS_ADDREF(nsCacheService::GlobalInstance()); // ensure it lives for the lifetime of the descriptor +} + + +nsCacheEntryDescriptor::~nsCacheEntryDescriptor() +{ + // No need to close if the cache entry has already been severed. This + // helps avoid a shutdown assertion (bug 285519) that is caused when + // consumers end up holding onto these objects past xpcom-shutdown. It's + // okay for them to do that because the cache service calls our Close + // method during xpcom-shutdown, so we don't need to complain about it. + if (mCacheEntry) + Close(); + + NS_ASSERTION(mInputWrappers.IsEmpty(), + "We have still some input wrapper!"); + NS_ASSERTION(!mOutputWrapper, "We have still an output wrapper!"); + + nsCacheService * service = nsCacheService::GlobalInstance(); + NS_RELEASE(service); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetClientID(char ** result) +{ + NS_ENSURE_ARG_POINTER(result); + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return ClientIDFromCacheKey(*(mCacheEntry->Key()), result); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID) +{ + NS_ENSURE_ARG_POINTER(aDeviceID); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDEVICEID)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + const char* deviceID = mCacheEntry->GetDeviceID(); + if (!deviceID) { + *aDeviceID = nullptr; + return NS_OK; + } + + *aDeviceID = NS_strdup(deviceID); + return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetKey(nsACString &result) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetFetchCount(int32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFETCHCOUNT)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->FetchCount(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetLastFetched(uint32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTFETCHED)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->LastFetched(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetLastModified(uint32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTMODIFIED)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->LastModified(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetExpirationTime(uint32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETEXPIRATIONTIME)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->ExpirationTime(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetExpirationTime(uint32_t expirationTime) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETEXPIRATIONTIME)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + mCacheEntry->SetExpirationTime(expirationTime); + mCacheEntry->MarkEntryDirty(); + return NS_OK; +} + + +NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(bool *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_ISSTREAMBASED)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->IsStreamData(); + return NS_OK; +} + +NS_IMETHODIMP nsCacheEntryDescriptor::GetPredictedDataSize(int64_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETPREDICTEDDATASIZE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->PredictedDataSize(); + return NS_OK; +} + +NS_IMETHODIMP nsCacheEntryDescriptor::SetPredictedDataSize(int64_t + predictedSize) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETPREDICTEDDATASIZE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + mCacheEntry->SetPredictedDataSize(predictedSize); + return NS_OK; +} + +NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(uint32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDATASIZE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len"); + if (!val) { + *result = mCacheEntry->DataSize(); + } else { + *result = atol(val); + } + + return NS_OK; +} + + +NS_IMETHODIMP nsCacheEntryDescriptor::GetStorageDataSize(uint32_t *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEDATASIZE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->DataSize(); + + return NS_OK; +} + + +nsresult +nsCacheEntryDescriptor::RequestDataSizeChange(int32_t deltaSize) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + nsresult rv; + rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize); + if (NS_SUCCEEDED(rv)) { + // XXX review for signed/unsigned math errors + uint32_t newDataSize = mCacheEntry->DataSize() + deltaSize; + mCacheEntry->SetDataSize(newDataSize); + mCacheEntry->TouchData(); + } + return rv; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetDataSize(uint32_t dataSize) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETDATASIZE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + // XXX review for signed/unsigned math errors + int32_t deltaSize = dataSize - mCacheEntry->DataSize(); + + nsresult rv; + rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize); + // this had better be NS_OK, this call instance is advisory for memory cache objects + if (NS_SUCCEEDED(rv)) { + // XXX review for signed/unsigned math errors + uint32_t newDataSize = mCacheEntry->DataSize() + deltaSize; + mCacheEntry->SetDataSize(newDataSize); + mCacheEntry->TouchData(); + } else { + NS_WARNING("failed SetDataSize() on memory cache object!"); + } + + return rv; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::OpenInputStream(uint32_t offset, nsIInputStream ** result) +{ + NS_ENSURE_ARG_POINTER(result); + + nsInputStreamWrapper* cacheInput = nullptr; + { + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENINPUTSTREAM)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM; + + // Don't open any new stream when closing descriptor or clearing entries + if (mClosingDescriptor || nsCacheService::GetClearingEntries()) + return NS_ERROR_NOT_AVAILABLE; + + // ensure valid permissions + if (!(mAccessGranted & nsICache::ACCESS_READ)) + return NS_ERROR_CACHE_READ_ACCESS_DENIED; + + const char *val; + val = mCacheEntry->GetMetaDataElement("uncompressed-len"); + if (val) { + cacheInput = new nsDecompressInputStreamWrapper(this, offset); + } else { + cacheInput = new nsInputStreamWrapper(this, offset); + } + if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY; + + mInputWrappers.AppendElement(cacheInput); + } + + NS_ADDREF(*result = cacheInput); + return NS_OK; +} + +NS_IMETHODIMP +nsCacheEntryDescriptor::OpenOutputStream(uint32_t offset, nsIOutputStream ** result) +{ + NS_ENSURE_ARG_POINTER(result); + + nsOutputStreamWrapper* cacheOutput = nullptr; + { + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENOUTPUTSTREAM)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM; + + // Don't open any new stream when closing descriptor or clearing entries + if (mClosingDescriptor || nsCacheService::GetClearingEntries()) + return NS_ERROR_NOT_AVAILABLE; + + // ensure valid permissions + if (!(mAccessGranted & nsICache::ACCESS_WRITE)) + return NS_ERROR_CACHE_WRITE_ACCESS_DENIED; + + int32_t compressionLevel = nsCacheService::CacheCompressionLevel(); + const char *val; + val = mCacheEntry->GetMetaDataElement("uncompressed-len"); + if ((compressionLevel > 0) && val) { + cacheOutput = new nsCompressOutputStreamWrapper(this, offset); + } else { + // clear compression flag when compression disabled - see bug 715198 + if (val) { + mCacheEntry->SetMetaDataElement("uncompressed-len", nullptr); + } + cacheOutput = new nsOutputStreamWrapper(this, offset); + } + if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY; + + mOutputWrapper = cacheOutput; + } + + NS_ADDREF(*result = cacheOutput); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCACHEELEMENT)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM; + + NS_IF_ADDREF(*result = mCacheEntry->Data()); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM; + + return nsCacheService::SetCacheElement(mCacheEntry, cacheElement); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result) +{ + NS_ENSURE_ARG_POINTER(result); + *result = mAccessGranted; + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEPOLICY)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->StoragePolicy(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSTORAGEPOLICY)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + // XXX validate policy against session? + + bool storageEnabled = false; + storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy); + if (!storageEnabled) return NS_ERROR_FAILURE; + + // Don't change the storage policy of entries we can't write + if (!(mAccessGranted & nsICache::ACCESS_WRITE)) + return NS_ERROR_NOT_AVAILABLE; + + // Don't allow a cache entry to move from memory-only to anything else + if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY && + policy != nsICache::STORE_IN_MEMORY) + return NS_ERROR_NOT_AVAILABLE; + + mCacheEntry->SetStoragePolicy(policy); + mCacheEntry->MarkEntryDirty(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetFile(nsIFile ** result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFILE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return nsCacheService::GetFileForEntry(mCacheEntry, result); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result) +{ + NS_ENSURE_ARG_POINTER(result); + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSECURITYINFO)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + *result = mCacheEntry->SecurityInfo(); + NS_IF_ADDREF(*result); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + mCacheEntry->SetSecurityInfo(securityInfo); + mCacheEntry->MarkEntryDirty(); + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::Doom() +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return nsCacheService::DoomEntry(mCacheEntry); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::AsyncDoom(nsICacheListener *listener) +{ + bool asyncDoomPending; + { + mozilla::MutexAutoLock lock(mLock); + asyncDoomPending = mAsyncDoomPending; + mAsyncDoomPending = true; + } + + if (asyncDoomPending) { + // AsyncDoom was already called. Notify listener if it is non-null, + // otherwise just return success. + if (listener) { + nsresult rv = NS_DispatchToCurrentThread( + new nsNotifyDoomListener(listener, NS_ERROR_NOT_AVAILABLE)); + if (NS_SUCCEEDED(rv)) + NS_IF_ADDREF(listener); + return rv; + } + return NS_OK; + } + + nsCOMPtr<nsIRunnable> event = new nsAsyncDoomEvent(this, listener); + return nsCacheService::DispatchToCacheIOThread(event); +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::MarkValid() +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + nsresult rv = nsCacheService::ValidateEntry(mCacheEntry); + return rv; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::Close() +{ + RefPtr<nsOutputStreamWrapper> outputWrapper; + nsTArray<RefPtr<nsInputStreamWrapper> > inputWrappers; + + { + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + // Make sure no other stream can be opened + mClosingDescriptor = true; + outputWrapper = mOutputWrapper; + for (size_t i = 0; i < mInputWrappers.Length(); i++) + inputWrappers.AppendElement(mInputWrappers[i]); + } + + // Call Close() on the streams outside the lock since it might need to call + // methods that grab the cache service lock, e.g. compressed output stream + // when it finalizes the entry + if (outputWrapper) { + if (NS_FAILED(outputWrapper->Close())) { + NS_WARNING("Dooming entry because Close() failed!!!"); + Doom(); + } + outputWrapper = nullptr; + } + + for (uint32_t i = 0 ; i < inputWrappers.Length() ; i++) + inputWrappers[i]->Close(); + + inputWrappers.Clear(); + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE)); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + // XXX perhaps closing descriptors should clear/sever transports + + // tell nsCacheService we're going away + nsCacheService::CloseDescriptor(this); + NS_ASSERTION(mCacheEntry == nullptr, "mCacheEntry not null"); + + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result) +{ + NS_ENSURE_ARG_POINTER(key); + *result = nullptr; + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT)); + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE); + + const char *value; + + value = mCacheEntry->GetMetaDataElement(key); + if (!value) return NS_ERROR_NOT_AVAILABLE; + + *result = NS_strdup(value); + if (!*result) return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value) +{ + NS_ENSURE_ARG_POINTER(key); + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT)); + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE); + + // XXX allow null value, for clearing key? + + nsresult rv = mCacheEntry->SetMetaDataElement(key, value); + if (NS_SUCCEEDED(rv)) + mCacheEntry->TouchMetaData(); + return rv; +} + + +NS_IMETHODIMP +nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor) +{ + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_VISITMETADATA)); + // XXX check callers, we're calling out of module + NS_ENSURE_ARG_POINTER(visitor); + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; + + return mCacheEntry->VisitMetaDataElements(visitor); +} + + +/****************************************************************************** + * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry + * open while referenced. + ******************************************************************************/ + +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsInputStreamWrapper) +NS_IMETHODIMP_(MozExternalRefCountType) +nsCacheEntryDescriptor::nsInputStreamWrapper::Release() +{ + // Holding a reference to descriptor ensures that cache service won't go + // away. Do not grab cache service lock if there is no descriptor. + RefPtr<nsCacheEntryDescriptor> desc; + + { + mozilla::MutexAutoLock lock(mLock); + desc = mDescriptor; + } + + if (desc) + nsCacheService::Lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_RELEASE)); + + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = --mRefCnt; + NS_LOG_RELEASE(this, count, "nsCacheEntryDescriptor::nsInputStreamWrapper"); + + if (0 == count) { + // don't use desc here since mDescriptor might be already nulled out + if (mDescriptor) { + NS_ASSERTION(mDescriptor->mInputWrappers.Contains(this), + "Wrapper not found in array!"); + mDescriptor->mInputWrappers.RemoveElement(this); + } + + if (desc) + nsCacheService::Unlock(); + + mRefCnt = 1; + delete (this); + return 0; + } + + if (desc) + nsCacheService::Unlock(); + + return count; +} + +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsInputStreamWrapper) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END_THREADSAFE + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::LazyInit() +{ + // Check if we have the descriptor. If not we can't even grab the cache + // lock since it is not ensured that the cache service still exists. + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT)); + + nsCacheAccessMode mode; + nsresult rv = mDescriptor->GetAccessGranted(&mode); + if (NS_FAILED(rv)) return rv; + + NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED); + + nsCacheEntry* cacheEntry = mDescriptor->CacheEntry(); + if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE; + + rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode, + mStartOffset, + getter_AddRefs(mInput)); + + CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit " + "[entry=%p, wrapper=%p, mInput=%p, rv=%d]", + mDescriptor, this, mInput.get(), int(rv))); + + if (NS_FAILED(rv)) return rv; + + mInitialized = true; + return NS_OK; +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::EnsureInit() +{ + if (mInitialized) { + NS_ASSERTION(mDescriptor, "Bad state"); + return NS_OK; + } + + return LazyInit(); +} + +void nsCacheEntryDescriptor:: +nsInputStreamWrapper::CloseInternal() +{ + mLock.AssertCurrentThreadOwns(); + if (!mDescriptor) { + NS_ASSERTION(!mInitialized, "Bad state"); + NS_ASSERTION(!mInput, "Bad state"); + return; + } + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_CLOSEINTERNAL)); + + if (mDescriptor) { + mDescriptor->mInputWrappers.RemoveElement(this); + nsCacheService::ReleaseObject_Locked(mDescriptor); + mDescriptor = nullptr; + } + mInitialized = false; + mInput = nullptr; +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::Close() +{ + mozilla::MutexAutoLock lock(mLock); + + return Close_Locked(); +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::Close_Locked() +{ + nsresult rv = EnsureInit(); + if (NS_SUCCEEDED(rv)) { + rv = mInput->Close(); + } else { + NS_ASSERTION(!mInput, + "Shouldn't have mInput when EnsureInit() failed"); + } + + // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are + // closing streams with nsCacheService::CloseAllStream() + CloseInternal(); + return rv; +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::Available(uint64_t *avail) +{ + mozilla::MutexAutoLock lock(mLock); + + nsresult rv = EnsureInit(); + if (NS_FAILED(rv)) return rv; + + return mInput->Available(avail); +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::Read(char *buf, uint32_t count, uint32_t *countRead) +{ + mozilla::MutexAutoLock lock(mLock); + + return Read_Locked(buf, count, countRead); +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::Read_Locked(char *buf, uint32_t count, uint32_t *countRead) +{ + nsresult rv = EnsureInit(); + if (NS_SUCCEEDED(rv)) + rv = mInput->Read(buf, count, countRead); + + CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read " + "[entry=%p, wrapper=%p, mInput=%p, rv=%d]", + mDescriptor, this, mInput.get(), rv)); + + return rv; +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure, + uint32_t count, uint32_t *countRead) +{ + // cache stream not buffered + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult nsCacheEntryDescriptor:: +nsInputStreamWrapper::IsNonBlocking(bool *result) +{ + // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK + *result = false; + return NS_OK; +} + + +/****************************************************************************** + * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses + ******************************************************************************/ + +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper) +NS_IMETHODIMP_(MozExternalRefCountType) +nsCacheEntryDescriptor::nsDecompressInputStreamWrapper::Release() +{ + // Holding a reference to descriptor ensures that cache service won't go + // away. Do not grab cache service lock if there is no descriptor. + RefPtr<nsCacheEntryDescriptor> desc; + + { + mozilla::MutexAutoLock lock(mLock); + desc = mDescriptor; + } + + if (desc) + nsCacheService::Lock(LOCK_TELEM( + NSDECOMPRESSINPUTSTREAMWRAPPER_RELEASE)); + + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = --mRefCnt; + NS_LOG_RELEASE(this, count, + "nsCacheEntryDescriptor::nsDecompressInputStreamWrapper"); + + if (0 == count) { + // don't use desc here since mDescriptor might be already nulled out + if (mDescriptor) { + NS_ASSERTION(mDescriptor->mInputWrappers.Contains(this), + "Wrapper not found in array!"); + mDescriptor->mInputWrappers.RemoveElement(this); + } + + if (desc) + nsCacheService::Unlock(); + + mRefCnt = 1; + delete (this); + return 0; + } + + if (desc) + nsCacheService::Unlock(); + + return count; +} + +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END_THREADSAFE + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsDecompressInputStreamWrapper::Read(char * buf, + uint32_t count, + uint32_t *countRead) +{ + mozilla::MutexAutoLock lock(mLock); + + int zerr = Z_OK; + nsresult rv = NS_OK; + + if (!mStreamInitialized) { + rv = InitZstream(); + if (NS_FAILED(rv)) { + return rv; + } + } + + mZstream.next_out = (Bytef*)buf; + mZstream.avail_out = count; + + if (mReadBufferLen < count) { + // Allocate a buffer for reading from the input stream. This will + // determine the max number of compressed bytes read from the + // input stream at one time. Making the buffer size proportional + // to the request size is not necessary, but helps minimize the + // number of read requests to the input stream. + uint32_t newBufLen = std::max(count, (uint32_t)kMinDecompressReadBufLen); + unsigned char* newBuf; + newBuf = (unsigned char*)moz_xrealloc(mReadBuffer, + newBufLen); + if (newBuf) { + mReadBuffer = newBuf; + mReadBufferLen = newBufLen; + } + if (!mReadBuffer) { + mReadBufferLen = 0; + return NS_ERROR_OUT_OF_MEMORY; + } + } + + // read and inflate data until the output buffer is full, or + // there is no more data to read + while (NS_SUCCEEDED(rv) && + zerr == Z_OK && + mZstream.avail_out > 0 && + count > 0) { + if (mZstream.avail_in == 0) { + rv = nsInputStreamWrapper::Read_Locked((char*)mReadBuffer, + mReadBufferLen, + &mZstream.avail_in); + if (NS_FAILED(rv) || !mZstream.avail_in) { + break; + } + mZstream.next_in = mReadBuffer; + } + zerr = inflate(&mZstream, Z_NO_FLUSH); + if (zerr == Z_STREAM_END) { + // The compressed data may have been stored in multiple + // chunks/streams. To allow for this case, re-initialize + // the inflate stream and continue decompressing from + // the next byte. + Bytef * saveNextIn = mZstream.next_in; + unsigned int saveAvailIn = mZstream.avail_in; + Bytef * saveNextOut = mZstream.next_out; + unsigned int saveAvailOut = mZstream.avail_out; + inflateReset(&mZstream); + mZstream.next_in = saveNextIn; + mZstream.avail_in = saveAvailIn; + mZstream.next_out = saveNextOut; + mZstream.avail_out = saveAvailOut; + zerr = Z_OK; + } else if (zerr != Z_OK) { + rv = NS_ERROR_INVALID_CONTENT_ENCODING; + } + } + if (NS_SUCCEEDED(rv)) { + *countRead = count - mZstream.avail_out; + } + return rv; +} + +nsresult nsCacheEntryDescriptor:: +nsDecompressInputStreamWrapper::Close() +{ + mozilla::MutexAutoLock lock(mLock); + + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + EndZstream(); + if (mReadBuffer) { + free(mReadBuffer); + mReadBuffer = 0; + mReadBufferLen = 0; + } + return nsInputStreamWrapper::Close_Locked(); +} + +nsresult nsCacheEntryDescriptor:: +nsDecompressInputStreamWrapper::InitZstream() +{ + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + if (mStreamEnded) + return NS_ERROR_FAILURE; + + // Initialize zlib inflate stream + mZstream.zalloc = Z_NULL; + mZstream.zfree = Z_NULL; + mZstream.opaque = Z_NULL; + mZstream.next_out = Z_NULL; + mZstream.avail_out = 0; + mZstream.next_in = Z_NULL; + mZstream.avail_in = 0; + if (inflateInit(&mZstream) != Z_OK) { + return NS_ERROR_FAILURE; + } + mStreamInitialized = true; + return NS_OK; +} + +nsresult nsCacheEntryDescriptor:: +nsDecompressInputStreamWrapper::EndZstream() +{ + if (mStreamInitialized && !mStreamEnded) { + inflateEnd(&mZstream); + mStreamInitialized = false; + mStreamEnded = true; + } + return NS_OK; +} + + +/****************************************************************************** + * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of + * data written to a cache entry. + * - also keeps the cache entry open while referenced. + ******************************************************************************/ + +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsOutputStreamWrapper) +NS_IMETHODIMP_(MozExternalRefCountType) +nsCacheEntryDescriptor::nsOutputStreamWrapper::Release() +{ + // Holding a reference to descriptor ensures that cache service won't go + // away. Do not grab cache service lock if there is no descriptor. + RefPtr<nsCacheEntryDescriptor> desc; + + { + mozilla::MutexAutoLock lock(mLock); + desc = mDescriptor; + } + + if (desc) + nsCacheService::Lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_RELEASE)); + + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = --mRefCnt; + NS_LOG_RELEASE(this, count, + "nsCacheEntryDescriptor::nsOutputStreamWrapper"); + + if (0 == count) { + // don't use desc here since mDescriptor might be already nulled out + if (mDescriptor) + mDescriptor->mOutputWrapper = nullptr; + + if (desc) + nsCacheService::Unlock(); + + mRefCnt = 1; + delete (this); + return 0; + } + + if (desc) + nsCacheService::Unlock(); + + return count; +} + +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsOutputStreamWrapper) + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END_THREADSAFE + +nsresult nsCacheEntryDescriptor:: +nsOutputStreamWrapper::LazyInit() +{ + // Check if we have the descriptor. If not we can't even grab the cache + // lock since it is not ensured that the cache service still exists. + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT)); + + nsCacheAccessMode mode; + nsresult rv = mDescriptor->GetAccessGranted(&mode); + if (NS_FAILED(rv)) return rv; + + NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED); + + nsCacheEntry* cacheEntry = mDescriptor->CacheEntry(); + if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE; + + NS_ASSERTION(mOutput == nullptr, "mOutput set in LazyInit"); + + nsCOMPtr<nsIOutputStream> stream; + rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset, + getter_AddRefs(stream)); + if (NS_FAILED(rv)) + return rv; + + nsCacheDevice* device = cacheEntry->CacheDevice(); + if (device) { + // the entry has been truncated to mStartOffset bytes, inform device + int32_t size = cacheEntry->DataSize(); + rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size); + if (NS_SUCCEEDED(rv)) + cacheEntry->SetDataSize(mStartOffset); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } + + // If anything above failed, clean up internal state and get out of here + // (see bug #654926)... + if (NS_FAILED(rv)) { + nsCacheService::ReleaseObject_Locked(stream.forget().take()); + mDescriptor->mOutputWrapper = nullptr; + nsCacheService::ReleaseObject_Locked(mDescriptor); + mDescriptor = nullptr; + mInitialized = false; + return rv; + } + + mOutput = stream; + mInitialized = true; + return NS_OK; +} + +nsresult nsCacheEntryDescriptor:: +nsOutputStreamWrapper::EnsureInit() +{ + if (mInitialized) { + NS_ASSERTION(mDescriptor, "Bad state"); + return NS_OK; + } + + return LazyInit(); +} + +nsresult nsCacheEntryDescriptor:: +nsOutputStreamWrapper::OnWrite(uint32_t count) +{ + if (count > INT32_MAX) return NS_ERROR_UNEXPECTED; + return mDescriptor->RequestDataSizeChange((int32_t)count); +} + +void nsCacheEntryDescriptor:: +nsOutputStreamWrapper::CloseInternal() +{ + mLock.AssertCurrentThreadOwns(); + if (!mDescriptor) { + NS_ASSERTION(!mInitialized, "Bad state"); + NS_ASSERTION(!mOutput, "Bad state"); + return; + } + + nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_CLOSEINTERNAL)); + + if (mDescriptor) { + mDescriptor->mOutputWrapper = nullptr; + nsCacheService::ReleaseObject_Locked(mDescriptor); + mDescriptor = nullptr; + } + mInitialized = false; + mOutput = nullptr; +} + + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::Close() +{ + mozilla::MutexAutoLock lock(mLock); + + return Close_Locked(); +} + +nsresult nsCacheEntryDescriptor:: +nsOutputStreamWrapper::Close_Locked() +{ + nsresult rv = EnsureInit(); + if (NS_SUCCEEDED(rv)) { + rv = mOutput->Close(); + } else { + NS_ASSERTION(!mOutput, + "Shouldn't have mOutput when EnsureInit() failed"); + } + + // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are + // closing streams with nsCacheService::CloseAllStream() + CloseInternal(); + return rv; +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::Flush() +{ + mozilla::MutexAutoLock lock(mLock); + + nsresult rv = EnsureInit(); + if (NS_FAILED(rv)) return rv; + + return mOutput->Flush(); +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::Write(const char * buf, + uint32_t count, + uint32_t * result) +{ + mozilla::MutexAutoLock lock(mLock); + return Write_Locked(buf, count, result); +} + +nsresult nsCacheEntryDescriptor:: +nsOutputStreamWrapper::Write_Locked(const char * buf, + uint32_t count, + uint32_t * result) +{ + nsresult rv = EnsureInit(); + if (NS_FAILED(rv)) return rv; + + rv = OnWrite(count); + if (NS_FAILED(rv)) return rv; + + return mOutput->Write(buf, count, result); +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr, + uint32_t count, + uint32_t * result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader, + void * closure, + uint32_t count, + uint32_t * result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsOutputStreamWrapper::IsNonBlocking(bool *result) +{ + // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK + *result = false; + return NS_OK; +} + + +/****************************************************************************** + * nsCompressOutputStreamWrapper - an output stream wrapper that compresses + * data before it is written + ******************************************************************************/ + +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper) +NS_IMETHODIMP_(MozExternalRefCountType) +nsCacheEntryDescriptor::nsCompressOutputStreamWrapper::Release() +{ + // Holding a reference to descriptor ensures that cache service won't go + // away. Do not grab cache service lock if there is no descriptor. + RefPtr<nsCacheEntryDescriptor> desc; + + { + mozilla::MutexAutoLock lock(mLock); + desc = mDescriptor; + } + + if (desc) + nsCacheService::Lock(LOCK_TELEM(NSCOMPRESSOUTPUTSTREAMWRAPPER_RELEASE)); + + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = --mRefCnt; + NS_LOG_RELEASE(this, count, + "nsCacheEntryDescriptor::nsCompressOutputStreamWrapper"); + + if (0 == count) { + // don't use desc here since mDescriptor might be already nulled out + if (mDescriptor) + mDescriptor->mOutputWrapper = nullptr; + + if (desc) + nsCacheService::Unlock(); + + mRefCnt = 1; + delete (this); + return 0; + } + + if (desc) + nsCacheService::Unlock(); + + return count; +} + +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper) + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END_THREADSAFE + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsCompressOutputStreamWrapper::Write(const char * buf, + uint32_t count, + uint32_t * result) +{ + mozilla::MutexAutoLock lock(mLock); + + int zerr = Z_OK; + nsresult rv = NS_OK; + + if (!mStreamInitialized) { + rv = InitZstream(); + if (NS_FAILED(rv)) { + return rv; + } + } + + if (!mWriteBuffer) { + // Once allocated, this buffer is referenced by the zlib stream and + // cannot be grown. We use 2x(initial write request) to approximate + // a stream buffer size proportional to request buffers. + mWriteBufferLen = std::max(count*2, (uint32_t)kMinCompressWriteBufLen); + mWriteBuffer = (unsigned char*)moz_xmalloc(mWriteBufferLen); + if (!mWriteBuffer) { + mWriteBufferLen = 0; + return NS_ERROR_OUT_OF_MEMORY; + } + mZstream.next_out = mWriteBuffer; + mZstream.avail_out = mWriteBufferLen; + } + + // Compress (deflate) the requested buffer. Keep going + // until the entire buffer has been deflated. + mZstream.avail_in = count; + mZstream.next_in = (Bytef*)buf; + while (mZstream.avail_in > 0) { + zerr = deflate(&mZstream, Z_NO_FLUSH); + if (zerr == Z_STREAM_ERROR) { + deflateEnd(&mZstream); + mStreamEnded = true; + mStreamInitialized = false; + return NS_ERROR_FAILURE; + } + // Note: Z_BUF_ERROR is non-fatal and sometimes expected here. + + // If the compression stream output buffer is filled, write + // it out to the underlying stream wrapper. + if (mZstream.avail_out == 0) { + rv = WriteBuffer(); + if (NS_FAILED(rv)) { + deflateEnd(&mZstream); + mStreamEnded = true; + mStreamInitialized = false; + return rv; + } + } + } + *result = count; + mUncompressedCount += *result; + return NS_OK; +} + +NS_IMETHODIMP nsCacheEntryDescriptor:: +nsCompressOutputStreamWrapper::Close() +{ + mozilla::MutexAutoLock lock(mLock); + + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + nsresult retval = NS_OK; + nsresult rv; + int zerr = 0; + + if (mStreamInitialized) { + // complete compression of any data remaining in the zlib stream + do { + zerr = deflate(&mZstream, Z_FINISH); + rv = WriteBuffer(); + if (NS_FAILED(rv)) + retval = rv; + } while (zerr == Z_OK && rv == NS_OK); + deflateEnd(&mZstream); + mStreamInitialized = false; + } + // Do not allow to initialize stream after calling Close(). + mStreamEnded = true; + + if (mDescriptor->CacheEntry()) { + nsAutoCString uncompressedLenStr; + rv = mDescriptor->GetMetaDataElement("uncompressed-len", + getter_Copies(uncompressedLenStr)); + if (NS_SUCCEEDED(rv)) { + int32_t oldCount = uncompressedLenStr.ToInteger(&rv); + if (NS_SUCCEEDED(rv)) { + mUncompressedCount += oldCount; + } + } + uncompressedLenStr.Adopt(0); + uncompressedLenStr.AppendInt(mUncompressedCount); + rv = mDescriptor->SetMetaDataElement("uncompressed-len", + uncompressedLenStr.get()); + if (NS_FAILED(rv)) + retval = rv; + } + + if (mWriteBuffer) { + free(mWriteBuffer); + mWriteBuffer = 0; + mWriteBufferLen = 0; + } + + rv = nsOutputStreamWrapper::Close_Locked(); + if (NS_FAILED(rv)) + retval = rv; + + return retval; +} + +nsresult nsCacheEntryDescriptor:: +nsCompressOutputStreamWrapper::InitZstream() +{ + if (!mDescriptor) + return NS_ERROR_NOT_AVAILABLE; + + if (mStreamEnded) + return NS_ERROR_FAILURE; + + // Determine compression level: Aggressive compression + // may impact performance on mobile devices, while a + // lower compression level still provides substantial + // space savings for many text streams. + int32_t compressionLevel = nsCacheService::CacheCompressionLevel(); + + // Initialize zlib deflate stream + mZstream.zalloc = Z_NULL; + mZstream.zfree = Z_NULL; + mZstream.opaque = Z_NULL; + if (deflateInit2(&mZstream, compressionLevel, Z_DEFLATED, + MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + return NS_ERROR_FAILURE; + } + mZstream.next_in = Z_NULL; + mZstream.avail_in = 0; + + mStreamInitialized = true; + + return NS_OK; +} + +nsresult nsCacheEntryDescriptor:: +nsCompressOutputStreamWrapper::WriteBuffer() +{ + uint32_t bytesToWrite = mWriteBufferLen - mZstream.avail_out; + uint32_t result = 0; + nsresult rv = nsCacheEntryDescriptor::nsOutputStreamWrapper::Write_Locked( + (const char *)mWriteBuffer, bytesToWrite, &result); + mZstream.next_out = mWriteBuffer; + mZstream.avail_out = mWriteBufferLen; + return rv; +} + |