1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is nsCacheEntryDescriptor.cpp, released
17 : * February 22, 2001.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2001
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Gordon Sheridan, 22-February-2001
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsICache.h"
42 : #include "nsCache.h"
43 : #include "nsCacheService.h"
44 : #include "nsCacheEntryDescriptor.h"
45 : #include "nsCacheEntry.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsIOutputStream.h"
48 : #include "nsCRT.h"
49 :
50 : #define kMinDecompressReadBufLen 1024
51 : #define kMinCompressWriteBufLen 1024
52 :
53 17618 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsCacheEntryDescriptor,
54 : nsICacheEntryDescriptor,
55 : nsICacheEntryInfo)
56 :
57 1778 : nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry,
58 : nsCacheAccessMode accessGranted)
59 : : mCacheEntry(entry),
60 : mAccessGranted(accessGranted),
61 1778 : mOutput(nsnull)
62 : {
63 1778 : PR_INIT_CLIST(this);
64 1778 : NS_ADDREF(nsCacheService::GlobalInstance()); // ensure it lives for the lifetime of the descriptor
65 1778 : }
66 :
67 :
68 3556 : nsCacheEntryDescriptor::~nsCacheEntryDescriptor()
69 : {
70 : // No need to close if the cache entry has already been severed. This
71 : // helps avoid a shutdown assertion (bug 285519) that is caused when
72 : // consumers end up holding onto these objects past xpcom-shutdown. It's
73 : // okay for them to do that because the cache service calls our Close
74 : // method during xpcom-shutdown, so we don't need to complain about it.
75 1778 : if (mCacheEntry)
76 1727 : Close();
77 :
78 1778 : nsCacheService * service = nsCacheService::GlobalInstance();
79 1778 : NS_RELEASE(service);
80 7112 : }
81 :
82 :
83 : NS_IMETHODIMP
84 0 : nsCacheEntryDescriptor::GetClientID(char ** result)
85 : {
86 0 : NS_ENSURE_ARG_POINTER(result);
87 :
88 0 : nsCacheServiceAutoLock lock;
89 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
90 :
91 0 : return ClientIDFromCacheKey(*(mCacheEntry->Key()), result);
92 : }
93 :
94 :
95 : NS_IMETHODIMP
96 1488 : nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID)
97 : {
98 1488 : NS_ENSURE_ARG_POINTER(aDeviceID);
99 2976 : nsCacheServiceAutoLock lock;
100 1488 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
101 :
102 1488 : const char* deviceID = mCacheEntry->GetDeviceID();
103 1488 : if (!deviceID) {
104 901 : *aDeviceID = nsnull;
105 901 : return NS_OK;
106 : }
107 :
108 587 : *aDeviceID = NS_strdup(deviceID);
109 587 : return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
110 : }
111 :
112 :
113 : NS_IMETHODIMP
114 0 : nsCacheEntryDescriptor::GetKey(nsACString &result)
115 : {
116 0 : nsCacheServiceAutoLock lock;
117 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
118 :
119 0 : return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result);
120 : }
121 :
122 :
123 : NS_IMETHODIMP
124 0 : nsCacheEntryDescriptor::GetFetchCount(PRInt32 *result)
125 : {
126 0 : NS_ENSURE_ARG_POINTER(result);
127 0 : nsCacheServiceAutoLock lock;
128 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
129 :
130 0 : *result = mCacheEntry->FetchCount();
131 0 : return NS_OK;
132 : }
133 :
134 :
135 : NS_IMETHODIMP
136 0 : nsCacheEntryDescriptor::GetLastFetched(PRUint32 *result)
137 : {
138 0 : NS_ENSURE_ARG_POINTER(result);
139 0 : nsCacheServiceAutoLock lock;
140 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
141 :
142 0 : *result = mCacheEntry->LastFetched();
143 0 : return NS_OK;
144 : }
145 :
146 :
147 : NS_IMETHODIMP
148 567 : nsCacheEntryDescriptor::GetLastModified(PRUint32 *result)
149 : {
150 567 : NS_ENSURE_ARG_POINTER(result);
151 1134 : nsCacheServiceAutoLock lock;
152 567 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
153 :
154 567 : *result = mCacheEntry->LastModified();
155 567 : return NS_OK;
156 : }
157 :
158 :
159 : NS_IMETHODIMP
160 289 : nsCacheEntryDescriptor::GetExpirationTime(PRUint32 *result)
161 : {
162 289 : NS_ENSURE_ARG_POINTER(result);
163 578 : nsCacheServiceAutoLock lock;
164 289 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
165 :
166 289 : *result = mCacheEntry->ExpirationTime();
167 289 : return NS_OK;
168 : }
169 :
170 :
171 : NS_IMETHODIMP
172 1357 : nsCacheEntryDescriptor::SetExpirationTime(PRUint32 expirationTime)
173 : {
174 2714 : nsCacheServiceAutoLock lock;
175 1357 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
176 :
177 1357 : mCacheEntry->SetExpirationTime(expirationTime);
178 1357 : mCacheEntry->MarkEntryDirty();
179 1357 : return NS_OK;
180 : }
181 :
182 :
183 0 : NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(bool *result)
184 : {
185 0 : NS_ENSURE_ARG_POINTER(result);
186 0 : nsCacheServiceAutoLock lock;
187 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
188 :
189 0 : *result = mCacheEntry->IsStreamData();
190 0 : return NS_OK;
191 : }
192 :
193 0 : NS_IMETHODIMP nsCacheEntryDescriptor::GetPredictedDataSize(PRInt64 *result)
194 : {
195 0 : NS_ENSURE_ARG_POINTER(result);
196 0 : nsCacheServiceAutoLock lock;
197 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
198 :
199 0 : *result = mCacheEntry->PredictedDataSize();
200 0 : return NS_OK;
201 : }
202 :
203 1363 : NS_IMETHODIMP nsCacheEntryDescriptor::SetPredictedDataSize(PRInt64
204 : predictedSize)
205 : {
206 2726 : nsCacheServiceAutoLock lock;
207 1363 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
208 :
209 1363 : mCacheEntry->SetPredictedDataSize(predictedSize);
210 1363 : return NS_OK;
211 : }
212 :
213 536 : NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(PRUint32 *result)
214 : {
215 536 : NS_ENSURE_ARG_POINTER(result);
216 1072 : nsCacheServiceAutoLock lock;
217 536 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
218 :
219 536 : const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len");
220 536 : if (!val) {
221 536 : *result = mCacheEntry->DataSize();
222 : } else {
223 0 : *result = atol(val);
224 : }
225 :
226 536 : return NS_OK;
227 : }
228 :
229 :
230 2 : NS_IMETHODIMP nsCacheEntryDescriptor::GetStorageDataSize(PRUint32 *result)
231 : {
232 2 : NS_ENSURE_ARG_POINTER(result);
233 4 : nsCacheServiceAutoLock lock;
234 2 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
235 :
236 2 : *result = mCacheEntry->DataSize();
237 :
238 2 : return NS_OK;
239 : }
240 :
241 :
242 : nsresult
243 1496 : nsCacheEntryDescriptor::RequestDataSizeChange(PRInt32 deltaSize)
244 : {
245 2992 : nsCacheServiceAutoLock lock;
246 1496 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
247 :
248 : nsresult rv;
249 1496 : rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
250 1496 : if (NS_SUCCEEDED(rv)) {
251 : // XXX review for signed/unsigned math errors
252 1496 : PRUint32 newDataSize = mCacheEntry->DataSize() + deltaSize;
253 1496 : mCacheEntry->SetDataSize(newDataSize);
254 1496 : mCacheEntry->TouchData();
255 : }
256 1496 : return rv;
257 : }
258 :
259 :
260 : NS_IMETHODIMP
261 0 : nsCacheEntryDescriptor::SetDataSize(PRUint32 dataSize)
262 : {
263 0 : nsCacheServiceAutoLock lock;
264 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
265 :
266 : // XXX review for signed/unsigned math errors
267 0 : PRInt32 deltaSize = dataSize - mCacheEntry->DataSize();
268 :
269 : nsresult rv;
270 0 : rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
271 : // this had better be NS_OK, this call instance is advisory for memory cache objects
272 0 : if (NS_SUCCEEDED(rv)) {
273 : // XXX review for signed/unsigned math errors
274 0 : PRUint32 newDataSize = mCacheEntry->DataSize() + deltaSize;
275 0 : mCacheEntry->SetDataSize(newDataSize);
276 0 : mCacheEntry->TouchData();
277 : } else {
278 0 : NS_WARNING("failed SetDataSize() on memory cache object!");
279 : }
280 :
281 0 : return rv;
282 : }
283 :
284 :
285 : NS_IMETHODIMP
286 242 : nsCacheEntryDescriptor::OpenInputStream(PRUint32 offset, nsIInputStream ** result)
287 : {
288 242 : NS_ENSURE_ARG_POINTER(result);
289 :
290 : {
291 484 : nsCacheServiceAutoLock lock;
292 242 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
293 242 : if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
294 :
295 : // ensure valid permissions
296 242 : if (!(mAccessGranted & nsICache::ACCESS_READ))
297 0 : return NS_ERROR_CACHE_READ_ACCESS_DENIED;
298 : }
299 :
300 242 : nsInputStreamWrapper* cacheInput = nsnull;
301 : const char *val;
302 242 : val = mCacheEntry->GetMetaDataElement("uncompressed-len");
303 242 : if (val) {
304 0 : cacheInput = new nsDecompressInputStreamWrapper(this, offset);
305 : } else {
306 242 : cacheInput = new nsInputStreamWrapper(this, offset);
307 : }
308 242 : if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY;
309 :
310 242 : NS_ADDREF(*result = cacheInput);
311 242 : return NS_OK;
312 : }
313 :
314 : NS_IMETHODIMP
315 1194 : nsCacheEntryDescriptor::OpenOutputStream(PRUint32 offset, nsIOutputStream ** result)
316 : {
317 1194 : NS_ENSURE_ARG_POINTER(result);
318 :
319 : {
320 2388 : nsCacheServiceAutoLock lock;
321 1194 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
322 1194 : if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
323 :
324 : // ensure valid permissions
325 1194 : if (!(mAccessGranted & nsICache::ACCESS_WRITE))
326 0 : return NS_ERROR_CACHE_WRITE_ACCESS_DENIED;
327 : }
328 :
329 1194 : nsOutputStreamWrapper* cacheOutput = nsnull;
330 1194 : PRInt32 compressionLevel = nsCacheService::CacheCompressionLevel();
331 : const char *val;
332 1194 : val = mCacheEntry->GetMetaDataElement("uncompressed-len");
333 1194 : if ((compressionLevel > 0) && val) {
334 0 : cacheOutput = new nsCompressOutputStreamWrapper(this, offset);
335 : } else {
336 : // clear compression flag when compression disabled - see bug #715198
337 1194 : if (val) {
338 903 : mCacheEntry->SetMetaDataElement("uncompressed-len", nsnull);
339 : }
340 1194 : cacheOutput = new nsOutputStreamWrapper(this, offset);
341 : }
342 1194 : if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY;
343 :
344 1194 : NS_ADDREF(*result = cacheOutput);
345 1194 : return NS_OK;
346 : }
347 :
348 :
349 : NS_IMETHODIMP
350 0 : nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result)
351 : {
352 0 : NS_ENSURE_ARG_POINTER(result);
353 0 : nsCacheServiceAutoLock lock;
354 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
355 0 : if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM;
356 :
357 0 : NS_IF_ADDREF(*result = mCacheEntry->Data());
358 0 : return NS_OK;
359 : }
360 :
361 :
362 : NS_IMETHODIMP
363 0 : nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement)
364 : {
365 0 : nsCacheServiceAutoLock lock;
366 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
367 0 : if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM;
368 :
369 0 : return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
370 : }
371 :
372 :
373 : NS_IMETHODIMP
374 1580 : nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result)
375 : {
376 1580 : NS_ENSURE_ARG_POINTER(result);
377 1580 : *result = mAccessGranted;
378 1580 : return NS_OK;
379 : }
380 :
381 :
382 : NS_IMETHODIMP
383 2244 : nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result)
384 : {
385 2244 : NS_ENSURE_ARG_POINTER(result);
386 4488 : nsCacheServiceAutoLock lock;
387 2244 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
388 :
389 2244 : *result = mCacheEntry->StoragePolicy();
390 2244 : return NS_OK;
391 : }
392 :
393 :
394 : NS_IMETHODIMP
395 6 : nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy)
396 : {
397 12 : nsCacheServiceAutoLock lock;
398 6 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
399 : // XXX validate policy against session?
400 :
401 6 : bool storageEnabled = false;
402 6 : storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy);
403 6 : if (!storageEnabled) return NS_ERROR_FAILURE;
404 :
405 : // Don't change the storage policy of entries we can't write
406 5 : if (!(mAccessGranted & nsICache::ACCESS_WRITE))
407 0 : return NS_ERROR_NOT_AVAILABLE;
408 :
409 : // Don't allow a cache entry to move from memory-only to anything else
410 5 : if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY &&
411 : policy != nsICache::STORE_IN_MEMORY)
412 0 : return NS_ERROR_NOT_AVAILABLE;
413 :
414 5 : mCacheEntry->SetStoragePolicy(policy);
415 5 : mCacheEntry->MarkEntryDirty();
416 5 : return NS_OK;
417 : }
418 :
419 :
420 : NS_IMETHODIMP
421 0 : nsCacheEntryDescriptor::GetFile(nsIFile ** result)
422 : {
423 0 : NS_ENSURE_ARG_POINTER(result);
424 0 : nsCacheServiceAutoLock lock;
425 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
426 :
427 0 : return nsCacheService::GetFileForEntry(mCacheEntry, result);
428 : }
429 :
430 :
431 : NS_IMETHODIMP
432 228 : nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result)
433 : {
434 228 : NS_ENSURE_ARG_POINTER(result);
435 456 : nsCacheServiceAutoLock lock;
436 228 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
437 :
438 228 : *result = mCacheEntry->SecurityInfo();
439 228 : NS_IF_ADDREF(*result);
440 228 : return NS_OK;
441 : }
442 :
443 :
444 : NS_IMETHODIMP
445 4 : nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo)
446 : {
447 8 : nsCacheServiceAutoLock lock;
448 4 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
449 :
450 4 : mCacheEntry->SetSecurityInfo(securityInfo);
451 4 : mCacheEntry->MarkEntryDirty();
452 4 : return NS_OK;
453 : }
454 :
455 :
456 : NS_IMETHODIMP
457 252 : nsCacheEntryDescriptor::Doom()
458 : {
459 504 : nsCacheServiceAutoLock lock;
460 252 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
461 :
462 244 : return nsCacheService::DoomEntry(mCacheEntry);
463 : }
464 :
465 :
466 : NS_IMETHODIMP
467 0 : nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status)
468 : {
469 0 : nsCacheServiceAutoLock lock;
470 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
471 :
472 0 : return NS_ERROR_NOT_IMPLEMENTED;
473 : }
474 :
475 :
476 : NS_IMETHODIMP
477 212 : nsCacheEntryDescriptor::MarkValid()
478 : {
479 424 : nsCacheServiceAutoLock lock;
480 212 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
481 :
482 212 : nsresult rv = nsCacheService::ValidateEntry(mCacheEntry);
483 212 : return rv;
484 : }
485 :
486 :
487 : NS_IMETHODIMP
488 1770 : nsCacheEntryDescriptor::Close()
489 : {
490 3540 : nsCacheServiceAutoLock lock;
491 1770 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
492 :
493 : // XXX perhaps closing descriptors should clear/sever transports
494 :
495 : // tell nsCacheService we're going away
496 1770 : nsCacheService::CloseDescriptor(this);
497 1770 : NS_ASSERTION(mCacheEntry == nsnull, "mCacheEntry not null");
498 :
499 1770 : return NS_OK;
500 : }
501 :
502 :
503 : NS_IMETHODIMP
504 1395 : nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result)
505 : {
506 1395 : NS_ENSURE_ARG_POINTER(key);
507 1395 : *result = nsnull;
508 :
509 2790 : nsCacheServiceAutoLock lock;
510 1395 : NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
511 :
512 : const char *value;
513 :
514 1395 : value = mCacheEntry->GetMetaDataElement(key);
515 1395 : if (!value) return NS_ERROR_NOT_AVAILABLE;
516 :
517 1177 : *result = NS_strdup(value);
518 1177 : if (!*result) return NS_ERROR_OUT_OF_MEMORY;
519 :
520 1177 : return NS_OK;
521 : }
522 :
523 :
524 : NS_IMETHODIMP
525 3941 : nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value)
526 : {
527 3941 : NS_ENSURE_ARG_POINTER(key);
528 :
529 7882 : nsCacheServiceAutoLock lock;
530 3941 : NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
531 :
532 : // XXX allow null value, for clearing key?
533 :
534 3941 : nsresult rv = mCacheEntry->SetMetaDataElement(key, value);
535 3941 : if (NS_SUCCEEDED(rv))
536 3941 : mCacheEntry->TouchMetaData();
537 3941 : return rv;
538 : }
539 :
540 :
541 : NS_IMETHODIMP
542 0 : nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor)
543 : {
544 0 : nsCacheServiceAutoLock lock; // XXX check callers, we're calling out of module
545 0 : NS_ENSURE_ARG_POINTER(visitor);
546 0 : if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
547 :
548 0 : return mCacheEntry->VisitMetaDataElements(visitor);
549 : }
550 :
551 :
552 : /******************************************************************************
553 : * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry
554 : * open while referenced.
555 : ******************************************************************************/
556 :
557 3601 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsInputStreamWrapper,
558 : nsIInputStream)
559 :
560 242 : nsresult nsCacheEntryDescriptor::
561 : nsInputStreamWrapper::LazyInit()
562 : {
563 484 : nsCacheServiceAutoLock lock;
564 :
565 : nsCacheAccessMode mode;
566 242 : nsresult rv = mDescriptor->GetAccessGranted(&mode);
567 242 : if (NS_FAILED(rv)) return rv;
568 :
569 242 : NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
570 :
571 242 : nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
572 242 : if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
573 :
574 : rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
575 : mStartOffset,
576 242 : getter_AddRefs(mInput));
577 242 : if (NS_FAILED(rv)) return rv;
578 :
579 242 : mInitialized = true;
580 242 : return NS_OK;
581 : }
582 :
583 242 : nsresult nsCacheEntryDescriptor::
584 : nsInputStreamWrapper::Close()
585 : {
586 242 : nsresult rv = EnsureInit();
587 242 : if (NS_FAILED(rv)) return rv;
588 :
589 242 : return mInput->Close();
590 : }
591 :
592 16 : nsresult nsCacheEntryDescriptor::
593 : nsInputStreamWrapper::Available(PRUint32 *avail)
594 : {
595 16 : nsresult rv = EnsureInit();
596 16 : if (NS_FAILED(rv)) return rv;
597 :
598 16 : return mInput->Available(avail);
599 : }
600 :
601 476 : nsresult nsCacheEntryDescriptor::
602 : nsInputStreamWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead)
603 : {
604 476 : nsresult rv = EnsureInit();
605 476 : if (NS_FAILED(rv)) return rv;
606 :
607 476 : return mInput->Read(buf, count, countRead);
608 : }
609 :
610 0 : nsresult nsCacheEntryDescriptor::
611 : nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
612 : PRUint32 count, PRUint32 *countRead)
613 : {
614 : // cache stream not buffered
615 0 : return NS_ERROR_NOT_IMPLEMENTED;
616 : }
617 :
618 217 : nsresult nsCacheEntryDescriptor::
619 : nsInputStreamWrapper::IsNonBlocking(bool *result)
620 : {
621 : // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
622 217 : *result = false;
623 217 : return NS_OK;
624 : }
625 :
626 :
627 : /******************************************************************************
628 : * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses
629 : ******************************************************************************/
630 :
631 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper,
632 : nsIInputStream)
633 :
634 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
635 : nsDecompressInputStreamWrapper::Read(char * buf,
636 : PRUint32 count,
637 : PRUint32 *countRead)
638 : {
639 0 : int zerr = Z_OK;
640 0 : nsresult rv = NS_OK;
641 :
642 0 : if (!mStreamInitialized) {
643 0 : rv = InitZstream();
644 0 : if (NS_FAILED(rv)) {
645 0 : return rv;
646 : }
647 : }
648 :
649 0 : mZstream.next_out = (Bytef*)buf;
650 0 : mZstream.avail_out = count;
651 :
652 0 : if (mReadBufferLen < count) {
653 : // Allocate a buffer for reading from the input stream. This will
654 : // determine the max number of compressed bytes read from the
655 : // input stream at one time. Making the buffer size proportional
656 : // to the request size is not necessary, but helps minimize the
657 : // number of read requests to the input stream.
658 0 : PRUint32 newBufLen = NS_MAX(count, (PRUint32)kMinDecompressReadBufLen);
659 : unsigned char* newBuf;
660 : newBuf = (unsigned char*)nsMemory::Realloc(mReadBuffer,
661 0 : newBufLen);
662 0 : if (newBuf) {
663 0 : mReadBuffer = newBuf;
664 0 : mReadBufferLen = newBufLen;
665 : }
666 0 : if (!mReadBuffer) {
667 0 : mReadBufferLen = 0;
668 0 : return NS_ERROR_OUT_OF_MEMORY;
669 : }
670 : }
671 :
672 : // read and inflate data until the output buffer is full, or
673 : // there is no more data to read
674 0 : while (NS_SUCCEEDED(rv) &&
675 : zerr == Z_OK &&
676 : mZstream.avail_out > 0 &&
677 : count > 0) {
678 0 : if (mZstream.avail_in == 0) {
679 : rv = nsInputStreamWrapper::Read((char*)mReadBuffer,
680 : mReadBufferLen,
681 0 : &mZstream.avail_in);
682 0 : if (NS_FAILED(rv) || !mZstream.avail_in) {
683 0 : break;
684 : }
685 0 : mZstream.next_in = mReadBuffer;
686 : }
687 0 : zerr = inflate(&mZstream, Z_NO_FLUSH);
688 0 : if (zerr == Z_STREAM_END) {
689 : // The compressed data may have been stored in multiple
690 : // chunks/streams. To allow for this case, re-initialize
691 : // the inflate stream and continue decompressing from
692 : // the next byte.
693 0 : Bytef * saveNextIn = mZstream.next_in;
694 0 : unsigned int saveAvailIn = mZstream.avail_in;
695 0 : Bytef * saveNextOut = mZstream.next_out;
696 0 : unsigned int saveAvailOut = mZstream.avail_out;
697 0 : inflateReset(&mZstream);
698 0 : mZstream.next_in = saveNextIn;
699 0 : mZstream.avail_in = saveAvailIn;
700 0 : mZstream.next_out = saveNextOut;
701 0 : mZstream.avail_out = saveAvailOut;
702 0 : zerr = Z_OK;
703 0 : } else if (zerr != Z_OK) {
704 0 : rv = NS_ERROR_INVALID_CONTENT_ENCODING;
705 : }
706 : }
707 0 : if (NS_SUCCEEDED(rv)) {
708 0 : *countRead = count - mZstream.avail_out;
709 : }
710 0 : return rv;
711 : }
712 :
713 0 : nsresult nsCacheEntryDescriptor::
714 : nsDecompressInputStreamWrapper::Close()
715 : {
716 0 : EndZstream();
717 0 : if (mReadBuffer) {
718 0 : nsMemory::Free(mReadBuffer);
719 0 : mReadBuffer = 0;
720 0 : mReadBufferLen = 0;
721 : }
722 0 : return nsInputStreamWrapper::Close();
723 : }
724 :
725 0 : nsresult nsCacheEntryDescriptor::
726 : nsDecompressInputStreamWrapper::InitZstream()
727 : {
728 : // Initialize zlib inflate stream
729 0 : mZstream.zalloc = Z_NULL;
730 0 : mZstream.zfree = Z_NULL;
731 0 : mZstream.opaque = Z_NULL;
732 0 : mZstream.next_out = Z_NULL;
733 0 : mZstream.avail_out = 0;
734 0 : mZstream.next_in = Z_NULL;
735 0 : mZstream.avail_in = 0;
736 0 : if (inflateInit(&mZstream) != Z_OK) {
737 0 : return NS_ERROR_FAILURE;
738 : }
739 0 : mStreamInitialized = PR_TRUE;
740 0 : return NS_OK;
741 : }
742 :
743 0 : nsresult nsCacheEntryDescriptor::
744 : nsDecompressInputStreamWrapper::EndZstream()
745 : {
746 0 : if (mStreamInitialized && !mStreamEnded) {
747 0 : inflateEnd(&mZstream);
748 0 : mStreamEnded = PR_TRUE;
749 : }
750 0 : return NS_OK;
751 : }
752 :
753 :
754 : /******************************************************************************
755 : * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of
756 : * data written to a cache entry.
757 : * - also keeps the cache entry open while referenced.
758 : ******************************************************************************/
759 :
760 24254 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsOutputStreamWrapper,
761 : nsIOutputStream)
762 :
763 1254 : nsresult nsCacheEntryDescriptor::
764 : nsOutputStreamWrapper::LazyInit()
765 : {
766 2508 : nsCacheServiceAutoLock lock;
767 :
768 : nsCacheAccessMode mode;
769 1254 : nsresult rv = mDescriptor->GetAccessGranted(&mode);
770 1254 : if (NS_FAILED(rv)) return rv;
771 :
772 1254 : NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED);
773 :
774 1254 : nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
775 1254 : if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
776 :
777 1253 : NS_ASSERTION(mOutput == nsnull, "mOutput set in LazyInit");
778 :
779 2506 : nsCOMPtr<nsIOutputStream> stream;
780 : rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset,
781 1253 : getter_AddRefs(stream));
782 1253 : if (NS_FAILED(rv))
783 119 : return rv;
784 :
785 1134 : nsCacheDevice* device = cacheEntry->CacheDevice();
786 1134 : if (device) {
787 : // the entry has been truncated to mStartOffset bytes, inform device
788 1134 : PRInt32 size = cacheEntry->DataSize();
789 1134 : rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size);
790 1134 : if (NS_SUCCEEDED(rv))
791 1132 : cacheEntry->SetDataSize(mStartOffset);
792 : } else {
793 0 : rv = NS_ERROR_NOT_AVAILABLE;
794 : }
795 :
796 : // If anything above failed, clean up internal state and get out of here
797 : // (see bug #654926)...
798 1134 : if (NS_FAILED(rv)) {
799 2 : mDescriptor->InternalCleanup(stream);
800 2 : return rv;
801 : }
802 :
803 : // ... otherwise, set members and mark initialized
804 1132 : mDescriptor->mOutput = mOutput = stream;
805 1132 : mInitialized = true;
806 1132 : return NS_OK;
807 : }
808 :
809 1496 : nsresult nsCacheEntryDescriptor::
810 : nsOutputStreamWrapper::OnWrite(PRUint32 count)
811 : {
812 1496 : if (count > PR_INT32_MAX) return NS_ERROR_UNEXPECTED;
813 1496 : return mDescriptor->RequestDataSizeChange((PRInt32)count);
814 : }
815 :
816 1230 : NS_IMETHODIMP nsCacheEntryDescriptor::
817 : nsOutputStreamWrapper::Close()
818 : {
819 1230 : nsresult rv = EnsureInit();
820 1230 : if (NS_FAILED(rv)) return rv;
821 :
822 1167 : return mOutput->Close();
823 : }
824 :
825 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
826 : nsOutputStreamWrapper::Flush()
827 : {
828 0 : nsresult rv = EnsureInit();
829 0 : if (NS_FAILED(rv)) return rv;
830 :
831 0 : return mOutput->Flush();
832 : }
833 :
834 1555 : NS_IMETHODIMP nsCacheEntryDescriptor::
835 : nsOutputStreamWrapper::Write(const char * buf,
836 : PRUint32 count,
837 : PRUint32 * result)
838 : {
839 1555 : nsresult rv = EnsureInit();
840 1555 : if (NS_FAILED(rv)) return rv;
841 :
842 1496 : rv = OnWrite(count);
843 1496 : if (NS_FAILED(rv)) return rv;
844 :
845 1496 : return mOutput->Write(buf, count, result);
846 : }
847 :
848 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
849 : nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr,
850 : PRUint32 count,
851 : PRUint32 * result)
852 : {
853 0 : return NS_ERROR_NOT_IMPLEMENTED;
854 : }
855 :
856 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
857 : nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader,
858 : void * closure,
859 : PRUint32 count,
860 : PRUint32 * result)
861 : {
862 0 : return NS_ERROR_NOT_IMPLEMENTED;
863 : }
864 :
865 2434 : NS_IMETHODIMP nsCacheEntryDescriptor::
866 : nsOutputStreamWrapper::IsNonBlocking(bool *result)
867 : {
868 : // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
869 2434 : *result = false;
870 2434 : return NS_OK;
871 : }
872 :
873 :
874 : /******************************************************************************
875 : * nsCompressOutputStreamWrapper - an output stream wrapper that compresses
876 : * data before it is written
877 : ******************************************************************************/
878 :
879 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper,
880 : nsIOutputStream)
881 :
882 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
883 : nsCompressOutputStreamWrapper::Write(const char * buf,
884 : PRUint32 count,
885 : PRUint32 * result)
886 : {
887 0 : int zerr = Z_OK;
888 0 : nsresult rv = NS_OK;
889 :
890 0 : if (!mStreamInitialized) {
891 0 : rv = InitZstream();
892 0 : if (NS_FAILED(rv)) {
893 0 : return rv;
894 : }
895 : }
896 :
897 0 : if (!mWriteBuffer) {
898 : // Once allocated, this buffer is referenced by the zlib stream and
899 : // cannot be grown. We use 2x(initial write request) to approximate
900 : // a stream buffer size proportional to request buffers.
901 0 : mWriteBufferLen = NS_MAX(count*2, (PRUint32)kMinCompressWriteBufLen);
902 0 : mWriteBuffer = (unsigned char*)nsMemory::Alloc(mWriteBufferLen);
903 0 : if (!mWriteBuffer) {
904 0 : mWriteBufferLen = 0;
905 0 : return NS_ERROR_OUT_OF_MEMORY;
906 : }
907 0 : mZstream.next_out = mWriteBuffer;
908 0 : mZstream.avail_out = mWriteBufferLen;
909 : }
910 :
911 : // Compress (deflate) the requested buffer. Keep going
912 : // until the entire buffer has been deflated.
913 0 : mZstream.avail_in = count;
914 0 : mZstream.next_in = (Bytef*)buf;
915 0 : while (mZstream.avail_in > 0) {
916 0 : zerr = deflate(&mZstream, Z_NO_FLUSH);
917 0 : if (zerr == Z_STREAM_ERROR) {
918 0 : deflateEnd(&mZstream);
919 0 : mStreamInitialized = PR_FALSE;
920 0 : return NS_ERROR_FAILURE;
921 : }
922 : // Note: Z_BUF_ERROR is non-fatal and sometimes expected here.
923 :
924 : // If the compression stream output buffer is filled, write
925 : // it out to the underlying stream wrapper.
926 0 : if (mZstream.avail_out == 0) {
927 0 : rv = WriteBuffer();
928 0 : if (NS_FAILED(rv)) {
929 0 : deflateEnd(&mZstream);
930 0 : mStreamInitialized = PR_FALSE;
931 0 : return rv;
932 : }
933 : }
934 : }
935 0 : *result = count;
936 0 : mUncompressedCount += *result;
937 0 : return NS_OK;
938 : }
939 :
940 0 : NS_IMETHODIMP nsCacheEntryDescriptor::
941 : nsCompressOutputStreamWrapper::Close()
942 : {
943 0 : nsresult rv = NS_OK;
944 0 : int zerr = 0;
945 :
946 0 : if (mStreamInitialized) {
947 : // complete compression of any data remaining in the zlib stream
948 0 : do {
949 0 : zerr = deflate(&mZstream, Z_FINISH);
950 0 : rv = WriteBuffer();
951 : } while (zerr == Z_OK && rv == NS_OK);
952 0 : deflateEnd(&mZstream);
953 : }
954 :
955 0 : if (mDescriptor->CacheEntry()) {
956 0 : nsCAutoString uncompressedLenStr;
957 : rv = mDescriptor->GetMetaDataElement("uncompressed-len",
958 0 : getter_Copies(uncompressedLenStr));
959 0 : if (NS_SUCCEEDED(rv)) {
960 0 : PRInt32 oldCount = uncompressedLenStr.ToInteger(&rv);
961 0 : if (NS_SUCCEEDED(rv)) {
962 0 : mUncompressedCount += oldCount;
963 : }
964 : }
965 0 : uncompressedLenStr.Adopt(0);
966 0 : uncompressedLenStr.AppendInt(mUncompressedCount);
967 : rv = mDescriptor->SetMetaDataElement("uncompressed-len",
968 0 : uncompressedLenStr.get());
969 : }
970 :
971 0 : if (mWriteBuffer) {
972 0 : nsMemory::Free(mWriteBuffer);
973 0 : mWriteBuffer = 0;
974 0 : mWriteBufferLen = 0;
975 : }
976 :
977 0 : return nsOutputStreamWrapper::Close();
978 : }
979 :
980 0 : nsresult nsCacheEntryDescriptor::
981 : nsCompressOutputStreamWrapper::InitZstream()
982 : {
983 : // Determine compression level: Aggressive compression
984 : // may impact performance on mobile devices, while a
985 : // lower compression level still provides substantial
986 : // space savings for many text streams.
987 0 : PRInt32 compressionLevel = nsCacheService::CacheCompressionLevel();
988 :
989 : // Initialize zlib deflate stream
990 0 : mZstream.zalloc = Z_NULL;
991 0 : mZstream.zfree = Z_NULL;
992 0 : mZstream.opaque = Z_NULL;
993 0 : if (deflateInit2(&mZstream, compressionLevel, Z_DEFLATED,
994 : MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
995 0 : return NS_ERROR_FAILURE;
996 : }
997 0 : mZstream.next_in = Z_NULL;
998 0 : mZstream.avail_in = 0;
999 :
1000 0 : mStreamInitialized = PR_TRUE;
1001 :
1002 0 : return NS_OK;
1003 : }
1004 :
1005 0 : nsresult nsCacheEntryDescriptor::
1006 : nsCompressOutputStreamWrapper::WriteBuffer()
1007 : {
1008 0 : PRUint32 bytesToWrite = mWriteBufferLen - mZstream.avail_out;
1009 0 : PRUint32 result = 0;
1010 : nsresult rv = nsCacheEntryDescriptor::nsOutputStreamWrapper::Write(
1011 0 : (const char *)mWriteBuffer, bytesToWrite, &result);
1012 0 : mZstream.next_out = mWriteBuffer;
1013 0 : mZstream.avail_out = mWriteBufferLen;
1014 0 : return rv;
1015 : }
1016 :
|