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