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