Coverage Report

Created: 2018-09-25 14:53

/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