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