1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsCacheEntry.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,528 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + 1.11 +#include "nsCache.h" 1.12 +#include "nspr.h" 1.13 +#include "nsCacheEntry.h" 1.14 +#include "nsCacheEntryDescriptor.h" 1.15 +#include "nsCacheMetaData.h" 1.16 +#include "nsCacheRequest.h" 1.17 +#include "nsThreadUtils.h" 1.18 +#include "nsError.h" 1.19 +#include "nsICacheService.h" 1.20 +#include "nsCacheService.h" 1.21 +#include "nsCacheDevice.h" 1.22 +#include "nsHashKeys.h" 1.23 +#include "mozilla/VisualEventTracer.h" 1.24 + 1.25 +using namespace mozilla; 1.26 + 1.27 +nsCacheEntry::nsCacheEntry(const nsACString & key, 1.28 + bool streamBased, 1.29 + nsCacheStoragePolicy storagePolicy) 1.30 + : mKey(key), 1.31 + mFetchCount(0), 1.32 + mLastFetched(0), 1.33 + mLastModified(0), 1.34 + mExpirationTime(nsICache::NO_EXPIRATION_TIME), 1.35 + mFlags(0), 1.36 + mPredictedDataSize(-1), 1.37 + mDataSize(0), 1.38 + mCacheDevice(nullptr), 1.39 + mCustomDevice(nullptr), 1.40 + mData(nullptr) 1.41 +{ 1.42 + MOZ_COUNT_CTOR(nsCacheEntry); 1.43 + PR_INIT_CLIST(this); 1.44 + PR_INIT_CLIST(&mRequestQ); 1.45 + PR_INIT_CLIST(&mDescriptorQ); 1.46 + 1.47 + if (streamBased) MarkStreamBased(); 1.48 + SetStoragePolicy(storagePolicy); 1.49 + 1.50 + MarkPublic(); 1.51 + 1.52 + MOZ_EVENT_TRACER_NAME_OBJECT(this, key.BeginReading()); 1.53 +} 1.54 + 1.55 + 1.56 +nsCacheEntry::~nsCacheEntry() 1.57 +{ 1.58 + MOZ_COUNT_DTOR(nsCacheEntry); 1.59 + 1.60 + if (mData) 1.61 + nsCacheService::ReleaseObject_Locked(mData, mThread); 1.62 +} 1.63 + 1.64 + 1.65 +nsresult 1.66 +nsCacheEntry::Create( const char * key, 1.67 + bool streamBased, 1.68 + nsCacheStoragePolicy storagePolicy, 1.69 + nsCacheDevice * device, 1.70 + nsCacheEntry ** result) 1.71 +{ 1.72 + nsCacheEntry* entry = new nsCacheEntry(nsCString(key), 1.73 + streamBased, 1.74 + storagePolicy); 1.75 + entry->SetCacheDevice(device); 1.76 + *result = entry; 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 + 1.81 +void 1.82 +nsCacheEntry::Fetched() 1.83 +{ 1.84 + mLastFetched = SecondsFromPRTime(PR_Now()); 1.85 + ++mFetchCount; 1.86 + MarkEntryDirty(); 1.87 +} 1.88 + 1.89 + 1.90 +const char * 1.91 +nsCacheEntry::GetDeviceID() 1.92 +{ 1.93 + if (mCacheDevice) return mCacheDevice->GetDeviceID(); 1.94 + return nullptr; 1.95 +} 1.96 + 1.97 + 1.98 +void 1.99 +nsCacheEntry::TouchData() 1.100 +{ 1.101 + mLastModified = SecondsFromPRTime(PR_Now()); 1.102 + MarkDataDirty(); 1.103 +} 1.104 + 1.105 + 1.106 +void 1.107 +nsCacheEntry::SetData(nsISupports * data) 1.108 +{ 1.109 + if (mData) { 1.110 + nsCacheService::ReleaseObject_Locked(mData, mThread); 1.111 + mData = nullptr; 1.112 + } 1.113 + 1.114 + if (data) { 1.115 + NS_ADDREF(mData = data); 1.116 + mThread = do_GetCurrentThread(); 1.117 + } 1.118 +} 1.119 + 1.120 + 1.121 +void 1.122 +nsCacheEntry::TouchMetaData() 1.123 +{ 1.124 + mLastModified = SecondsFromPRTime(PR_Now()); 1.125 + MarkMetaDataDirty(); 1.126 +} 1.127 + 1.128 + 1.129 +/** 1.130 + * cache entry states 1.131 + * 0 descriptors (new entry) 1.132 + * 0 descriptors (existing, bound entry) 1.133 + * n descriptors (existing, bound entry) valid 1.134 + * n descriptors (existing, bound entry) not valid (wait until valid or doomed) 1.135 + */ 1.136 + 1.137 +nsresult 1.138 +nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted) 1.139 +{ 1.140 + nsresult rv = NS_OK; 1.141 + 1.142 + if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED; 1.143 + 1.144 + if (!IsInitialized()) { 1.145 + // brand new, unbound entry 1.146 + if (request->IsStreamBased()) MarkStreamBased(); 1.147 + MarkInitialized(); 1.148 + 1.149 + *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE; 1.150 + NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request"); 1.151 + PR_APPEND_LINK(request, &mRequestQ); 1.152 + return rv; 1.153 + } 1.154 + 1.155 + if (IsStreamData() != request->IsStreamBased()) { 1.156 + *accessGranted = nsICache::ACCESS_NONE; 1.157 + return request->IsStreamBased() ? 1.158 + NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM; 1.159 + } 1.160 + 1.161 + if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) { 1.162 + // 1st descriptor for existing bound entry 1.163 + *accessGranted = request->AccessRequested(); 1.164 + if (*accessGranted & nsICache::ACCESS_WRITE) { 1.165 + MarkInvalid(); 1.166 + } else { 1.167 + MarkValid(); 1.168 + } 1.169 + } else { 1.170 + // nth request for existing, bound entry 1.171 + *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE; 1.172 + if (!IsValid()) 1.173 + rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION; 1.174 + } 1.175 + PR_APPEND_LINK(request,&mRequestQ); 1.176 + 1.177 + return rv; 1.178 +} 1.179 + 1.180 + 1.181 +nsresult 1.182 +nsCacheEntry::CreateDescriptor(nsCacheRequest * request, 1.183 + nsCacheAccessMode accessGranted, 1.184 + nsICacheEntryDescriptor ** result) 1.185 +{ 1.186 + NS_ENSURE_ARG_POINTER(request && result); 1.187 + 1.188 + nsCacheEntryDescriptor * descriptor = 1.189 + new nsCacheEntryDescriptor(this, accessGranted); 1.190 + 1.191 + // XXX check request is on q 1.192 + PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success 1.193 + 1.194 + if (descriptor == nullptr) 1.195 + return NS_ERROR_OUT_OF_MEMORY; 1.196 + 1.197 + PR_APPEND_LINK(descriptor, &mDescriptorQ); 1.198 + 1.199 + CACHE_LOG_DEBUG((" descriptor %p created for request %p on entry %p\n", 1.200 + descriptor, request, this)); 1.201 + 1.202 + NS_ADDREF(*result = descriptor); 1.203 + return NS_OK; 1.204 +} 1.205 + 1.206 + 1.207 +bool 1.208 +nsCacheEntry::RemoveRequest(nsCacheRequest * request) 1.209 +{ 1.210 + // XXX if debug: verify this request belongs to this entry 1.211 + PR_REMOVE_AND_INIT_LINK(request); 1.212 + 1.213 + // return true if this entry should stay active 1.214 + return !((PR_CLIST_IS_EMPTY(&mRequestQ)) && 1.215 + (PR_CLIST_IS_EMPTY(&mDescriptorQ))); 1.216 +} 1.217 + 1.218 + 1.219 +bool 1.220 +nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor, 1.221 + bool * doomEntry) 1.222 +{ 1.223 + NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!"); 1.224 + 1.225 + *doomEntry = descriptor->ClearCacheEntry(); 1.226 + 1.227 + PR_REMOVE_AND_INIT_LINK(descriptor); 1.228 + 1.229 + if (!PR_CLIST_IS_EMPTY(&mDescriptorQ)) 1.230 + return true; // stay active if we still have open descriptors 1.231 + 1.232 + if (PR_CLIST_IS_EMPTY(&mRequestQ)) 1.233 + return false; // no descriptors or requests, we can deactivate 1.234 + 1.235 + return true; // find next best request to give a descriptor to 1.236 +} 1.237 + 1.238 + 1.239 +void 1.240 +nsCacheEntry::DetachDescriptors() 1.241 +{ 1.242 + nsCacheEntryDescriptor * descriptor = 1.243 + (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ); 1.244 + 1.245 + while (descriptor != &mDescriptorQ) { 1.246 + nsCacheEntryDescriptor * nextDescriptor = 1.247 + (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor); 1.248 + 1.249 + descriptor->ClearCacheEntry(); 1.250 + PR_REMOVE_AND_INIT_LINK(descriptor); 1.251 + descriptor = nextDescriptor; 1.252 + } 1.253 +} 1.254 + 1.255 + 1.256 +void 1.257 +nsCacheEntry::GetDescriptors( 1.258 + nsTArray<nsRefPtr<nsCacheEntryDescriptor> > &outDescriptors) 1.259 +{ 1.260 + nsCacheEntryDescriptor * descriptor = 1.261 + (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ); 1.262 + 1.263 + while (descriptor != &mDescriptorQ) { 1.264 + nsCacheEntryDescriptor * nextDescriptor = 1.265 + (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor); 1.266 + 1.267 + outDescriptors.AppendElement(descriptor); 1.268 + descriptor = nextDescriptor; 1.269 + } 1.270 +} 1.271 + 1.272 + 1.273 +/****************************************************************************** 1.274 + * nsCacheEntryInfo - for implementing about:cache 1.275 + *****************************************************************************/ 1.276 + 1.277 +NS_IMPL_ISUPPORTS(nsCacheEntryInfo, nsICacheEntryInfo) 1.278 + 1.279 + 1.280 +NS_IMETHODIMP 1.281 +nsCacheEntryInfo::GetClientID(char ** clientID) 1.282 +{ 1.283 + NS_ENSURE_ARG_POINTER(clientID); 1.284 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.285 + 1.286 + return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID); 1.287 +} 1.288 + 1.289 + 1.290 +NS_IMETHODIMP 1.291 +nsCacheEntryInfo::GetDeviceID(char ** deviceID) 1.292 +{ 1.293 + NS_ENSURE_ARG_POINTER(deviceID); 1.294 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.295 + 1.296 + *deviceID = NS_strdup(mCacheEntry->GetDeviceID()); 1.297 + return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.298 +} 1.299 + 1.300 + 1.301 +NS_IMETHODIMP 1.302 +nsCacheEntryInfo::GetKey(nsACString &key) 1.303 +{ 1.304 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.305 + 1.306 + return ClientKeyFromCacheKey(*mCacheEntry->Key(), key); 1.307 +} 1.308 + 1.309 + 1.310 +NS_IMETHODIMP 1.311 +nsCacheEntryInfo::GetFetchCount(int32_t * fetchCount) 1.312 +{ 1.313 + NS_ENSURE_ARG_POINTER(fetchCount); 1.314 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.315 + 1.316 + *fetchCount = mCacheEntry->FetchCount(); 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 + 1.321 +NS_IMETHODIMP 1.322 +nsCacheEntryInfo::GetLastFetched(uint32_t * lastFetched) 1.323 +{ 1.324 + NS_ENSURE_ARG_POINTER(lastFetched); 1.325 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.326 + 1.327 + *lastFetched = mCacheEntry->LastFetched(); 1.328 + return NS_OK; 1.329 +} 1.330 + 1.331 + 1.332 +NS_IMETHODIMP 1.333 +nsCacheEntryInfo::GetLastModified(uint32_t * lastModified) 1.334 +{ 1.335 + NS_ENSURE_ARG_POINTER(lastModified); 1.336 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.337 + 1.338 + *lastModified = mCacheEntry->LastModified(); 1.339 + return NS_OK; 1.340 +} 1.341 + 1.342 + 1.343 +NS_IMETHODIMP 1.344 +nsCacheEntryInfo::GetExpirationTime(uint32_t * expirationTime) 1.345 +{ 1.346 + NS_ENSURE_ARG_POINTER(expirationTime); 1.347 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.348 + 1.349 + *expirationTime = mCacheEntry->ExpirationTime(); 1.350 + return NS_OK; 1.351 +} 1.352 + 1.353 + 1.354 +NS_IMETHODIMP 1.355 +nsCacheEntryInfo::GetDataSize(uint32_t * dataSize) 1.356 +{ 1.357 + NS_ENSURE_ARG_POINTER(dataSize); 1.358 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.359 + 1.360 + *dataSize = mCacheEntry->DataSize(); 1.361 + return NS_OK; 1.362 +} 1.363 + 1.364 + 1.365 +NS_IMETHODIMP 1.366 +nsCacheEntryInfo::IsStreamBased(bool * result) 1.367 +{ 1.368 + NS_ENSURE_ARG_POINTER(result); 1.369 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.370 + 1.371 + *result = mCacheEntry->IsStreamData(); 1.372 + return NS_OK; 1.373 +} 1.374 + 1.375 + 1.376 +/****************************************************************************** 1.377 + * nsCacheEntryHashTable 1.378 + *****************************************************************************/ 1.379 + 1.380 +const PLDHashTableOps 1.381 +nsCacheEntryHashTable::ops = 1.382 +{ 1.383 + PL_DHashAllocTable, 1.384 + PL_DHashFreeTable, 1.385 + HashKey, 1.386 + MatchEntry, 1.387 + MoveEntry, 1.388 + ClearEntry, 1.389 + PL_DHashFinalizeStub 1.390 +}; 1.391 + 1.392 + 1.393 +nsCacheEntryHashTable::nsCacheEntryHashTable() 1.394 + : initialized(false) 1.395 +{ 1.396 + MOZ_COUNT_CTOR(nsCacheEntryHashTable); 1.397 +} 1.398 + 1.399 + 1.400 +nsCacheEntryHashTable::~nsCacheEntryHashTable() 1.401 +{ 1.402 + MOZ_COUNT_DTOR(nsCacheEntryHashTable); 1.403 + if (initialized) 1.404 + Shutdown(); 1.405 +} 1.406 + 1.407 + 1.408 +nsresult 1.409 +nsCacheEntryHashTable::Init() 1.410 +{ 1.411 + nsresult rv = NS_OK; 1.412 + initialized = PL_DHashTableInit(&table, &ops, nullptr, 1.413 + sizeof(nsCacheEntryHashTableEntry), 1.414 + 512, fallible_t()); 1.415 + 1.416 + if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY; 1.417 + 1.418 + return rv; 1.419 +} 1.420 + 1.421 +void 1.422 +nsCacheEntryHashTable::Shutdown() 1.423 +{ 1.424 + if (initialized) { 1.425 + PL_DHashTableFinish(&table); 1.426 + initialized = false; 1.427 + } 1.428 +} 1.429 + 1.430 + 1.431 +nsCacheEntry * 1.432 +nsCacheEntryHashTable::GetEntry( const nsCString * key) 1.433 +{ 1.434 + PLDHashEntryHdr *hashEntry; 1.435 + nsCacheEntry *result = nullptr; 1.436 + 1.437 + NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized"); 1.438 + if (!initialized) return nullptr; 1.439 + 1.440 + hashEntry = PL_DHashTableOperate(&table, key, PL_DHASH_LOOKUP); 1.441 + if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) { 1.442 + result = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry; 1.443 + } 1.444 + return result; 1.445 +} 1.446 + 1.447 + 1.448 +nsresult 1.449 +nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry) 1.450 +{ 1.451 + PLDHashEntryHdr *hashEntry; 1.452 + 1.453 + NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized"); 1.454 + if (!initialized) return NS_ERROR_NOT_INITIALIZED; 1.455 + if (!cacheEntry) return NS_ERROR_NULL_POINTER; 1.456 + 1.457 + hashEntry = PL_DHashTableOperate(&table, &(cacheEntry->mKey), PL_DHASH_ADD); 1.458 +#ifndef DEBUG_dougt 1.459 + NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0, 1.460 + "### nsCacheEntryHashTable::AddEntry - entry already used"); 1.461 +#endif 1.462 + ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry; 1.463 + 1.464 + return NS_OK; 1.465 +} 1.466 + 1.467 + 1.468 +void 1.469 +nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry) 1.470 +{ 1.471 + NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized"); 1.472 + NS_ASSERTION(cacheEntry, "### cacheEntry == nullptr"); 1.473 + 1.474 + if (!initialized) return; // NS_ERROR_NOT_INITIALIZED 1.475 + 1.476 +#if DEBUG 1.477 + // XXX debug code to make sure we have the entry we're trying to remove 1.478 + nsCacheEntry *check = GetEntry(&(cacheEntry->mKey)); 1.479 + NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!"); 1.480 +#endif 1.481 + (void) PL_DHashTableOperate(&table, &(cacheEntry->mKey), PL_DHASH_REMOVE); 1.482 +} 1.483 + 1.484 + 1.485 +void 1.486 +nsCacheEntryHashTable::VisitEntries( PLDHashEnumerator etor, void *arg) 1.487 +{ 1.488 + NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized"); 1.489 + if (!initialized) return; // NS_ERROR_NOT_INITIALIZED 1.490 + PL_DHashTableEnumerate(&table, etor, arg); 1.491 +} 1.492 + 1.493 + 1.494 +/** 1.495 + * hash table operation callback functions 1.496 + */ 1.497 + 1.498 +PLDHashNumber 1.499 +nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key) 1.500 +{ 1.501 + return HashString(*static_cast<const nsCString *>(key)); 1.502 +} 1.503 + 1.504 +bool 1.505 +nsCacheEntryHashTable::MatchEntry(PLDHashTable * /* table */, 1.506 + const PLDHashEntryHdr * hashEntry, 1.507 + const void * key) 1.508 +{ 1.509 + NS_ASSERTION(key != nullptr, "### nsCacheEntryHashTable::MatchEntry : null key"); 1.510 + nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry; 1.511 + 1.512 + return cacheEntry->mKey.Equals(*(nsCString *)key); 1.513 +} 1.514 + 1.515 + 1.516 +void 1.517 +nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */, 1.518 + const PLDHashEntryHdr *from, 1.519 + PLDHashEntryHdr *to) 1.520 +{ 1.521 + ((nsCacheEntryHashTableEntry *)to)->cacheEntry = 1.522 + ((nsCacheEntryHashTableEntry *)from)->cacheEntry; 1.523 +} 1.524 + 1.525 + 1.526 +void 1.527 +nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */, 1.528 + PLDHashEntryHdr * hashEntry) 1.529 +{ 1.530 + ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0; 1.531 +}