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