Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache/nsCacheEntry.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
8
#include "nsCache.h"
9
#include "nspr.h"
10
#include "nsCacheEntry.h"
11
#include "nsCacheEntryDescriptor.h"
12
#include "nsCacheMetaData.h"
13
#include "nsCacheRequest.h"
14
#include "nsThreadUtils.h"
15
#include "nsError.h"
16
#include "nsICacheService.h"
17
#include "nsCacheService.h"
18
#include "nsCacheDevice.h"
19
#include "nsHashKeys.h"
20
21
using namespace mozilla;
22
23
nsCacheEntry::nsCacheEntry(const nsACString &   key,
24
                           bool                 streamBased,
25
                           nsCacheStoragePolicy storagePolicy)
26
    : mKey(key),
27
      mFetchCount(0),
28
      mLastFetched(0),
29
      mLastModified(0),
30
      mLastValidated(0),
31
      mExpirationTime(nsICache::NO_EXPIRATION_TIME),
32
      mFlags(0),
33
      mPredictedDataSize(-1),
34
      mDataSize(0),
35
      mCacheDevice(nullptr),
36
      mCustomDevice(nullptr),
37
      mData(nullptr),
38
      mRequestQ{},
39
      mDescriptorQ{}
40
{
41
    MOZ_COUNT_CTOR(nsCacheEntry);
42
    PR_INIT_CLIST(this);
43
    PR_INIT_CLIST(&mRequestQ);
44
    PR_INIT_CLIST(&mDescriptorQ);
45
46
    if (streamBased) MarkStreamBased();
47
    SetStoragePolicy(storagePolicy);
48
49
    MarkPublic();
50
}
51
52
53
nsCacheEntry::~nsCacheEntry()
54
0
{
55
0
    MOZ_COUNT_DTOR(nsCacheEntry);
56
0
57
0
    if (mData)
58
0
        nsCacheService::ReleaseObject_Locked(mData, mEventTarget);
59
0
}
60
61
62
nsresult
63
nsCacheEntry::Create( const char *          key,
64
                      bool                  streamBased,
65
                      nsCacheStoragePolicy  storagePolicy,
66
                      nsCacheDevice *       device,
67
                      nsCacheEntry **       result)
68
0
{
69
0
    nsCacheEntry* entry = new nsCacheEntry(nsCString(key),
70
0
                                           streamBased,
71
0
                                           storagePolicy);
72
0
    entry->SetCacheDevice(device);
73
0
    *result = entry;
74
0
    return NS_OK;
75
0
}
76
77
78
void
79
nsCacheEntry::Fetched()
80
0
{
81
0
    mLastFetched = SecondsFromPRTime(PR_Now());
82
0
    ++mFetchCount;
83
0
    MarkEntryDirty();
84
0
}
85
86
87
const char *
88
nsCacheEntry::GetDeviceID()
89
0
{
90
0
    if (mCacheDevice)  return mCacheDevice->GetDeviceID();
91
0
    return nullptr;
92
0
}
93
94
95
void
96
nsCacheEntry::TouchData()
97
0
{
98
0
    mLastModified = SecondsFromPRTime(PR_Now());
99
0
    MarkDataDirty();
100
0
}
101
102
103
void
104
nsCacheEntry::SetData(nsISupports * data)
105
0
{
106
0
    if (mData) {
107
0
        nsCacheService::ReleaseObject_Locked(mData, mEventTarget);
108
0
        mData = nullptr;
109
0
    }
110
0
111
0
    if (data) {
112
0
        NS_ADDREF(mData = data);
113
0
        mEventTarget = GetCurrentThreadEventTarget();
114
0
    }
115
0
}
116
117
118
void
119
nsCacheEntry::TouchMetaData()
120
0
{
121
0
    mLastModified = SecondsFromPRTime(PR_Now());
122
0
    MarkMetaDataDirty();
123
0
}
124
125
126
/**
127
 *  cache entry states
128
 *      0 descriptors (new entry)
129
 *      0 descriptors (existing, bound entry)
130
 *      n descriptors (existing, bound entry) valid
131
 *      n descriptors (existing, bound entry) not valid (wait until valid or doomed)
132
 */
133
134
nsresult
135
nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
136
0
{
137
0
    nsresult  rv = NS_OK;
138
0
139
0
    if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
140
0
141
0
    if (!IsInitialized()) {
142
0
        // brand new, unbound entry
143
0
        if (request->IsStreamBased())  MarkStreamBased();
144
0
        MarkInitialized();
145
0
146
0
        *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
147
0
        NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
148
0
        PR_APPEND_LINK(request, &mRequestQ);
149
0
        return rv;
150
0
    }
151
0
152
0
    if (IsStreamData() != request->IsStreamBased()) {
153
0
        *accessGranted = nsICache::ACCESS_NONE;
154
0
        return request->IsStreamBased() ?
155
0
            NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
156
0
    }
157
0
158
0
    if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
159
0
        // 1st descriptor for existing bound entry
160
0
        *accessGranted = request->AccessRequested();
161
0
        if (*accessGranted & nsICache::ACCESS_WRITE) {
162
0
            MarkInvalid();
163
0
        } else {
164
0
            MarkValid();
165
0
        }
166
0
    } else {
167
0
        // nth request for existing, bound entry
168
0
        *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
169
0
        if (!IsValid())
170
0
            rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
171
0
    }
172
0
    PR_APPEND_LINK(request,&mRequestQ);
173
0
174
0
    return rv;
175
0
}
176
177
178
nsresult
179
nsCacheEntry::CreateDescriptor(nsCacheRequest *           request,
180
                               nsCacheAccessMode          accessGranted,
181
                               nsICacheEntryDescriptor ** result)
182
0
{
183
0
    NS_ENSURE_ARG_POINTER(request && result);
184
0
185
0
    nsCacheEntryDescriptor * descriptor =
186
0
        new nsCacheEntryDescriptor(this, accessGranted);
187
0
188
0
    // XXX check request is on q
189
0
    PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
190
0
191
0
    if (descriptor == nullptr)
192
0
        return NS_ERROR_OUT_OF_MEMORY;
193
0
194
0
    PR_APPEND_LINK(descriptor, &mDescriptorQ);
195
0
196
0
    CACHE_LOG_DEBUG(("  descriptor %p created for request %p on entry %p\n",
197
0
                    descriptor, request, this));
198
0
199
0
    NS_ADDREF(*result = descriptor);
200
0
    return NS_OK;
201
0
}
202
203
204
bool
205
nsCacheEntry::RemoveRequest(nsCacheRequest * request)
206
0
{
207
0
    // XXX if debug: verify this request belongs to this entry
208
0
    PR_REMOVE_AND_INIT_LINK(request);
209
0
210
0
    // return true if this entry should stay active
211
0
    return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
212
0
             (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
213
0
}
214
215
216
bool
217
nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor,
218
                               bool                   * doomEntry)
219
0
{
220
0
    NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
221
0
222
0
    *doomEntry = descriptor->ClearCacheEntry();
223
0
224
0
    PR_REMOVE_AND_INIT_LINK(descriptor);
225
0
226
0
    if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
227
0
        return true;  // stay active if we still have open descriptors
228
0
229
0
    if (PR_CLIST_IS_EMPTY(&mRequestQ))
230
0
        return false; // no descriptors or requests, we can deactivate
231
0
232
0
    return true;     // find next best request to give a descriptor to
233
0
}
234
235
236
void
237
nsCacheEntry::DetachDescriptors()
238
0
{
239
0
    nsCacheEntryDescriptor * descriptor =
240
0
        (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
241
0
242
0
    while (descriptor != &mDescriptorQ) {
243
0
        nsCacheEntryDescriptor * nextDescriptor =
244
0
            (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
245
0
246
0
        descriptor->ClearCacheEntry();
247
0
        PR_REMOVE_AND_INIT_LINK(descriptor);
248
0
        descriptor = nextDescriptor;
249
0
    }
250
0
}
251
252
253
void
254
nsCacheEntry::GetDescriptors(
255
    nsTArray<RefPtr<nsCacheEntryDescriptor> > &outDescriptors)
256
0
{
257
0
    nsCacheEntryDescriptor * descriptor =
258
0
        (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
259
0
260
0
    while (descriptor != &mDescriptorQ) {
261
0
        nsCacheEntryDescriptor * nextDescriptor =
262
0
            (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
263
0
264
0
        outDescriptors.AppendElement(descriptor);
265
0
        descriptor = nextDescriptor;
266
0
    }
267
0
}
268
269
270
/******************************************************************************
271
 * nsCacheEntryInfo - for implementing about:cache
272
 *****************************************************************************/
273
274
NS_IMPL_ISUPPORTS(nsCacheEntryInfo, nsICacheEntryInfo)
275
276
277
NS_IMETHODIMP
278
nsCacheEntryInfo::GetClientID(nsACString& aClientID)
279
0
{
280
0
    if (!mCacheEntry) {
281
0
        aClientID.Truncate();
282
0
        return NS_ERROR_NOT_AVAILABLE;
283
0
    }
284
0
285
0
    return ClientIDFromCacheKey(*mCacheEntry->Key(), aClientID);
286
0
}
287
288
289
NS_IMETHODIMP
290
nsCacheEntryInfo::GetDeviceID(nsACString& aDeviceID)
291
0
{
292
0
    if (!mCacheEntry) {
293
0
        aDeviceID.Truncate();
294
0
        return NS_ERROR_NOT_AVAILABLE;
295
0
    }
296
0
297
0
    aDeviceID.Assign(mCacheEntry->GetDeviceID());
298
0
    return NS_OK;
299
0
}
300
301
302
NS_IMETHODIMP
303
nsCacheEntryInfo::GetKey(nsACString &key)
304
0
{
305
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
306
0
307
0
    return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
308
0
}
309
310
311
NS_IMETHODIMP
312
nsCacheEntryInfo::GetFetchCount(int32_t * fetchCount)
313
0
{
314
0
    NS_ENSURE_ARG_POINTER(fetchCount);
315
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
316
0
317
0
    *fetchCount = mCacheEntry->FetchCount();
318
0
    return NS_OK;
319
0
}
320
321
322
NS_IMETHODIMP
323
nsCacheEntryInfo::GetLastFetched(uint32_t * lastFetched)
324
0
{
325
0
    NS_ENSURE_ARG_POINTER(lastFetched);
326
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
327
0
328
0
    *lastFetched = mCacheEntry->LastFetched();
329
0
    return NS_OK;
330
0
}
331
332
333
NS_IMETHODIMP
334
nsCacheEntryInfo::GetLastModified(uint32_t * lastModified)
335
0
{
336
0
    NS_ENSURE_ARG_POINTER(lastModified);
337
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
338
0
339
0
    *lastModified = mCacheEntry->LastModified();
340
0
    return NS_OK;
341
0
}
342
343
344
NS_IMETHODIMP
345
nsCacheEntryInfo::GetExpirationTime(uint32_t * expirationTime)
346
0
{
347
0
    NS_ENSURE_ARG_POINTER(expirationTime);
348
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
349
0
350
0
    *expirationTime = mCacheEntry->ExpirationTime();
351
0
    return NS_OK;
352
0
}
353
354
355
NS_IMETHODIMP
356
nsCacheEntryInfo::GetDataSize(uint32_t * dataSize)
357
0
{
358
0
    NS_ENSURE_ARG_POINTER(dataSize);
359
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
360
0
361
0
    *dataSize = mCacheEntry->DataSize();
362
0
    return NS_OK;
363
0
}
364
365
366
NS_IMETHODIMP
367
nsCacheEntryInfo::IsStreamBased(bool * result)
368
0
{
369
0
    NS_ENSURE_ARG_POINTER(result);
370
0
    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
371
0
372
0
    *result = mCacheEntry->IsStreamData();
373
0
    return NS_OK;
374
0
}
375
376
377
/******************************************************************************
378
 *  nsCacheEntryHashTable
379
 *****************************************************************************/
380
381
const PLDHashTableOps
382
nsCacheEntryHashTable::ops =
383
{
384
    HashKey,
385
    MatchEntry,
386
    MoveEntry,
387
    ClearEntry
388
};
389
390
391
nsCacheEntryHashTable::nsCacheEntryHashTable()
392
    : table(&ops, sizeof(nsCacheEntryHashTableEntry), kInitialTableLength)
393
    , initialized(false)
394
0
{
395
0
    MOZ_COUNT_CTOR(nsCacheEntryHashTable);
396
0
}
397
398
399
nsCacheEntryHashTable::~nsCacheEntryHashTable()
400
0
{
401
0
    MOZ_COUNT_DTOR(nsCacheEntryHashTable);
402
0
    if (initialized)
403
0
        Shutdown();
404
0
}
405
406
407
void
408
nsCacheEntryHashTable::Init()
409
0
{
410
0
    table.ClearAndPrepareForLength(kInitialTableLength);
411
0
    initialized = true;
412
0
}
413
414
void
415
nsCacheEntryHashTable::Shutdown()
416
0
{
417
0
    if (initialized) {
418
0
        table.ClearAndPrepareForLength(kInitialTableLength);
419
0
        initialized = false;
420
0
    }
421
0
}
422
423
424
nsCacheEntry *
425
nsCacheEntryHashTable::GetEntry( const nsCString * key) const
426
0
{
427
0
    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
428
0
    if (!initialized)  return nullptr;
429
0
430
0
    PLDHashEntryHdr *hashEntry = table.Search(key);
431
0
    return hashEntry ? ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry
432
0
                     : nullptr;
433
0
}
434
435
436
nsresult
437
nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
438
0
{
439
0
    PLDHashEntryHdr    *hashEntry;
440
0
441
0
    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
442
0
    if (!initialized)  return NS_ERROR_NOT_INITIALIZED;
443
0
    if (!cacheEntry)   return NS_ERROR_NULL_POINTER;
444
0
445
0
    hashEntry = table.Add(&(cacheEntry->mKey), fallible);
446
0
447
0
    if (!hashEntry)
448
0
        return NS_ERROR_FAILURE;
449
0
    NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == nullptr,
450
0
                 "### nsCacheEntryHashTable::AddEntry - entry already used");
451
0
    ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
452
0
453
0
    return NS_OK;
454
0
}
455
456
457
void
458
nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
459
0
{
460
0
    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
461
0
    NS_ASSERTION(cacheEntry, "### cacheEntry == nullptr");
462
0
463
0
    if (!initialized)  return; // NS_ERROR_NOT_INITIALIZED
464
0
465
#if DEBUG
466
    // XXX debug code to make sure we have the entry we're trying to remove
467
    nsCacheEntry *check = GetEntry(&(cacheEntry->mKey));
468
    NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
469
#endif
470
0
    table.Remove(&(cacheEntry->mKey));
471
0
}
472
473
PLDHashTable::Iterator
474
nsCacheEntryHashTable::Iter()
475
0
{
476
0
    return PLDHashTable::Iterator(&table);
477
0
}
478
479
/**
480
 *  hash table operation callback functions
481
 */
482
483
PLDHashNumber
484
nsCacheEntryHashTable::HashKey(const void *key)
485
0
{
486
0
    return HashString(*static_cast<const nsCString *>(key));
487
0
}
488
489
bool
490
nsCacheEntryHashTable::MatchEntry(const PLDHashEntryHdr * hashEntry,
491
                                  const void *            key)
492
0
{
493
0
    NS_ASSERTION(key !=  nullptr, "### nsCacheEntryHashTable::MatchEntry : null key");
494
0
    nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
495
0
496
0
    return cacheEntry->mKey.Equals(*(nsCString *)key);
497
0
}
498
499
500
void
501
nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
502
                                 const PLDHashEntryHdr *from,
503
                                 PLDHashEntryHdr       *to)
504
0
{
505
0
    new (KnownNotNull, to) nsCacheEntryHashTableEntry(std::move(*((nsCacheEntryHashTableEntry *)from)));
506
0
    // No need to destroy `from`.
507
0
}
508
509
510
void
511
nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
512
                                  PLDHashEntryHdr * hashEntry)
513
0
{
514
0
    ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = nullptr;
515
0
}