1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
9 #include "nsCacheService.h"
10 #include "nsCacheEntryDescriptor.h"
11 #include "nsCacheEntry.h"
12 #include "nsReadableUtils.h"
13 #include "nsIOutputStream.h"
16 #define kMinDecompressReadBufLen 1024
17 #define kMinCompressWriteBufLen 1024
19 NS_IMPL_THREADSAFE_ISUPPORTS2(nsCacheEntryDescriptor
,
20 nsICacheEntryDescriptor
,
23 nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry
* entry
,
24 nsCacheAccessMode accessGranted
)
26 mAccessGranted(accessGranted
),
30 NS_ADDREF(nsCacheService::GlobalInstance()); // ensure it lives for the lifetime of the descriptor
34 nsCacheEntryDescriptor::~nsCacheEntryDescriptor()
36 // No need to close if the cache entry has already been severed. This
37 // helps avoid a shutdown assertion (bug 285519) that is caused when
38 // consumers end up holding onto these objects past xpcom-shutdown. It's
39 // okay for them to do that because the cache service calls our Close
40 // method during xpcom-shutdown, so we don't need to complain about it.
44 nsCacheService
* service
= nsCacheService::GlobalInstance();
50 nsCacheEntryDescriptor::GetClientID(char ** result
)
52 NS_ENSURE_ARG_POINTER(result
);
54 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID
));
55 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
57 return ClientIDFromCacheKey(*(mCacheEntry
->Key()), result
);
62 nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID
)
64 NS_ENSURE_ARG_POINTER(aDeviceID
);
65 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDEVICEID
));
66 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
68 const char* deviceID
= mCacheEntry
->GetDeviceID();
74 *aDeviceID
= NS_strdup(deviceID
);
75 return *aDeviceID
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
80 nsCacheEntryDescriptor::GetKey(nsACString
&result
)
82 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY
));
83 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
85 return ClientKeyFromCacheKey(*(mCacheEntry
->Key()), result
);
90 nsCacheEntryDescriptor::GetFetchCount(PRInt32
*result
)
92 NS_ENSURE_ARG_POINTER(result
);
93 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFETCHCOUNT
));
94 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
96 *result
= mCacheEntry
->FetchCount();
102 nsCacheEntryDescriptor::GetLastFetched(PRUint32
*result
)
104 NS_ENSURE_ARG_POINTER(result
);
105 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTFETCHED
));
106 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
108 *result
= mCacheEntry
->LastFetched();
114 nsCacheEntryDescriptor::GetLastModified(PRUint32
*result
)
116 NS_ENSURE_ARG_POINTER(result
);
117 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTMODIFIED
));
118 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
120 *result
= mCacheEntry
->LastModified();
126 nsCacheEntryDescriptor::GetExpirationTime(PRUint32
*result
)
128 NS_ENSURE_ARG_POINTER(result
);
129 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETEXPIRATIONTIME
));
130 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
132 *result
= mCacheEntry
->ExpirationTime();
138 nsCacheEntryDescriptor::SetExpirationTime(PRUint32 expirationTime
)
140 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETEXPIRATIONTIME
));
141 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
143 mCacheEntry
->SetExpirationTime(expirationTime
);
144 mCacheEntry
->MarkEntryDirty();
149 NS_IMETHODIMP
nsCacheEntryDescriptor::IsStreamBased(bool *result
)
151 NS_ENSURE_ARG_POINTER(result
);
152 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_ISSTREAMBASED
));
153 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
155 *result
= mCacheEntry
->IsStreamData();
159 NS_IMETHODIMP
nsCacheEntryDescriptor::GetPredictedDataSize(PRInt64
*result
)
161 NS_ENSURE_ARG_POINTER(result
);
162 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETPREDICTEDDATASIZE
));
163 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
165 *result
= mCacheEntry
->PredictedDataSize();
169 NS_IMETHODIMP
nsCacheEntryDescriptor::SetPredictedDataSize(PRInt64
172 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETPREDICTEDDATASIZE
));
173 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
175 mCacheEntry
->SetPredictedDataSize(predictedSize
);
179 NS_IMETHODIMP
nsCacheEntryDescriptor::GetDataSize(PRUint32
*result
)
181 NS_ENSURE_ARG_POINTER(result
);
182 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDATASIZE
));
183 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
185 const char* val
= mCacheEntry
->GetMetaDataElement("uncompressed-len");
187 *result
= mCacheEntry
->DataSize();
196 NS_IMETHODIMP
nsCacheEntryDescriptor::GetStorageDataSize(PRUint32
*result
)
198 NS_ENSURE_ARG_POINTER(result
);
199 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEDATASIZE
));
200 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
202 *result
= mCacheEntry
->DataSize();
209 nsCacheEntryDescriptor::RequestDataSizeChange(PRInt32 deltaSize
)
211 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE
));
212 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
215 rv
= nsCacheService::OnDataSizeChange(mCacheEntry
, deltaSize
);
216 if (NS_SUCCEEDED(rv
)) {
217 // XXX review for signed/unsigned math errors
218 PRUint32 newDataSize
= mCacheEntry
->DataSize() + deltaSize
;
219 mCacheEntry
->SetDataSize(newDataSize
);
220 mCacheEntry
->TouchData();
227 nsCacheEntryDescriptor::SetDataSize(PRUint32 dataSize
)
229 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETDATASIZE
));
230 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
232 // XXX review for signed/unsigned math errors
233 PRInt32 deltaSize
= dataSize
- mCacheEntry
->DataSize();
236 rv
= nsCacheService::OnDataSizeChange(mCacheEntry
, deltaSize
);
237 // this had better be NS_OK, this call instance is advisory for memory cache objects
238 if (NS_SUCCEEDED(rv
)) {
239 // XXX review for signed/unsigned math errors
240 PRUint32 newDataSize
= mCacheEntry
->DataSize() + deltaSize
;
241 mCacheEntry
->SetDataSize(newDataSize
);
242 mCacheEntry
->TouchData();
244 NS_WARNING("failed SetDataSize() on memory cache object!");
252 nsCacheEntryDescriptor::OpenInputStream(PRUint32 offset
, nsIInputStream
** result
)
254 NS_ENSURE_ARG_POINTER(result
);
257 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENINPUTSTREAM
));
258 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
259 if (!mCacheEntry
->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM
;
261 // ensure valid permissions
262 if (!(mAccessGranted
& nsICache::ACCESS_READ
))
263 return NS_ERROR_CACHE_READ_ACCESS_DENIED
;
266 nsInputStreamWrapper
* cacheInput
= nullptr;
268 val
= mCacheEntry
->GetMetaDataElement("uncompressed-len");
270 cacheInput
= new nsDecompressInputStreamWrapper(this, offset
);
272 cacheInput
= new nsInputStreamWrapper(this, offset
);
274 if (!cacheInput
) return NS_ERROR_OUT_OF_MEMORY
;
276 NS_ADDREF(*result
= cacheInput
);
281 nsCacheEntryDescriptor::OpenOutputStream(PRUint32 offset
, nsIOutputStream
** result
)
283 NS_ENSURE_ARG_POINTER(result
);
286 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENOUTPUTSTREAM
));
287 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
288 if (!mCacheEntry
->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM
;
290 // ensure valid permissions
291 if (!(mAccessGranted
& nsICache::ACCESS_WRITE
))
292 return NS_ERROR_CACHE_WRITE_ACCESS_DENIED
;
295 nsOutputStreamWrapper
* cacheOutput
= nullptr;
296 PRInt32 compressionLevel
= nsCacheService::CacheCompressionLevel();
298 val
= mCacheEntry
->GetMetaDataElement("uncompressed-len");
299 if ((compressionLevel
> 0) && val
) {
300 cacheOutput
= new nsCompressOutputStreamWrapper(this, offset
);
302 // clear compression flag when compression disabled - see bug #715198
304 mCacheEntry
->SetMetaDataElement("uncompressed-len", nullptr);
306 cacheOutput
= new nsOutputStreamWrapper(this, offset
);
308 if (!cacheOutput
) return NS_ERROR_OUT_OF_MEMORY
;
310 NS_ADDREF(*result
= cacheOutput
);
316 nsCacheEntryDescriptor::GetCacheElement(nsISupports
** result
)
318 NS_ENSURE_ARG_POINTER(result
);
319 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCACHEELEMENT
));
320 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
321 if (mCacheEntry
->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM
;
323 NS_IF_ADDREF(*result
= mCacheEntry
->Data());
329 nsCacheEntryDescriptor::SetCacheElement(nsISupports
* cacheElement
)
331 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT
));
332 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
333 if (mCacheEntry
->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM
;
335 return nsCacheService::SetCacheElement(mCacheEntry
, cacheElement
);
340 nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode
*result
)
342 NS_ENSURE_ARG_POINTER(result
);
343 *result
= mAccessGranted
;
349 nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy
*result
)
351 NS_ENSURE_ARG_POINTER(result
);
352 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEPOLICY
));
353 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
355 *result
= mCacheEntry
->StoragePolicy();
361 nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy
)
363 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSTORAGEPOLICY
));
364 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
365 // XXX validate policy against session?
367 bool storageEnabled
= false;
368 storageEnabled
= nsCacheService::IsStorageEnabledForPolicy_Locked(policy
);
369 if (!storageEnabled
) return NS_ERROR_FAILURE
;
371 // Don't change the storage policy of entries we can't write
372 if (!(mAccessGranted
& nsICache::ACCESS_WRITE
))
373 return NS_ERROR_NOT_AVAILABLE
;
375 // Don't allow a cache entry to move from memory-only to anything else
376 if (mCacheEntry
->StoragePolicy() == nsICache::STORE_IN_MEMORY
&&
377 policy
!= nsICache::STORE_IN_MEMORY
)
378 return NS_ERROR_NOT_AVAILABLE
;
380 mCacheEntry
->SetStoragePolicy(policy
);
381 mCacheEntry
->MarkEntryDirty();
387 nsCacheEntryDescriptor::GetFile(nsIFile
** result
)
389 NS_ENSURE_ARG_POINTER(result
);
390 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFILE
));
391 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
393 return nsCacheService::GetFileForEntry(mCacheEntry
, result
);
398 nsCacheEntryDescriptor::GetSecurityInfo(nsISupports
** result
)
400 NS_ENSURE_ARG_POINTER(result
);
401 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSECURITYINFO
));
402 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
404 *result
= mCacheEntry
->SecurityInfo();
405 NS_IF_ADDREF(*result
);
411 nsCacheEntryDescriptor::SetSecurityInfo(nsISupports
* securityInfo
)
413 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO
));
414 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
416 mCacheEntry
->SetSecurityInfo(securityInfo
);
417 mCacheEntry
->MarkEntryDirty();
423 nsCacheEntryDescriptor::Doom()
425 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM
));
426 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
428 return nsCacheService::DoomEntry(mCacheEntry
);
433 nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status
)
435 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS
));
436 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
438 return NS_ERROR_NOT_IMPLEMENTED
;
443 nsCacheEntryDescriptor::MarkValid()
445 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID
));
446 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
448 nsresult rv
= nsCacheService::ValidateEntry(mCacheEntry
);
454 nsCacheEntryDescriptor::Close()
456 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE
));
457 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
459 // XXX perhaps closing descriptors should clear/sever transports
461 // tell nsCacheService we're going away
462 nsCacheService::CloseDescriptor(this);
463 NS_ASSERTION(mCacheEntry
== nullptr, "mCacheEntry not null");
470 nsCacheEntryDescriptor::GetMetaDataElement(const char *key
, char **result
)
472 NS_ENSURE_ARG_POINTER(key
);
475 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT
));
476 NS_ENSURE_TRUE(mCacheEntry
, NS_ERROR_NOT_AVAILABLE
);
480 value
= mCacheEntry
->GetMetaDataElement(key
);
481 if (!value
) return NS_ERROR_NOT_AVAILABLE
;
483 *result
= NS_strdup(value
);
484 if (!*result
) return NS_ERROR_OUT_OF_MEMORY
;
491 nsCacheEntryDescriptor::SetMetaDataElement(const char *key
, const char *value
)
493 NS_ENSURE_ARG_POINTER(key
);
495 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT
));
496 NS_ENSURE_TRUE(mCacheEntry
, NS_ERROR_NOT_AVAILABLE
);
498 // XXX allow null value, for clearing key?
500 nsresult rv
= mCacheEntry
->SetMetaDataElement(key
, value
);
501 if (NS_SUCCEEDED(rv
))
502 mCacheEntry
->TouchMetaData();
508 nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor
* visitor
)
510 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_VISITMETADATA
));
511 // XXX check callers, we're calling out of module
512 NS_ENSURE_ARG_POINTER(visitor
);
513 if (!mCacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
515 return mCacheEntry
->VisitMetaDataElements(visitor
);
519 /******************************************************************************
520 * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry
521 * open while referenced.
522 ******************************************************************************/
524 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsInputStreamWrapper
,
527 nsresult
nsCacheEntryDescriptor::
528 nsInputStreamWrapper::LazyInit()
530 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT
));
532 nsCacheAccessMode mode
;
533 nsresult rv
= mDescriptor
->GetAccessGranted(&mode
);
534 if (NS_FAILED(rv
)) return rv
;
536 NS_ENSURE_TRUE(mode
& nsICache::ACCESS_READ
, NS_ERROR_UNEXPECTED
);
538 nsCacheEntry
* cacheEntry
= mDescriptor
->CacheEntry();
539 if (!cacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
541 rv
= nsCacheService::OpenInputStreamForEntry(cacheEntry
, mode
,
543 getter_AddRefs(mInput
));
545 CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
546 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
547 mDescriptor
, this, mInput
.get(), int(rv
)));
549 if (NS_FAILED(rv
)) return rv
;
555 nsresult
nsCacheEntryDescriptor::
556 nsInputStreamWrapper::Close()
558 nsresult rv
= EnsureInit();
559 if (NS_FAILED(rv
)) return rv
;
561 return mInput
->Close();
564 nsresult
nsCacheEntryDescriptor::
565 nsInputStreamWrapper::Available(PRUint64
*avail
)
567 nsresult rv
= EnsureInit();
568 if (NS_FAILED(rv
)) return rv
;
570 return mInput
->Available(avail
);
573 nsresult
nsCacheEntryDescriptor::
574 nsInputStreamWrapper::Read(char *buf
, PRUint32 count
, PRUint32
*countRead
)
576 nsresult rv
= EnsureInit();
577 if (NS_SUCCEEDED(rv
))
578 rv
= mInput
->Read(buf
, count
, countRead
);
580 CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
581 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
582 mDescriptor
, this, mInput
.get(), rv
));
587 nsresult
nsCacheEntryDescriptor::
588 nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer
, void *closure
,
589 PRUint32 count
, PRUint32
*countRead
)
591 // cache stream not buffered
592 return NS_ERROR_NOT_IMPLEMENTED
;
595 nsresult
nsCacheEntryDescriptor::
596 nsInputStreamWrapper::IsNonBlocking(bool *result
)
598 // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
604 /******************************************************************************
605 * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses
606 ******************************************************************************/
608 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper
,
611 NS_IMETHODIMP
nsCacheEntryDescriptor::
612 nsDecompressInputStreamWrapper::Read(char * buf
,
619 if (!mStreamInitialized
) {
626 mZstream
.next_out
= (Bytef
*)buf
;
627 mZstream
.avail_out
= count
;
629 if (mReadBufferLen
< count
) {
630 // Allocate a buffer for reading from the input stream. This will
631 // determine the max number of compressed bytes read from the
632 // input stream at one time. Making the buffer size proportional
633 // to the request size is not necessary, but helps minimize the
634 // number of read requests to the input stream.
635 PRUint32 newBufLen
= NS_MAX(count
, (PRUint32
)kMinDecompressReadBufLen
);
636 unsigned char* newBuf
;
637 newBuf
= (unsigned char*)nsMemory::Realloc(mReadBuffer
,
640 mReadBuffer
= newBuf
;
641 mReadBufferLen
= newBufLen
;
645 return NS_ERROR_OUT_OF_MEMORY
;
649 // read and inflate data until the output buffer is full, or
650 // there is no more data to read
651 while (NS_SUCCEEDED(rv
) &&
653 mZstream
.avail_out
> 0 &&
655 if (mZstream
.avail_in
== 0) {
656 rv
= nsInputStreamWrapper::Read((char*)mReadBuffer
,
659 if (NS_FAILED(rv
) || !mZstream
.avail_in
) {
662 mZstream
.next_in
= mReadBuffer
;
664 zerr
= inflate(&mZstream
, Z_NO_FLUSH
);
665 if (zerr
== Z_STREAM_END
) {
666 // The compressed data may have been stored in multiple
667 // chunks/streams. To allow for this case, re-initialize
668 // the inflate stream and continue decompressing from
670 Bytef
* saveNextIn
= mZstream
.next_in
;
671 unsigned int saveAvailIn
= mZstream
.avail_in
;
672 Bytef
* saveNextOut
= mZstream
.next_out
;
673 unsigned int saveAvailOut
= mZstream
.avail_out
;
674 inflateReset(&mZstream
);
675 mZstream
.next_in
= saveNextIn
;
676 mZstream
.avail_in
= saveAvailIn
;
677 mZstream
.next_out
= saveNextOut
;
678 mZstream
.avail_out
= saveAvailOut
;
680 } else if (zerr
!= Z_OK
) {
681 rv
= NS_ERROR_INVALID_CONTENT_ENCODING
;
684 if (NS_SUCCEEDED(rv
)) {
685 *countRead
= count
- mZstream
.avail_out
;
690 nsresult
nsCacheEntryDescriptor::
691 nsDecompressInputStreamWrapper::Close()
695 nsMemory::Free(mReadBuffer
);
699 return nsInputStreamWrapper::Close();
702 nsresult
nsCacheEntryDescriptor::
703 nsDecompressInputStreamWrapper::InitZstream()
705 // Initialize zlib inflate stream
706 mZstream
.zalloc
= Z_NULL
;
707 mZstream
.zfree
= Z_NULL
;
708 mZstream
.opaque
= Z_NULL
;
709 mZstream
.next_out
= Z_NULL
;
710 mZstream
.avail_out
= 0;
711 mZstream
.next_in
= Z_NULL
;
712 mZstream
.avail_in
= 0;
713 if (inflateInit(&mZstream
) != Z_OK
) {
714 return NS_ERROR_FAILURE
;
716 mStreamInitialized
= true;
720 nsresult
nsCacheEntryDescriptor::
721 nsDecompressInputStreamWrapper::EndZstream()
723 if (mStreamInitialized
&& !mStreamEnded
) {
724 inflateEnd(&mZstream
);
731 /******************************************************************************
732 * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of
733 * data written to a cache entry.
734 * - also keeps the cache entry open while referenced.
735 ******************************************************************************/
737 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsOutputStreamWrapper
,
740 nsresult
nsCacheEntryDescriptor::
741 nsOutputStreamWrapper::LazyInit()
743 nsCacheServiceAutoLock
lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT
));
745 nsCacheAccessMode mode
;
746 nsresult rv
= mDescriptor
->GetAccessGranted(&mode
);
747 if (NS_FAILED(rv
)) return rv
;
749 NS_ENSURE_TRUE(mode
& nsICache::ACCESS_WRITE
, NS_ERROR_UNEXPECTED
);
751 nsCacheEntry
* cacheEntry
= mDescriptor
->CacheEntry();
752 if (!cacheEntry
) return NS_ERROR_NOT_AVAILABLE
;
754 NS_ASSERTION(mOutput
== nullptr, "mOutput set in LazyInit");
756 nsCOMPtr
<nsIOutputStream
> stream
;
757 rv
= nsCacheService::OpenOutputStreamForEntry(cacheEntry
, mode
, mStartOffset
,
758 getter_AddRefs(stream
));
762 nsCacheDevice
* device
= cacheEntry
->CacheDevice();
764 // the entry has been truncated to mStartOffset bytes, inform device
765 PRInt32 size
= cacheEntry
->DataSize();
766 rv
= device
->OnDataSizeChange(cacheEntry
, mStartOffset
- size
);
767 if (NS_SUCCEEDED(rv
))
768 cacheEntry
->SetDataSize(mStartOffset
);
770 rv
= NS_ERROR_NOT_AVAILABLE
;
773 // If anything above failed, clean up internal state and get out of here
774 // (see bug #654926)...
776 mDescriptor
->InternalCleanup(stream
);
780 // ... otherwise, set members and mark initialized
781 mDescriptor
->mOutput
= mOutput
= stream
;
786 nsresult
nsCacheEntryDescriptor::
787 nsOutputStreamWrapper::OnWrite(PRUint32 count
)
789 if (count
> PR_INT32_MAX
) return NS_ERROR_UNEXPECTED
;
790 return mDescriptor
->RequestDataSizeChange((PRInt32
)count
);
793 NS_IMETHODIMP
nsCacheEntryDescriptor::
794 nsOutputStreamWrapper::Close()
796 nsresult rv
= EnsureInit();
797 if (NS_FAILED(rv
)) return rv
;
799 return mOutput
->Close();
802 NS_IMETHODIMP
nsCacheEntryDescriptor::
803 nsOutputStreamWrapper::Flush()
805 nsresult rv
= EnsureInit();
806 if (NS_FAILED(rv
)) return rv
;
808 return mOutput
->Flush();
811 NS_IMETHODIMP
nsCacheEntryDescriptor::
812 nsOutputStreamWrapper::Write(const char * buf
,
816 nsresult rv
= EnsureInit();
817 if (NS_FAILED(rv
)) return rv
;
820 if (NS_FAILED(rv
)) return rv
;
822 return mOutput
->Write(buf
, count
, result
);
825 NS_IMETHODIMP
nsCacheEntryDescriptor::
826 nsOutputStreamWrapper::WriteFrom(nsIInputStream
* inStr
,
830 return NS_ERROR_NOT_IMPLEMENTED
;
833 NS_IMETHODIMP
nsCacheEntryDescriptor::
834 nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader
,
839 return NS_ERROR_NOT_IMPLEMENTED
;
842 NS_IMETHODIMP
nsCacheEntryDescriptor::
843 nsOutputStreamWrapper::IsNonBlocking(bool *result
)
845 // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
851 /******************************************************************************
852 * nsCompressOutputStreamWrapper - an output stream wrapper that compresses
853 * data before it is written
854 ******************************************************************************/
856 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper
,
859 NS_IMETHODIMP
nsCacheEntryDescriptor::
860 nsCompressOutputStreamWrapper::Write(const char * buf
,
867 if (!mStreamInitialized
) {
875 // Once allocated, this buffer is referenced by the zlib stream and
876 // cannot be grown. We use 2x(initial write request) to approximate
877 // a stream buffer size proportional to request buffers.
878 mWriteBufferLen
= NS_MAX(count
*2, (PRUint32
)kMinCompressWriteBufLen
);
879 mWriteBuffer
= (unsigned char*)nsMemory::Alloc(mWriteBufferLen
);
882 return NS_ERROR_OUT_OF_MEMORY
;
884 mZstream
.next_out
= mWriteBuffer
;
885 mZstream
.avail_out
= mWriteBufferLen
;
888 // Compress (deflate) the requested buffer. Keep going
889 // until the entire buffer has been deflated.
890 mZstream
.avail_in
= count
;
891 mZstream
.next_in
= (Bytef
*)buf
;
892 while (mZstream
.avail_in
> 0) {
893 zerr
= deflate(&mZstream
, Z_NO_FLUSH
);
894 if (zerr
== Z_STREAM_ERROR
) {
895 deflateEnd(&mZstream
);
896 mStreamInitialized
= false;
897 return NS_ERROR_FAILURE
;
899 // Note: Z_BUF_ERROR is non-fatal and sometimes expected here.
901 // If the compression stream output buffer is filled, write
902 // it out to the underlying stream wrapper.
903 if (mZstream
.avail_out
== 0) {
906 deflateEnd(&mZstream
);
907 mStreamInitialized
= false;
913 mUncompressedCount
+= *result
;
917 NS_IMETHODIMP
nsCacheEntryDescriptor::
918 nsCompressOutputStreamWrapper::Close()
923 if (mStreamInitialized
) {
924 // complete compression of any data remaining in the zlib stream
926 zerr
= deflate(&mZstream
, Z_FINISH
);
928 } while (zerr
== Z_OK
&& rv
== NS_OK
);
929 deflateEnd(&mZstream
);
932 if (mDescriptor
->CacheEntry()) {
933 nsCAutoString uncompressedLenStr
;
934 rv
= mDescriptor
->GetMetaDataElement("uncompressed-len",
935 getter_Copies(uncompressedLenStr
));
936 if (NS_SUCCEEDED(rv
)) {
937 PRInt32 oldCount
= uncompressedLenStr
.ToInteger(&rv
);
938 if (NS_SUCCEEDED(rv
)) {
939 mUncompressedCount
+= oldCount
;
942 uncompressedLenStr
.Adopt(0);
943 uncompressedLenStr
.AppendInt(mUncompressedCount
);
944 rv
= mDescriptor
->SetMetaDataElement("uncompressed-len",
945 uncompressedLenStr
.get());
949 nsMemory::Free(mWriteBuffer
);
954 return nsOutputStreamWrapper::Close();
957 nsresult
nsCacheEntryDescriptor::
958 nsCompressOutputStreamWrapper::InitZstream()
960 // Determine compression level: Aggressive compression
961 // may impact performance on mobile devices, while a
962 // lower compression level still provides substantial
963 // space savings for many text streams.
964 PRInt32 compressionLevel
= nsCacheService::CacheCompressionLevel();
966 // Initialize zlib deflate stream
967 mZstream
.zalloc
= Z_NULL
;
968 mZstream
.zfree
= Z_NULL
;
969 mZstream
.opaque
= Z_NULL
;
970 if (deflateInit2(&mZstream
, compressionLevel
, Z_DEFLATED
,
971 MAX_WBITS
, 8, Z_DEFAULT_STRATEGY
) != Z_OK
) {
972 return NS_ERROR_FAILURE
;
974 mZstream
.next_in
= Z_NULL
;
975 mZstream
.avail_in
= 0;
977 mStreamInitialized
= true;
982 nsresult
nsCacheEntryDescriptor::
983 nsCompressOutputStreamWrapper::WriteBuffer()
985 PRUint32 bytesToWrite
= mWriteBufferLen
- mZstream
.avail_out
;
987 nsresult rv
= nsCacheEntryDescriptor::nsOutputStreamWrapper::Write(
988 (const char *)mWriteBuffer
, bytesToWrite
, &result
);
989 mZstream
.next_out
= mWriteBuffer
;
990 mZstream
.avail_out
= mWriteBufferLen
;