netwerk/cache/nsCacheEntryDescriptor.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:52f8f8044645
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/. */
6
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>
17
18 #define kMinDecompressReadBufLen 1024
19 #define kMinCompressWriteBufLen 1024
20
21
22 /******************************************************************************
23 * nsAsyncDoomEvent
24 *****************************************************************************/
25
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 }
40
41 NS_IMETHOD Run()
42 {
43 nsresult status = NS_OK;
44
45 {
46 nsCacheServiceAutoLock lock(LOCK_TELEM(NSASYNCDOOMEVENT_RUN));
47
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 }
55
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 }
62
63 return NS_OK;
64 }
65
66 private:
67 nsRefPtr<nsCacheEntryDescriptor> mDescriptor;
68 nsICacheListener *mListener;
69 nsCOMPtr<nsIThread> mThread;
70 };
71
72
73 NS_IMPL_ISUPPORTS(nsCacheEntryDescriptor,
74 nsICacheEntryDescriptor,
75 nsICacheEntryInfo)
76
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 }
90
91
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();
101
102 NS_ASSERTION(mInputWrappers.Count() == 0,
103 "We have still some input wrapper!");
104 NS_ASSERTION(!mOutputWrapper, "We have still an output wrapper!");
105
106 nsCacheService * service = nsCacheService::GlobalInstance();
107 NS_RELEASE(service);
108 }
109
110
111 NS_IMETHODIMP
112 nsCacheEntryDescriptor::GetClientID(char ** result)
113 {
114 NS_ENSURE_ARG_POINTER(result);
115
116 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID));
117 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
118
119 return ClientIDFromCacheKey(*(mCacheEntry->Key()), result);
120 }
121
122
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;
129
130 const char* deviceID = mCacheEntry->GetDeviceID();
131 if (!deviceID) {
132 *aDeviceID = nullptr;
133 return NS_OK;
134 }
135
136 *aDeviceID = NS_strdup(deviceID);
137 return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
138 }
139
140
141 NS_IMETHODIMP
142 nsCacheEntryDescriptor::GetKey(nsACString &result)
143 {
144 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY));
145 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
146
147 return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result);
148 }
149
150
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;
157
158 *result = mCacheEntry->FetchCount();
159 return NS_OK;
160 }
161
162
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;
169
170 *result = mCacheEntry->LastFetched();
171 return NS_OK;
172 }
173
174
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;
181
182 *result = mCacheEntry->LastModified();
183 return NS_OK;
184 }
185
186
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;
193
194 *result = mCacheEntry->ExpirationTime();
195 return NS_OK;
196 }
197
198
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;
204
205 mCacheEntry->SetExpirationTime(expirationTime);
206 mCacheEntry->MarkEntryDirty();
207 return NS_OK;
208 }
209
210
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;
216
217 *result = mCacheEntry->IsStreamData();
218 return NS_OK;
219 }
220
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;
226
227 *result = mCacheEntry->PredictedDataSize();
228 return NS_OK;
229 }
230
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;
236
237 mCacheEntry->SetPredictedDataSize(predictedSize);
238 return NS_OK;
239 }
240
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;
246
247 const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len");
248 if (!val) {
249 *result = mCacheEntry->DataSize();
250 } else {
251 *result = atol(val);
252 }
253
254 return NS_OK;
255 }
256
257
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;
263
264 *result = mCacheEntry->DataSize();
265
266 return NS_OK;
267 }
268
269
270 nsresult
271 nsCacheEntryDescriptor::RequestDataSizeChange(int32_t deltaSize)
272 {
273 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE));
274 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
275
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 }
286
287
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;
293
294 // XXX review for signed/unsigned math errors
295 int32_t deltaSize = dataSize - mCacheEntry->DataSize();
296
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 }
308
309 return rv;
310 }
311
312
313 NS_IMETHODIMP
314 nsCacheEntryDescriptor::OpenInputStream(uint32_t offset, nsIInputStream ** result)
315 {
316 NS_ENSURE_ARG_POINTER(result);
317
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;
323
324 // Don't open any new stream when closing descriptor or clearing entries
325 if (mClosingDescriptor || nsCacheService::GetClearingEntries())
326 return NS_ERROR_NOT_AVAILABLE;
327
328 // ensure valid permissions
329 if (!(mAccessGranted & nsICache::ACCESS_READ))
330 return NS_ERROR_CACHE_READ_ACCESS_DENIED;
331
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;
340
341 mInputWrappers.AppendElement(cacheInput);
342 }
343
344 NS_ADDREF(*result = cacheInput);
345 return NS_OK;
346 }
347
348 NS_IMETHODIMP
349 nsCacheEntryDescriptor::OpenOutputStream(uint32_t offset, nsIOutputStream ** result)
350 {
351 NS_ENSURE_ARG_POINTER(result);
352
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;
358
359 // Don't open any new stream when closing descriptor or clearing entries
360 if (mClosingDescriptor || nsCacheService::GetClearingEntries())
361 return NS_ERROR_NOT_AVAILABLE;
362
363 // ensure valid permissions
364 if (!(mAccessGranted & nsICache::ACCESS_WRITE))
365 return NS_ERROR_CACHE_WRITE_ACCESS_DENIED;
366
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;
380
381 mOutputWrapper = cacheOutput;
382 }
383
384 NS_ADDREF(*result = cacheOutput);
385 return NS_OK;
386 }
387
388
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;
396
397 NS_IF_ADDREF(*result = mCacheEntry->Data());
398 return NS_OK;
399 }
400
401
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;
408
409 return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
410 }
411
412
413 NS_IMETHODIMP
414 nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result)
415 {
416 NS_ENSURE_ARG_POINTER(result);
417 *result = mAccessGranted;
418 return NS_OK;
419 }
420
421
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;
428
429 *result = mCacheEntry->StoragePolicy();
430 return NS_OK;
431 }
432
433
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?
440
441 bool storageEnabled = false;
442 storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy);
443 if (!storageEnabled) return NS_ERROR_FAILURE;
444
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;
448
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;
453
454 mCacheEntry->SetStoragePolicy(policy);
455 mCacheEntry->MarkEntryDirty();
456 return NS_OK;
457 }
458
459
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;
466
467 return nsCacheService::GetFileForEntry(mCacheEntry, result);
468 }
469
470
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;
477
478 *result = mCacheEntry->SecurityInfo();
479 NS_IF_ADDREF(*result);
480 return NS_OK;
481 }
482
483
484 NS_IMETHODIMP
485 nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo)
486 {
487 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO));
488 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
489
490 mCacheEntry->SetSecurityInfo(securityInfo);
491 mCacheEntry->MarkEntryDirty();
492 return NS_OK;
493 }
494
495
496 NS_IMETHODIMP
497 nsCacheEntryDescriptor::Doom()
498 {
499 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM));
500 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
501
502 return nsCacheService::DoomEntry(mCacheEntry);
503 }
504
505
506 NS_IMETHODIMP
507 nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status)
508 {
509 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS));
510 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
511
512 return NS_ERROR_NOT_IMPLEMENTED;
513 }
514
515
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 }
525
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 }
538
539 nsRefPtr<nsIRunnable> event = new nsAsyncDoomEvent(this, listener);
540 return nsCacheService::DispatchToCacheIOThread(event);
541 }
542
543
544 NS_IMETHODIMP
545 nsCacheEntryDescriptor::MarkValid()
546 {
547 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID));
548 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
549
550 nsresult rv = nsCacheService::ValidateEntry(mCacheEntry);
551 return rv;
552 }
553
554
555 NS_IMETHODIMP
556 nsCacheEntryDescriptor::Close()
557 {
558 nsRefPtr<nsOutputStreamWrapper> outputWrapper;
559 nsTArray<nsRefPtr<nsInputStreamWrapper> > inputWrappers;
560
561 {
562 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE));
563 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
564
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 }
572
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 }
583
584 for (uint32_t i = 0 ; i < inputWrappers.Length() ; i++)
585 inputWrappers[i]->Close();
586
587 inputWrappers.Clear();
588
589 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE));
590 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
591
592 // XXX perhaps closing descriptors should clear/sever transports
593
594 // tell nsCacheService we're going away
595 nsCacheService::CloseDescriptor(this);
596 NS_ASSERTION(mCacheEntry == nullptr, "mCacheEntry not null");
597
598 return NS_OK;
599 }
600
601
602 NS_IMETHODIMP
603 nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result)
604 {
605 NS_ENSURE_ARG_POINTER(key);
606 *result = nullptr;
607
608 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT));
609 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
610
611 const char *value;
612
613 value = mCacheEntry->GetMetaDataElement(key);
614 if (!value) return NS_ERROR_NOT_AVAILABLE;
615
616 *result = NS_strdup(value);
617 if (!*result) return NS_ERROR_OUT_OF_MEMORY;
618
619 return NS_OK;
620 }
621
622
623 NS_IMETHODIMP
624 nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value)
625 {
626 NS_ENSURE_ARG_POINTER(key);
627
628 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT));
629 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
630
631 // XXX allow null value, for clearing key?
632
633 nsresult rv = mCacheEntry->SetMetaDataElement(key, value);
634 if (NS_SUCCEEDED(rv))
635 mCacheEntry->TouchMetaData();
636 return rv;
637 }
638
639
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;
647
648 return mCacheEntry->VisitMetaDataElements(visitor);
649 }
650
651
652 /******************************************************************************
653 * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry
654 * open while referenced.
655 ******************************************************************************/
656
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;
664
665 {
666 mozilla::MutexAutoLock lock(mLock);
667 desc = mDescriptor;
668 }
669
670 if (desc)
671 nsCacheService::Lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_RELEASE));
672
673 nsrefcnt count;
674 NS_PRECONDITION(0 != mRefCnt, "dup release");
675 count = --mRefCnt;
676 NS_LOG_RELEASE(this, count, "nsCacheEntryDescriptor::nsInputStreamWrapper");
677
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 }
685
686 if (desc)
687 nsCacheService::Unlock();
688
689 mRefCnt = 1;
690 delete (this);
691 return 0;
692 }
693
694 if (desc)
695 nsCacheService::Unlock();
696
697 return count;
698 }
699
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
704
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;
712
713 nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT));
714
715 nsCacheAccessMode mode;
716 nsresult rv = mDescriptor->GetAccessGranted(&mode);
717 if (NS_FAILED(rv)) return rv;
718
719 NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
720
721 nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
722 if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
723
724 rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
725 mStartOffset,
726 getter_AddRefs(mInput));
727
728 CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
729 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
730 mDescriptor, this, mInput.get(), int(rv)));
731
732 if (NS_FAILED(rv)) return rv;
733
734 mInitialized = true;
735 return NS_OK;
736 }
737
738 nsresult nsCacheEntryDescriptor::
739 nsInputStreamWrapper::EnsureInit()
740 {
741 if (mInitialized) {
742 NS_ASSERTION(mDescriptor, "Bad state");
743 return NS_OK;
744 }
745
746 return LazyInit();
747 }
748
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 }
758
759 nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_CLOSEINTERNAL));
760
761 if (mDescriptor) {
762 mDescriptor->mInputWrappers.RemoveElement(this);
763 nsCacheService::ReleaseObject_Locked(mDescriptor);
764 mDescriptor = nullptr;
765 }
766 mInitialized = false;
767 mInput = nullptr;
768 }
769
770 nsresult nsCacheEntryDescriptor::
771 nsInputStreamWrapper::Close()
772 {
773 mozilla::MutexAutoLock lock(mLock);
774
775 return Close_Locked();
776 }
777
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 }
788
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 }
794
795 nsresult nsCacheEntryDescriptor::
796 nsInputStreamWrapper::Available(uint64_t *avail)
797 {
798 mozilla::MutexAutoLock lock(mLock);
799
800 nsresult rv = EnsureInit();
801 if (NS_FAILED(rv)) return rv;
802
803 return mInput->Available(avail);
804 }
805
806 nsresult nsCacheEntryDescriptor::
807 nsInputStreamWrapper::Read(char *buf, uint32_t count, uint32_t *countRead)
808 {
809 mozilla::MutexAutoLock lock(mLock);
810
811 return Read_Locked(buf, count, countRead);
812 }
813
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);
820
821 CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
822 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
823 mDescriptor, this, mInput.get(), rv));
824
825 return rv;
826 }
827
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 }
835
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 }
843
844
845 /******************************************************************************
846 * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses
847 ******************************************************************************/
848
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;
856
857 {
858 mozilla::MutexAutoLock lock(mLock);
859 desc = mDescriptor;
860 }
861
862 if (desc)
863 nsCacheService::Lock(LOCK_TELEM(
864 NSDECOMPRESSINPUTSTREAMWRAPPER_RELEASE));
865
866 nsrefcnt count;
867 NS_PRECONDITION(0 != mRefCnt, "dup release");
868 count = --mRefCnt;
869 NS_LOG_RELEASE(this, count,
870 "nsCacheEntryDescriptor::nsDecompressInputStreamWrapper");
871
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 }
879
880 if (desc)
881 nsCacheService::Unlock();
882
883 mRefCnt = 1;
884 delete (this);
885 return 0;
886 }
887
888 if (desc)
889 nsCacheService::Unlock();
890
891 return count;
892 }
893
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
898
899 NS_IMETHODIMP nsCacheEntryDescriptor::
900 nsDecompressInputStreamWrapper::Read(char * buf,
901 uint32_t count,
902 uint32_t *countRead)
903 {
904 mozilla::MutexAutoLock lock(mLock);
905
906 int zerr = Z_OK;
907 nsresult rv = NS_OK;
908
909 if (!mStreamInitialized) {
910 rv = InitZstream();
911 if (NS_FAILED(rv)) {
912 return rv;
913 }
914 }
915
916 mZstream.next_out = (Bytef*)buf;
917 mZstream.avail_out = count;
918
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 }
938
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 }
979
980 nsresult nsCacheEntryDescriptor::
981 nsDecompressInputStreamWrapper::Close()
982 {
983 mozilla::MutexAutoLock lock(mLock);
984
985 if (!mDescriptor)
986 return NS_ERROR_NOT_AVAILABLE;
987
988 EndZstream();
989 if (mReadBuffer) {
990 nsMemory::Free(mReadBuffer);
991 mReadBuffer = 0;
992 mReadBufferLen = 0;
993 }
994 return nsInputStreamWrapper::Close_Locked();
995 }
996
997 nsresult nsCacheEntryDescriptor::
998 nsDecompressInputStreamWrapper::InitZstream()
999 {
1000 if (!mDescriptor)
1001 return NS_ERROR_NOT_AVAILABLE;
1002
1003 if (mStreamEnded)
1004 return NS_ERROR_FAILURE;
1005
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;
1016 }
1017 mStreamInitialized = true;
1018 return NS_OK;
1019 }
1020
1021 nsresult nsCacheEntryDescriptor::
1022 nsDecompressInputStreamWrapper::EndZstream()
1023 {
1024 if (mStreamInitialized && !mStreamEnded) {
1025 inflateEnd(&mZstream);
1026 mStreamInitialized = false;
1027 mStreamEnded = true;
1028 }
1029 return NS_OK;
1030 }
1031
1032
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 ******************************************************************************/
1038
1039 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsOutputStreamWrapper)
1040 NS_IMETHODIMP_(MozExternalRefCountType)
1041 nsCacheEntryDescriptor::nsOutputStreamWrapper::Release()
1042 {
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;
1046
1047 {
1048 mozilla::MutexAutoLock lock(mLock);
1049 desc = mDescriptor;
1050 }
1051
1052 if (desc)
1053 nsCacheService::Lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_RELEASE));
1054
1055 nsrefcnt count;
1056 NS_PRECONDITION(0 != mRefCnt, "dup release");
1057 count = --mRefCnt;
1058 NS_LOG_RELEASE(this, count,
1059 "nsCacheEntryDescriptor::nsOutputStreamWrapper");
1060
1061 if (0 == count) {
1062 // don't use desc here since mDescriptor might be already nulled out
1063 if (mDescriptor)
1064 mDescriptor->mOutputWrapper = nullptr;
1065
1066 if (desc)
1067 nsCacheService::Unlock();
1068
1069 mRefCnt = 1;
1070 delete (this);
1071 return 0;
1072 }
1073
1074 if (desc)
1075 nsCacheService::Unlock();
1076
1077 return count;
1078 }
1079
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
1084
1085 nsresult nsCacheEntryDescriptor::
1086 nsOutputStreamWrapper::LazyInit()
1087 {
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;
1092
1093 nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT));
1094
1095 nsCacheAccessMode mode;
1096 nsresult rv = mDescriptor->GetAccessGranted(&mode);
1097 if (NS_FAILED(rv)) return rv;
1098
1099 NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED);
1100
1101 nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
1102 if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
1103
1104 NS_ASSERTION(mOutput == nullptr, "mOutput set in LazyInit");
1105
1106 nsCOMPtr<nsIOutputStream> stream;
1107 rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset,
1108 getter_AddRefs(stream));
1109 if (NS_FAILED(rv))
1110 return rv;
1111
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;
1121 }
1122
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;
1132 }
1133
1134 mOutput = stream;
1135 mInitialized = true;
1136 return NS_OK;
1137 }
1138
1139 nsresult nsCacheEntryDescriptor::
1140 nsOutputStreamWrapper::EnsureInit()
1141 {
1142 if (mInitialized) {
1143 NS_ASSERTION(mDescriptor, "Bad state");
1144 return NS_OK;
1145 }
1146
1147 return LazyInit();
1148 }
1149
1150 nsresult nsCacheEntryDescriptor::
1151 nsOutputStreamWrapper::OnWrite(uint32_t count)
1152 {
1153 if (count > INT32_MAX) return NS_ERROR_UNEXPECTED;
1154 return mDescriptor->RequestDataSizeChange((int32_t)count);
1155 }
1156
1157 void nsCacheEntryDescriptor::
1158 nsOutputStreamWrapper::CloseInternal()
1159 {
1160 mLock.AssertCurrentThreadOwns();
1161 if (!mDescriptor) {
1162 NS_ASSERTION(!mInitialized, "Bad state");
1163 NS_ASSERTION(!mOutput, "Bad state");
1164 return;
1165 }
1166
1167 nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_CLOSEINTERNAL));
1168
1169 if (mDescriptor) {
1170 mDescriptor->mOutputWrapper = nullptr;
1171 nsCacheService::ReleaseObject_Locked(mDescriptor);
1172 mDescriptor = nullptr;
1173 }
1174 mInitialized = false;
1175 mOutput = nullptr;
1176 }
1177
1178
1179 NS_IMETHODIMP nsCacheEntryDescriptor::
1180 nsOutputStreamWrapper::Close()
1181 {
1182 mozilla::MutexAutoLock lock(mLock);
1183
1184 return Close_Locked();
1185 }
1186
1187 nsresult nsCacheEntryDescriptor::
1188 nsOutputStreamWrapper::Close_Locked()
1189 {
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");
1196 }
1197
1198 // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are
1199 // closing streams with nsCacheService::CloseAllStream()
1200 CloseInternal();
1201 return rv;
1202 }
1203
1204 NS_IMETHODIMP nsCacheEntryDescriptor::
1205 nsOutputStreamWrapper::Flush()
1206 {
1207 mozilla::MutexAutoLock lock(mLock);
1208
1209 nsresult rv = EnsureInit();
1210 if (NS_FAILED(rv)) return rv;
1211
1212 return mOutput->Flush();
1213 }
1214
1215 NS_IMETHODIMP nsCacheEntryDescriptor::
1216 nsOutputStreamWrapper::Write(const char * buf,
1217 uint32_t count,
1218 uint32_t * result)
1219 {
1220 mozilla::MutexAutoLock lock(mLock);
1221 return Write_Locked(buf, count, result);
1222 }
1223
1224 nsresult nsCacheEntryDescriptor::
1225 nsOutputStreamWrapper::Write_Locked(const char * buf,
1226 uint32_t count,
1227 uint32_t * result)
1228 {
1229 nsresult rv = EnsureInit();
1230 if (NS_FAILED(rv)) return rv;
1231
1232 rv = OnWrite(count);
1233 if (NS_FAILED(rv)) return rv;
1234
1235 return mOutput->Write(buf, count, result);
1236 }
1237
1238 NS_IMETHODIMP nsCacheEntryDescriptor::
1239 nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr,
1240 uint32_t count,
1241 uint32_t * result)
1242 {
1243 return NS_ERROR_NOT_IMPLEMENTED;
1244 }
1245
1246 NS_IMETHODIMP nsCacheEntryDescriptor::
1247 nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader,
1248 void * closure,
1249 uint32_t count,
1250 uint32_t * result)
1251 {
1252 return NS_ERROR_NOT_IMPLEMENTED;
1253 }
1254
1255 NS_IMETHODIMP nsCacheEntryDescriptor::
1256 nsOutputStreamWrapper::IsNonBlocking(bool *result)
1257 {
1258 // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
1259 *result = false;
1260 return NS_OK;
1261 }
1262
1263
1264 /******************************************************************************
1265 * nsCompressOutputStreamWrapper - an output stream wrapper that compresses
1266 * data before it is written
1267 ******************************************************************************/
1268
1269 NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper)
1270 NS_IMETHODIMP_(MozExternalRefCountType)
1271 nsCacheEntryDescriptor::nsCompressOutputStreamWrapper::Release()
1272 {
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;
1276
1277 {
1278 mozilla::MutexAutoLock lock(mLock);
1279 desc = mDescriptor;
1280 }
1281
1282 if (desc)
1283 nsCacheService::Lock(LOCK_TELEM(NSCOMPRESSOUTPUTSTREAMWRAPPER_RELEASE));
1284
1285 nsrefcnt count;
1286 NS_PRECONDITION(0 != mRefCnt, "dup release");
1287 count = --mRefCnt;
1288 NS_LOG_RELEASE(this, count,
1289 "nsCacheEntryDescriptor::nsCompressOutputStreamWrapper");
1290
1291 if (0 == count) {
1292 // don't use desc here since mDescriptor might be already nulled out
1293 if (mDescriptor)
1294 mDescriptor->mOutputWrapper = nullptr;
1295
1296 if (desc)
1297 nsCacheService::Unlock();
1298
1299 mRefCnt = 1;
1300 delete (this);
1301 return 0;
1302 }
1303
1304 if (desc)
1305 nsCacheService::Unlock();
1306
1307 return count;
1308 }
1309
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
1314
1315 NS_IMETHODIMP nsCacheEntryDescriptor::
1316 nsCompressOutputStreamWrapper::Write(const char * buf,
1317 uint32_t count,
1318 uint32_t * result)
1319 {
1320 mozilla::MutexAutoLock lock(mLock);
1321
1322 int zerr = Z_OK;
1323 nsresult rv = NS_OK;
1324
1325 if (!mStreamInitialized) {
1326 rv = InitZstream();
1327 if (NS_FAILED(rv)) {
1328 return rv;
1329 }
1330 }
1331
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;
1341 }
1342 mZstream.next_out = mWriteBuffer;
1343 mZstream.avail_out = mWriteBufferLen;
1344 }
1345
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;
1357 }
1358 // Note: Z_BUF_ERROR is non-fatal and sometimes expected here.
1359
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;
1369 }
1370 }
1371 }
1372 *result = count;
1373 mUncompressedCount += *result;
1374 return NS_OK;
1375 }
1376
1377 NS_IMETHODIMP nsCacheEntryDescriptor::
1378 nsCompressOutputStreamWrapper::Close()
1379 {
1380 mozilla::MutexAutoLock lock(mLock);
1381
1382 if (!mDescriptor)
1383 return NS_ERROR_NOT_AVAILABLE;
1384
1385 nsresult retval = NS_OK;
1386 nsresult rv;
1387 int zerr = 0;
1388
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;
1399 }
1400 // Do not allow to initialize stream after calling Close().
1401 mStreamEnded = true;
1402
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;
1411 }
1412 }
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;
1419 }
1420
1421 if (mWriteBuffer) {
1422 nsMemory::Free(mWriteBuffer);
1423 mWriteBuffer = 0;
1424 mWriteBufferLen = 0;
1425 }
1426
1427 rv = nsOutputStreamWrapper::Close_Locked();
1428 if (NS_FAILED(rv))
1429 retval = rv;
1430
1431 return retval;
1432 }
1433
1434 nsresult nsCacheEntryDescriptor::
1435 nsCompressOutputStreamWrapper::InitZstream()
1436 {
1437 if (!mDescriptor)
1438 return NS_ERROR_NOT_AVAILABLE;
1439
1440 if (mStreamEnded)
1441 return NS_ERROR_FAILURE;
1442
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();
1448
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;
1456 }
1457 mZstream.next_in = Z_NULL;
1458 mZstream.avail_in = 0;
1459
1460 mStreamInitialized = true;
1461
1462 return NS_OK;
1463 }
1464
1465 nsresult nsCacheEntryDescriptor::
1466 nsCompressOutputStreamWrapper::WriteBuffer()
1467 {
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;
1475 }
1476

mercurial