Bug 458861. Validate TrueType headers before activating downloaded font. r=roc, sr...
[wine-gecko.git] / netwerk / cache / src / nsCacheEntry.cpp
blobeb1e0cadcf97e8c197557406e417895ed7e04ad8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is nsCacheEntry.cpp, released
17 * February 22, 2001.
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.
24 * Contributor(s):
25 * Gordon Sheridan <gordon@netscape.com>
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.
39 * ***** END LICENSE BLOCK ***** */
42 #include "nspr.h"
43 #include "nsCacheEntry.h"
44 #include "nsCacheEntryDescriptor.h"
45 #include "nsCacheMetaData.h"
46 #include "nsCacheRequest.h"
47 #include "nsThreadUtils.h"
48 #include "nsError.h"
49 #include "nsICacheService.h"
50 #include "nsCache.h"
51 #include "nsCacheService.h"
52 #include "nsCacheDevice.h"
53 #include "nsCRT.h"
56 nsCacheEntry::nsCacheEntry(nsCString * key,
57 PRBool streamBased,
58 nsCacheStoragePolicy storagePolicy)
59 : mKey(key),
60 mFetchCount(0),
61 mLastFetched(0),
62 mLastModified(0),
63 mExpirationTime(NO_EXPIRATION_TIME),
64 mFlags(0),
65 mDataSize(0),
66 mCacheDevice(nsnull),
67 mData(nsnull)
69 MOZ_COUNT_CTOR(nsCacheEntry);
70 PR_INIT_CLIST(this);
71 PR_INIT_CLIST(&mRequestQ);
72 PR_INIT_CLIST(&mDescriptorQ);
74 if (streamBased) MarkStreamBased();
75 SetStoragePolicy(storagePolicy);
79 nsCacheEntry::~nsCacheEntry()
81 MOZ_COUNT_DTOR(nsCacheEntry);
82 delete mKey;
84 if (mData)
85 nsCacheService::ReleaseObject_Locked(mData, mThread);
89 nsresult
90 nsCacheEntry::Create( const char * key,
91 PRBool streamBased,
92 nsCacheStoragePolicy storagePolicy,
93 nsCacheDevice * device,
94 nsCacheEntry ** result)
96 nsCString* newKey = new nsCString(key);
97 if (!newKey) return NS_ERROR_OUT_OF_MEMORY;
99 nsCacheEntry* entry = new nsCacheEntry(newKey, streamBased, storagePolicy);
100 if (!entry) { delete newKey; return NS_ERROR_OUT_OF_MEMORY; }
102 entry->SetCacheDevice(device);
104 *result = entry;
105 return NS_OK;
109 void
110 nsCacheEntry::Fetched()
112 mLastFetched = SecondsFromPRTime(PR_Now());
113 ++mFetchCount;
114 MarkEntryDirty();
118 const char *
119 nsCacheEntry::GetDeviceID()
121 if (mCacheDevice) return mCacheDevice->GetDeviceID();
122 return nsnull;
126 void
127 nsCacheEntry::TouchData()
129 mLastModified = SecondsFromPRTime(PR_Now());
130 MarkDataDirty();
134 void
135 nsCacheEntry::SetData(nsISupports * data)
137 if (mData) {
138 nsCacheService::ReleaseObject_Locked(mData, mThread);
139 mData = nsnull;
142 if (data) {
143 NS_ADDREF(mData = data);
144 mThread = do_GetCurrentThread();
149 void
150 nsCacheEntry::TouchMetaData()
152 mLastModified = SecondsFromPRTime(PR_Now());
153 MarkMetaDataDirty();
158 * cache entry states
159 * 0 descriptors (new entry)
160 * 0 descriptors (existing, bound entry)
161 * n descriptors (existing, bound entry) valid
162 * n descriptors (existing, bound entry) not valid (wait until valid or doomed)
165 nsresult
166 nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
168 nsresult rv = NS_OK;
170 if (!IsInitialized()) {
171 // brand new, unbound entry
172 request->mKey = nsnull; // steal ownership of the key string
173 if (request->IsStreamBased()) MarkStreamBased();
174 MarkInitialized();
176 *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
177 NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
178 PR_APPEND_LINK(request, &mRequestQ);
179 return rv;
182 if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
184 if (IsStreamData() != request->IsStreamBased()) {
185 *accessGranted = nsICache::ACCESS_NONE;
186 return request->IsStreamBased() ?
187 NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
190 if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
191 // 1st descriptor for existing bound entry
192 *accessGranted = request->AccessRequested();
193 if (*accessGranted & nsICache::ACCESS_WRITE) {
194 MarkInvalid();
195 } else {
196 MarkValid();
198 } else {
199 // nth request for existing, bound entry
200 *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
201 if (!IsValid())
202 rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
204 PR_APPEND_LINK(request,&mRequestQ);
206 return rv;
210 nsresult
211 nsCacheEntry::CreateDescriptor(nsCacheRequest * request,
212 nsCacheAccessMode accessGranted,
213 nsICacheEntryDescriptor ** result)
215 NS_ENSURE_ARG_POINTER(request && result);
217 nsCacheEntryDescriptor * descriptor =
218 new nsCacheEntryDescriptor(this, accessGranted);
220 // XXX check request is on q
221 PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
223 if (descriptor == nsnull)
224 return NS_ERROR_OUT_OF_MEMORY;
226 PR_APPEND_LINK(descriptor, &mDescriptorQ);
228 NS_ADDREF(*result = descriptor);
229 return NS_OK;
233 PRBool
234 nsCacheEntry::RemoveRequest(nsCacheRequest * request)
236 // XXX if debug: verify this request belongs to this entry
237 PR_REMOVE_AND_INIT_LINK(request);
239 // return true if this entry should stay active
240 return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
241 (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
245 PRBool
246 nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor)
248 NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
249 PR_REMOVE_AND_INIT_LINK(descriptor);
250 descriptor->ClearCacheEntry();
252 if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
253 return PR_TRUE; // stay active if we still have open descriptors
255 if (PR_CLIST_IS_EMPTY(&mRequestQ))
256 return PR_FALSE; // no descriptors or requests, we can deactivate
258 return PR_TRUE; // find next best request to give a descriptor to
262 void
263 nsCacheEntry::DetachDescriptors(void)
265 nsCacheEntryDescriptor * descriptor =
266 (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
268 while (descriptor != &mDescriptorQ) {
269 nsCacheEntryDescriptor * nextDescriptor =
270 (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
272 descriptor->ClearCacheEntry();
273 PR_REMOVE_AND_INIT_LINK(descriptor);
274 descriptor = nextDescriptor;
279 /******************************************************************************
280 * nsCacheEntryInfo - for implementing about:cache
281 *****************************************************************************/
283 NS_IMPL_ISUPPORTS1(nsCacheEntryInfo, nsICacheEntryInfo)
286 NS_IMETHODIMP
287 nsCacheEntryInfo::GetClientID(char ** clientID)
289 NS_ENSURE_ARG_POINTER(clientID);
290 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
292 return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID);
296 NS_IMETHODIMP
297 nsCacheEntryInfo::GetDeviceID(char ** deviceID)
299 NS_ENSURE_ARG_POINTER(deviceID);
300 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
302 *deviceID = NS_strdup(mCacheEntry->GetDeviceID());
303 return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
307 NS_IMETHODIMP
308 nsCacheEntryInfo::GetKey(nsACString &key)
310 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
312 return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
316 NS_IMETHODIMP
317 nsCacheEntryInfo::GetFetchCount(PRInt32 * fetchCount)
319 NS_ENSURE_ARG_POINTER(fetchCount);
320 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
322 *fetchCount = mCacheEntry->FetchCount();
323 return NS_OK;
327 NS_IMETHODIMP
328 nsCacheEntryInfo::GetLastFetched(PRUint32 * lastFetched)
330 NS_ENSURE_ARG_POINTER(lastFetched);
331 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
333 *lastFetched = mCacheEntry->LastFetched();
334 return NS_OK;
338 NS_IMETHODIMP
339 nsCacheEntryInfo::GetLastModified(PRUint32 * lastModified)
341 NS_ENSURE_ARG_POINTER(lastModified);
342 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
344 *lastModified = mCacheEntry->LastModified();
345 return NS_OK;
349 NS_IMETHODIMP
350 nsCacheEntryInfo::GetExpirationTime(PRUint32 * expirationTime)
352 NS_ENSURE_ARG_POINTER(expirationTime);
353 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
355 *expirationTime = mCacheEntry->ExpirationTime();
356 return NS_OK;
360 NS_IMETHODIMP
361 nsCacheEntryInfo::GetDataSize(PRUint32 * dataSize)
363 NS_ENSURE_ARG_POINTER(dataSize);
364 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
366 *dataSize = mCacheEntry->DataSize();
367 return NS_OK;
371 NS_IMETHODIMP
372 nsCacheEntryInfo::IsStreamBased(PRBool * result)
374 NS_ENSURE_ARG_POINTER(result);
375 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
377 *result = mCacheEntry->IsStreamData();
378 return NS_OK;
382 /******************************************************************************
383 * nsCacheEntryHashTable
384 *****************************************************************************/
386 PLDHashTableOps
387 nsCacheEntryHashTable::ops =
389 PL_DHashAllocTable,
390 PL_DHashFreeTable,
391 HashKey,
392 MatchEntry,
393 MoveEntry,
394 ClearEntry,
395 PL_DHashFinalizeStub
399 nsCacheEntryHashTable::nsCacheEntryHashTable()
400 : initialized(PR_FALSE)
402 MOZ_COUNT_CTOR(nsCacheEntryHashTable);
406 nsCacheEntryHashTable::~nsCacheEntryHashTable()
408 MOZ_COUNT_DTOR(nsCacheEntryHashTable);
409 if (initialized)
410 Shutdown();
414 nsresult
415 nsCacheEntryHashTable::Init()
417 nsresult rv = NS_OK;
418 initialized = PL_DHashTableInit(&table, &ops, nsnull,
419 sizeof(nsCacheEntryHashTableEntry), 512);
421 if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
423 return rv;
426 void
427 nsCacheEntryHashTable::Shutdown()
429 if (initialized) {
430 PL_DHashTableFinish(&table);
431 initialized = PR_FALSE;
436 nsCacheEntry *
437 nsCacheEntryHashTable::GetEntry( const nsCString * key)
439 PLDHashEntryHdr *hashEntry;
440 nsCacheEntry *result = nsnull;
442 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
443 if (!initialized) return nsnull;
445 hashEntry = PL_DHashTableOperate(&table, key, PL_DHASH_LOOKUP);
446 if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
447 result = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
449 return result;
453 nsresult
454 nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
456 PLDHashEntryHdr *hashEntry;
458 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
459 if (!initialized) return NS_ERROR_NOT_INITIALIZED;
460 if (!cacheEntry) return NS_ERROR_NULL_POINTER;
462 hashEntry = PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_ADD);
463 #ifndef DEBUG_dougt
464 NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0,
465 "### nsCacheEntryHashTable::AddEntry - entry already used");
466 #endif
467 ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
469 return NS_OK;
473 void
474 nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
476 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
477 NS_ASSERTION(cacheEntry, "### cacheEntry == nsnull");
479 if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
481 #if DEBUG
482 // XXX debug code to make sure we have the entry we're trying to remove
483 nsCacheEntry *check = GetEntry(cacheEntry->mKey);
484 NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
485 #endif
486 (void) PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_REMOVE);
490 void
491 nsCacheEntryHashTable::VisitEntries( PLDHashEnumerator etor, void *arg)
493 NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
494 if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
495 PL_DHashTableEnumerate(&table, etor, arg);
500 * hash table operation callback functions
503 PLDHashNumber
504 nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key)
506 return PL_DHashStringKey(table,((nsCString *)key)->get());
509 PRBool
510 nsCacheEntryHashTable::MatchEntry(PLDHashTable * /* table */,
511 const PLDHashEntryHdr * hashEntry,
512 const void * key)
514 NS_ASSERTION(key != nsnull, "### nsCacheEntryHashTable::MatchEntry : null key");
515 nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
517 return cacheEntry->mKey->Equals(*(nsCString *)key);
521 void
522 nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
523 const PLDHashEntryHdr *from,
524 PLDHashEntryHdr *to)
526 ((nsCacheEntryHashTableEntry *)to)->cacheEntry =
527 ((nsCacheEntryHashTableEntry *)from)->cacheEntry;
531 void
532 nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
533 PLDHashEntryHdr * hashEntry)
535 ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0;