1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsCacheEntryDescriptor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1476 @@ 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 +#include "nsICache.h" 1.11 +#include "nsCache.h" 1.12 +#include "nsCacheService.h" 1.13 +#include "nsCacheEntryDescriptor.h" 1.14 +#include "nsCacheEntry.h" 1.15 +#include "nsReadableUtils.h" 1.16 +#include "nsIOutputStream.h" 1.17 +#include "nsCRT.h" 1.18 +#include "nsThreadUtils.h" 1.19 +#include <algorithm> 1.20 + 1.21 +#define kMinDecompressReadBufLen 1024 1.22 +#define kMinCompressWriteBufLen 1024 1.23 + 1.24 + 1.25 +/****************************************************************************** 1.26 + * nsAsyncDoomEvent 1.27 + *****************************************************************************/ 1.28 + 1.29 +class nsAsyncDoomEvent : public nsRunnable { 1.30 +public: 1.31 + nsAsyncDoomEvent(nsCacheEntryDescriptor *descriptor, 1.32 + nsICacheListener *listener) 1.33 + { 1.34 + mDescriptor = descriptor; 1.35 + mListener = listener; 1.36 + mThread = do_GetCurrentThread(); 1.37 + // We addref the listener here and release it in nsNotifyDoomListener 1.38 + // on the callers thread. If posting of nsNotifyDoomListener event fails 1.39 + // we leak the listener which is better than releasing it on a wrong 1.40 + // thread. 1.41 + NS_IF_ADDREF(mListener); 1.42 + } 1.43 + 1.44 + NS_IMETHOD Run() 1.45 + { 1.46 + nsresult status = NS_OK; 1.47 + 1.48 + { 1.49 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSASYNCDOOMEVENT_RUN)); 1.50 + 1.51 + if (mDescriptor->mCacheEntry) { 1.52 + status = nsCacheService::gService->DoomEntry_Internal( 1.53 + mDescriptor->mCacheEntry, true); 1.54 + } else if (!mDescriptor->mDoomedOnClose) { 1.55 + status = NS_ERROR_NOT_AVAILABLE; 1.56 + } 1.57 + } 1.58 + 1.59 + if (mListener) { 1.60 + mThread->Dispatch(new nsNotifyDoomListener(mListener, status), 1.61 + NS_DISPATCH_NORMAL); 1.62 + // posted event will release the reference on the correct thread 1.63 + mListener = nullptr; 1.64 + } 1.65 + 1.66 + return NS_OK; 1.67 + } 1.68 + 1.69 +private: 1.70 + nsRefPtr<nsCacheEntryDescriptor> mDescriptor; 1.71 + nsICacheListener *mListener; 1.72 + nsCOMPtr<nsIThread> mThread; 1.73 +}; 1.74 + 1.75 + 1.76 +NS_IMPL_ISUPPORTS(nsCacheEntryDescriptor, 1.77 + nsICacheEntryDescriptor, 1.78 + nsICacheEntryInfo) 1.79 + 1.80 +nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry, 1.81 + nsCacheAccessMode accessGranted) 1.82 + : mCacheEntry(entry), 1.83 + mAccessGranted(accessGranted), 1.84 + mOutputWrapper(nullptr), 1.85 + mLock("nsCacheEntryDescriptor.mLock"), 1.86 + mAsyncDoomPending(false), 1.87 + mDoomedOnClose(false), 1.88 + mClosingDescriptor(false) 1.89 +{ 1.90 + PR_INIT_CLIST(this); 1.91 + NS_ADDREF(nsCacheService::GlobalInstance()); // ensure it lives for the lifetime of the descriptor 1.92 +} 1.93 + 1.94 + 1.95 +nsCacheEntryDescriptor::~nsCacheEntryDescriptor() 1.96 +{ 1.97 + // No need to close if the cache entry has already been severed. This 1.98 + // helps avoid a shutdown assertion (bug 285519) that is caused when 1.99 + // consumers end up holding onto these objects past xpcom-shutdown. It's 1.100 + // okay for them to do that because the cache service calls our Close 1.101 + // method during xpcom-shutdown, so we don't need to complain about it. 1.102 + if (mCacheEntry) 1.103 + Close(); 1.104 + 1.105 + NS_ASSERTION(mInputWrappers.Count() == 0, 1.106 + "We have still some input wrapper!"); 1.107 + NS_ASSERTION(!mOutputWrapper, "We have still an output wrapper!"); 1.108 + 1.109 + nsCacheService * service = nsCacheService::GlobalInstance(); 1.110 + NS_RELEASE(service); 1.111 +} 1.112 + 1.113 + 1.114 +NS_IMETHODIMP 1.115 +nsCacheEntryDescriptor::GetClientID(char ** result) 1.116 +{ 1.117 + NS_ENSURE_ARG_POINTER(result); 1.118 + 1.119 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID)); 1.120 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.121 + 1.122 + return ClientIDFromCacheKey(*(mCacheEntry->Key()), result); 1.123 +} 1.124 + 1.125 + 1.126 +NS_IMETHODIMP 1.127 +nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID) 1.128 +{ 1.129 + NS_ENSURE_ARG_POINTER(aDeviceID); 1.130 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDEVICEID)); 1.131 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.132 + 1.133 + const char* deviceID = mCacheEntry->GetDeviceID(); 1.134 + if (!deviceID) { 1.135 + *aDeviceID = nullptr; 1.136 + return NS_OK; 1.137 + } 1.138 + 1.139 + *aDeviceID = NS_strdup(deviceID); 1.140 + return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.141 +} 1.142 + 1.143 + 1.144 +NS_IMETHODIMP 1.145 +nsCacheEntryDescriptor::GetKey(nsACString &result) 1.146 +{ 1.147 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY)); 1.148 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.149 + 1.150 + return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result); 1.151 +} 1.152 + 1.153 + 1.154 +NS_IMETHODIMP 1.155 +nsCacheEntryDescriptor::GetFetchCount(int32_t *result) 1.156 +{ 1.157 + NS_ENSURE_ARG_POINTER(result); 1.158 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFETCHCOUNT)); 1.159 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.160 + 1.161 + *result = mCacheEntry->FetchCount(); 1.162 + return NS_OK; 1.163 +} 1.164 + 1.165 + 1.166 +NS_IMETHODIMP 1.167 +nsCacheEntryDescriptor::GetLastFetched(uint32_t *result) 1.168 +{ 1.169 + NS_ENSURE_ARG_POINTER(result); 1.170 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTFETCHED)); 1.171 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.172 + 1.173 + *result = mCacheEntry->LastFetched(); 1.174 + return NS_OK; 1.175 +} 1.176 + 1.177 + 1.178 +NS_IMETHODIMP 1.179 +nsCacheEntryDescriptor::GetLastModified(uint32_t *result) 1.180 +{ 1.181 + NS_ENSURE_ARG_POINTER(result); 1.182 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTMODIFIED)); 1.183 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.184 + 1.185 + *result = mCacheEntry->LastModified(); 1.186 + return NS_OK; 1.187 +} 1.188 + 1.189 + 1.190 +NS_IMETHODIMP 1.191 +nsCacheEntryDescriptor::GetExpirationTime(uint32_t *result) 1.192 +{ 1.193 + NS_ENSURE_ARG_POINTER(result); 1.194 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETEXPIRATIONTIME)); 1.195 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.196 + 1.197 + *result = mCacheEntry->ExpirationTime(); 1.198 + return NS_OK; 1.199 +} 1.200 + 1.201 + 1.202 +NS_IMETHODIMP 1.203 +nsCacheEntryDescriptor::SetExpirationTime(uint32_t expirationTime) 1.204 +{ 1.205 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETEXPIRATIONTIME)); 1.206 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.207 + 1.208 + mCacheEntry->SetExpirationTime(expirationTime); 1.209 + mCacheEntry->MarkEntryDirty(); 1.210 + return NS_OK; 1.211 +} 1.212 + 1.213 + 1.214 +NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(bool *result) 1.215 +{ 1.216 + NS_ENSURE_ARG_POINTER(result); 1.217 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_ISSTREAMBASED)); 1.218 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.219 + 1.220 + *result = mCacheEntry->IsStreamData(); 1.221 + return NS_OK; 1.222 +} 1.223 + 1.224 +NS_IMETHODIMP nsCacheEntryDescriptor::GetPredictedDataSize(int64_t *result) 1.225 +{ 1.226 + NS_ENSURE_ARG_POINTER(result); 1.227 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETPREDICTEDDATASIZE)); 1.228 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.229 + 1.230 + *result = mCacheEntry->PredictedDataSize(); 1.231 + return NS_OK; 1.232 +} 1.233 + 1.234 +NS_IMETHODIMP nsCacheEntryDescriptor::SetPredictedDataSize(int64_t 1.235 + predictedSize) 1.236 +{ 1.237 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETPREDICTEDDATASIZE)); 1.238 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.239 + 1.240 + mCacheEntry->SetPredictedDataSize(predictedSize); 1.241 + return NS_OK; 1.242 +} 1.243 + 1.244 +NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(uint32_t *result) 1.245 +{ 1.246 + NS_ENSURE_ARG_POINTER(result); 1.247 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDATASIZE)); 1.248 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.249 + 1.250 + const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len"); 1.251 + if (!val) { 1.252 + *result = mCacheEntry->DataSize(); 1.253 + } else { 1.254 + *result = atol(val); 1.255 + } 1.256 + 1.257 + return NS_OK; 1.258 +} 1.259 + 1.260 + 1.261 +NS_IMETHODIMP nsCacheEntryDescriptor::GetStorageDataSize(uint32_t *result) 1.262 +{ 1.263 + NS_ENSURE_ARG_POINTER(result); 1.264 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEDATASIZE)); 1.265 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.266 + 1.267 + *result = mCacheEntry->DataSize(); 1.268 + 1.269 + return NS_OK; 1.270 +} 1.271 + 1.272 + 1.273 +nsresult 1.274 +nsCacheEntryDescriptor::RequestDataSizeChange(int32_t deltaSize) 1.275 +{ 1.276 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE)); 1.277 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.278 + 1.279 + nsresult rv; 1.280 + rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize); 1.281 + if (NS_SUCCEEDED(rv)) { 1.282 + // XXX review for signed/unsigned math errors 1.283 + uint32_t newDataSize = mCacheEntry->DataSize() + deltaSize; 1.284 + mCacheEntry->SetDataSize(newDataSize); 1.285 + mCacheEntry->TouchData(); 1.286 + } 1.287 + return rv; 1.288 +} 1.289 + 1.290 + 1.291 +NS_IMETHODIMP 1.292 +nsCacheEntryDescriptor::SetDataSize(uint32_t dataSize) 1.293 +{ 1.294 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETDATASIZE)); 1.295 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.296 + 1.297 + // XXX review for signed/unsigned math errors 1.298 + int32_t deltaSize = dataSize - mCacheEntry->DataSize(); 1.299 + 1.300 + nsresult rv; 1.301 + rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize); 1.302 + // this had better be NS_OK, this call instance is advisory for memory cache objects 1.303 + if (NS_SUCCEEDED(rv)) { 1.304 + // XXX review for signed/unsigned math errors 1.305 + uint32_t newDataSize = mCacheEntry->DataSize() + deltaSize; 1.306 + mCacheEntry->SetDataSize(newDataSize); 1.307 + mCacheEntry->TouchData(); 1.308 + } else { 1.309 + NS_WARNING("failed SetDataSize() on memory cache object!"); 1.310 + } 1.311 + 1.312 + return rv; 1.313 +} 1.314 + 1.315 + 1.316 +NS_IMETHODIMP 1.317 +nsCacheEntryDescriptor::OpenInputStream(uint32_t offset, nsIInputStream ** result) 1.318 +{ 1.319 + NS_ENSURE_ARG_POINTER(result); 1.320 + 1.321 + nsInputStreamWrapper* cacheInput = nullptr; 1.322 + { 1.323 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENINPUTSTREAM)); 1.324 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.325 + if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM; 1.326 + 1.327 + // Don't open any new stream when closing descriptor or clearing entries 1.328 + if (mClosingDescriptor || nsCacheService::GetClearingEntries()) 1.329 + return NS_ERROR_NOT_AVAILABLE; 1.330 + 1.331 + // ensure valid permissions 1.332 + if (!(mAccessGranted & nsICache::ACCESS_READ)) 1.333 + return NS_ERROR_CACHE_READ_ACCESS_DENIED; 1.334 + 1.335 + const char *val; 1.336 + val = mCacheEntry->GetMetaDataElement("uncompressed-len"); 1.337 + if (val) { 1.338 + cacheInput = new nsDecompressInputStreamWrapper(this, offset); 1.339 + } else { 1.340 + cacheInput = new nsInputStreamWrapper(this, offset); 1.341 + } 1.342 + if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY; 1.343 + 1.344 + mInputWrappers.AppendElement(cacheInput); 1.345 + } 1.346 + 1.347 + NS_ADDREF(*result = cacheInput); 1.348 + return NS_OK; 1.349 +} 1.350 + 1.351 +NS_IMETHODIMP 1.352 +nsCacheEntryDescriptor::OpenOutputStream(uint32_t offset, nsIOutputStream ** result) 1.353 +{ 1.354 + NS_ENSURE_ARG_POINTER(result); 1.355 + 1.356 + nsOutputStreamWrapper* cacheOutput = nullptr; 1.357 + { 1.358 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENOUTPUTSTREAM)); 1.359 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.360 + if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM; 1.361 + 1.362 + // Don't open any new stream when closing descriptor or clearing entries 1.363 + if (mClosingDescriptor || nsCacheService::GetClearingEntries()) 1.364 + return NS_ERROR_NOT_AVAILABLE; 1.365 + 1.366 + // ensure valid permissions 1.367 + if (!(mAccessGranted & nsICache::ACCESS_WRITE)) 1.368 + return NS_ERROR_CACHE_WRITE_ACCESS_DENIED; 1.369 + 1.370 + int32_t compressionLevel = nsCacheService::CacheCompressionLevel(); 1.371 + const char *val; 1.372 + val = mCacheEntry->GetMetaDataElement("uncompressed-len"); 1.373 + if ((compressionLevel > 0) && val) { 1.374 + cacheOutput = new nsCompressOutputStreamWrapper(this, offset); 1.375 + } else { 1.376 + // clear compression flag when compression disabled - see bug 715198 1.377 + if (val) { 1.378 + mCacheEntry->SetMetaDataElement("uncompressed-len", nullptr); 1.379 + } 1.380 + cacheOutput = new nsOutputStreamWrapper(this, offset); 1.381 + } 1.382 + if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY; 1.383 + 1.384 + mOutputWrapper = cacheOutput; 1.385 + } 1.386 + 1.387 + NS_ADDREF(*result = cacheOutput); 1.388 + return NS_OK; 1.389 +} 1.390 + 1.391 + 1.392 +NS_IMETHODIMP 1.393 +nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result) 1.394 +{ 1.395 + NS_ENSURE_ARG_POINTER(result); 1.396 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCACHEELEMENT)); 1.397 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.398 + if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM; 1.399 + 1.400 + NS_IF_ADDREF(*result = mCacheEntry->Data()); 1.401 + return NS_OK; 1.402 +} 1.403 + 1.404 + 1.405 +NS_IMETHODIMP 1.406 +nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement) 1.407 +{ 1.408 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT)); 1.409 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.410 + if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM; 1.411 + 1.412 + return nsCacheService::SetCacheElement(mCacheEntry, cacheElement); 1.413 +} 1.414 + 1.415 + 1.416 +NS_IMETHODIMP 1.417 +nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result) 1.418 +{ 1.419 + NS_ENSURE_ARG_POINTER(result); 1.420 + *result = mAccessGranted; 1.421 + return NS_OK; 1.422 +} 1.423 + 1.424 + 1.425 +NS_IMETHODIMP 1.426 +nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result) 1.427 +{ 1.428 + NS_ENSURE_ARG_POINTER(result); 1.429 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEPOLICY)); 1.430 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.431 + 1.432 + *result = mCacheEntry->StoragePolicy(); 1.433 + return NS_OK; 1.434 +} 1.435 + 1.436 + 1.437 +NS_IMETHODIMP 1.438 +nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy) 1.439 +{ 1.440 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSTORAGEPOLICY)); 1.441 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.442 + // XXX validate policy against session? 1.443 + 1.444 + bool storageEnabled = false; 1.445 + storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy); 1.446 + if (!storageEnabled) return NS_ERROR_FAILURE; 1.447 + 1.448 + // Don't change the storage policy of entries we can't write 1.449 + if (!(mAccessGranted & nsICache::ACCESS_WRITE)) 1.450 + return NS_ERROR_NOT_AVAILABLE; 1.451 + 1.452 + // Don't allow a cache entry to move from memory-only to anything else 1.453 + if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY && 1.454 + policy != nsICache::STORE_IN_MEMORY) 1.455 + return NS_ERROR_NOT_AVAILABLE; 1.456 + 1.457 + mCacheEntry->SetStoragePolicy(policy); 1.458 + mCacheEntry->MarkEntryDirty(); 1.459 + return NS_OK; 1.460 +} 1.461 + 1.462 + 1.463 +NS_IMETHODIMP 1.464 +nsCacheEntryDescriptor::GetFile(nsIFile ** result) 1.465 +{ 1.466 + NS_ENSURE_ARG_POINTER(result); 1.467 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFILE)); 1.468 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.469 + 1.470 + return nsCacheService::GetFileForEntry(mCacheEntry, result); 1.471 +} 1.472 + 1.473 + 1.474 +NS_IMETHODIMP 1.475 +nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result) 1.476 +{ 1.477 + NS_ENSURE_ARG_POINTER(result); 1.478 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSECURITYINFO)); 1.479 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.480 + 1.481 + *result = mCacheEntry->SecurityInfo(); 1.482 + NS_IF_ADDREF(*result); 1.483 + return NS_OK; 1.484 +} 1.485 + 1.486 + 1.487 +NS_IMETHODIMP 1.488 +nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo) 1.489 +{ 1.490 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO)); 1.491 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.492 + 1.493 + mCacheEntry->SetSecurityInfo(securityInfo); 1.494 + mCacheEntry->MarkEntryDirty(); 1.495 + return NS_OK; 1.496 +} 1.497 + 1.498 + 1.499 +NS_IMETHODIMP 1.500 +nsCacheEntryDescriptor::Doom() 1.501 +{ 1.502 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM)); 1.503 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.504 + 1.505 + return nsCacheService::DoomEntry(mCacheEntry); 1.506 +} 1.507 + 1.508 + 1.509 +NS_IMETHODIMP 1.510 +nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status) 1.511 +{ 1.512 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS)); 1.513 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.514 + 1.515 + return NS_ERROR_NOT_IMPLEMENTED; 1.516 +} 1.517 + 1.518 + 1.519 +NS_IMETHODIMP 1.520 +nsCacheEntryDescriptor::AsyncDoom(nsICacheListener *listener) 1.521 +{ 1.522 + bool asyncDoomPending; 1.523 + { 1.524 + mozilla::MutexAutoLock lock(mLock); 1.525 + asyncDoomPending = mAsyncDoomPending; 1.526 + mAsyncDoomPending = true; 1.527 + } 1.528 + 1.529 + if (asyncDoomPending) { 1.530 + // AsyncDoom was already called. Notify listener if it is non-null, 1.531 + // otherwise just return success. 1.532 + if (listener) { 1.533 + nsresult rv = NS_DispatchToCurrentThread( 1.534 + new nsNotifyDoomListener(listener, NS_ERROR_NOT_AVAILABLE)); 1.535 + if (NS_SUCCEEDED(rv)) 1.536 + NS_IF_ADDREF(listener); 1.537 + return rv; 1.538 + } 1.539 + return NS_OK; 1.540 + } 1.541 + 1.542 + nsRefPtr<nsIRunnable> event = new nsAsyncDoomEvent(this, listener); 1.543 + return nsCacheService::DispatchToCacheIOThread(event); 1.544 +} 1.545 + 1.546 + 1.547 +NS_IMETHODIMP 1.548 +nsCacheEntryDescriptor::MarkValid() 1.549 +{ 1.550 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID)); 1.551 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.552 + 1.553 + nsresult rv = nsCacheService::ValidateEntry(mCacheEntry); 1.554 + return rv; 1.555 +} 1.556 + 1.557 + 1.558 +NS_IMETHODIMP 1.559 +nsCacheEntryDescriptor::Close() 1.560 +{ 1.561 + nsRefPtr<nsOutputStreamWrapper> outputWrapper; 1.562 + nsTArray<nsRefPtr<nsInputStreamWrapper> > inputWrappers; 1.563 + 1.564 + { 1.565 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE)); 1.566 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.567 + 1.568 + // Make sure no other stream can be opened 1.569 + mClosingDescriptor = true; 1.570 + outputWrapper = mOutputWrapper; 1.571 + for (int32_t i = 0 ; i < mInputWrappers.Count() ; i++) 1.572 + inputWrappers.AppendElement(static_cast<nsInputStreamWrapper *>( 1.573 + mInputWrappers[i])); 1.574 + } 1.575 + 1.576 + // Call Close() on the streams outside the lock since it might need to call 1.577 + // methods that grab the cache service lock, e.g. compressed output stream 1.578 + // when it finalizes the entry 1.579 + if (outputWrapper) { 1.580 + if (NS_FAILED(outputWrapper->Close())) { 1.581 + NS_WARNING("Dooming entry because Close() failed!!!"); 1.582 + Doom(); 1.583 + } 1.584 + outputWrapper = nullptr; 1.585 + } 1.586 + 1.587 + for (uint32_t i = 0 ; i < inputWrappers.Length() ; i++) 1.588 + inputWrappers[i]->Close(); 1.589 + 1.590 + inputWrappers.Clear(); 1.591 + 1.592 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE)); 1.593 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.594 + 1.595 + // XXX perhaps closing descriptors should clear/sever transports 1.596 + 1.597 + // tell nsCacheService we're going away 1.598 + nsCacheService::CloseDescriptor(this); 1.599 + NS_ASSERTION(mCacheEntry == nullptr, "mCacheEntry not null"); 1.600 + 1.601 + return NS_OK; 1.602 +} 1.603 + 1.604 + 1.605 +NS_IMETHODIMP 1.606 +nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result) 1.607 +{ 1.608 + NS_ENSURE_ARG_POINTER(key); 1.609 + *result = nullptr; 1.610 + 1.611 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT)); 1.612 + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE); 1.613 + 1.614 + const char *value; 1.615 + 1.616 + value = mCacheEntry->GetMetaDataElement(key); 1.617 + if (!value) return NS_ERROR_NOT_AVAILABLE; 1.618 + 1.619 + *result = NS_strdup(value); 1.620 + if (!*result) return NS_ERROR_OUT_OF_MEMORY; 1.621 + 1.622 + return NS_OK; 1.623 +} 1.624 + 1.625 + 1.626 +NS_IMETHODIMP 1.627 +nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value) 1.628 +{ 1.629 + NS_ENSURE_ARG_POINTER(key); 1.630 + 1.631 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT)); 1.632 + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE); 1.633 + 1.634 + // XXX allow null value, for clearing key? 1.635 + 1.636 + nsresult rv = mCacheEntry->SetMetaDataElement(key, value); 1.637 + if (NS_SUCCEEDED(rv)) 1.638 + mCacheEntry->TouchMetaData(); 1.639 + return rv; 1.640 +} 1.641 + 1.642 + 1.643 +NS_IMETHODIMP 1.644 +nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor) 1.645 +{ 1.646 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_VISITMETADATA)); 1.647 + // XXX check callers, we're calling out of module 1.648 + NS_ENSURE_ARG_POINTER(visitor); 1.649 + if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.650 + 1.651 + return mCacheEntry->VisitMetaDataElements(visitor); 1.652 +} 1.653 + 1.654 + 1.655 +/****************************************************************************** 1.656 + * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry 1.657 + * open while referenced. 1.658 + ******************************************************************************/ 1.659 + 1.660 +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsInputStreamWrapper) 1.661 +NS_IMETHODIMP_(MozExternalRefCountType) 1.662 +nsCacheEntryDescriptor::nsInputStreamWrapper::Release() 1.663 +{ 1.664 + // Holding a reference to descriptor ensures that cache service won't go 1.665 + // away. Do not grab cache service lock if there is no descriptor. 1.666 + nsRefPtr<nsCacheEntryDescriptor> desc; 1.667 + 1.668 + { 1.669 + mozilla::MutexAutoLock lock(mLock); 1.670 + desc = mDescriptor; 1.671 + } 1.672 + 1.673 + if (desc) 1.674 + nsCacheService::Lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_RELEASE)); 1.675 + 1.676 + nsrefcnt count; 1.677 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.678 + count = --mRefCnt; 1.679 + NS_LOG_RELEASE(this, count, "nsCacheEntryDescriptor::nsInputStreamWrapper"); 1.680 + 1.681 + if (0 == count) { 1.682 + // don't use desc here since mDescriptor might be already nulled out 1.683 + if (mDescriptor) { 1.684 + NS_ASSERTION(mDescriptor->mInputWrappers.IndexOf(this) != -1, 1.685 + "Wrapper not found in array!"); 1.686 + mDescriptor->mInputWrappers.RemoveElement(this); 1.687 + } 1.688 + 1.689 + if (desc) 1.690 + nsCacheService::Unlock(); 1.691 + 1.692 + mRefCnt = 1; 1.693 + delete (this); 1.694 + return 0; 1.695 + } 1.696 + 1.697 + if (desc) 1.698 + nsCacheService::Unlock(); 1.699 + 1.700 + return count; 1.701 +} 1.702 + 1.703 +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsInputStreamWrapper) 1.704 + NS_INTERFACE_MAP_ENTRY(nsIInputStream) 1.705 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.706 +NS_INTERFACE_MAP_END_THREADSAFE 1.707 + 1.708 +nsresult nsCacheEntryDescriptor:: 1.709 +nsInputStreamWrapper::LazyInit() 1.710 +{ 1.711 + // Check if we have the descriptor. If not we can't even grab the cache 1.712 + // lock since it is not ensured that the cache service still exists. 1.713 + if (!mDescriptor) 1.714 + return NS_ERROR_NOT_AVAILABLE; 1.715 + 1.716 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT)); 1.717 + 1.718 + nsCacheAccessMode mode; 1.719 + nsresult rv = mDescriptor->GetAccessGranted(&mode); 1.720 + if (NS_FAILED(rv)) return rv; 1.721 + 1.722 + NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED); 1.723 + 1.724 + nsCacheEntry* cacheEntry = mDescriptor->CacheEntry(); 1.725 + if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.726 + 1.727 + rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode, 1.728 + mStartOffset, 1.729 + getter_AddRefs(mInput)); 1.730 + 1.731 + CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit " 1.732 + "[entry=%p, wrapper=%p, mInput=%p, rv=%d]", 1.733 + mDescriptor, this, mInput.get(), int(rv))); 1.734 + 1.735 + if (NS_FAILED(rv)) return rv; 1.736 + 1.737 + mInitialized = true; 1.738 + return NS_OK; 1.739 +} 1.740 + 1.741 +nsresult nsCacheEntryDescriptor:: 1.742 +nsInputStreamWrapper::EnsureInit() 1.743 +{ 1.744 + if (mInitialized) { 1.745 + NS_ASSERTION(mDescriptor, "Bad state"); 1.746 + return NS_OK; 1.747 + } 1.748 + 1.749 + return LazyInit(); 1.750 +} 1.751 + 1.752 +void nsCacheEntryDescriptor:: 1.753 +nsInputStreamWrapper::CloseInternal() 1.754 +{ 1.755 + mLock.AssertCurrentThreadOwns(); 1.756 + if (!mDescriptor) { 1.757 + NS_ASSERTION(!mInitialized, "Bad state"); 1.758 + NS_ASSERTION(!mInput, "Bad state"); 1.759 + return; 1.760 + } 1.761 + 1.762 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_CLOSEINTERNAL)); 1.763 + 1.764 + if (mDescriptor) { 1.765 + mDescriptor->mInputWrappers.RemoveElement(this); 1.766 + nsCacheService::ReleaseObject_Locked(mDescriptor); 1.767 + mDescriptor = nullptr; 1.768 + } 1.769 + mInitialized = false; 1.770 + mInput = nullptr; 1.771 +} 1.772 + 1.773 +nsresult nsCacheEntryDescriptor:: 1.774 +nsInputStreamWrapper::Close() 1.775 +{ 1.776 + mozilla::MutexAutoLock lock(mLock); 1.777 + 1.778 + return Close_Locked(); 1.779 +} 1.780 + 1.781 +nsresult nsCacheEntryDescriptor:: 1.782 +nsInputStreamWrapper::Close_Locked() 1.783 +{ 1.784 + nsresult rv = EnsureInit(); 1.785 + if (NS_SUCCEEDED(rv)) { 1.786 + rv = mInput->Close(); 1.787 + } else { 1.788 + NS_ASSERTION(!mInput, 1.789 + "Shouldn't have mInput when EnsureInit() failed"); 1.790 + } 1.791 + 1.792 + // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are 1.793 + // closing streams with nsCacheService::CloseAllStream() 1.794 + CloseInternal(); 1.795 + return rv; 1.796 +} 1.797 + 1.798 +nsresult nsCacheEntryDescriptor:: 1.799 +nsInputStreamWrapper::Available(uint64_t *avail) 1.800 +{ 1.801 + mozilla::MutexAutoLock lock(mLock); 1.802 + 1.803 + nsresult rv = EnsureInit(); 1.804 + if (NS_FAILED(rv)) return rv; 1.805 + 1.806 + return mInput->Available(avail); 1.807 +} 1.808 + 1.809 +nsresult nsCacheEntryDescriptor:: 1.810 +nsInputStreamWrapper::Read(char *buf, uint32_t count, uint32_t *countRead) 1.811 +{ 1.812 + mozilla::MutexAutoLock lock(mLock); 1.813 + 1.814 + return Read_Locked(buf, count, countRead); 1.815 +} 1.816 + 1.817 +nsresult nsCacheEntryDescriptor:: 1.818 +nsInputStreamWrapper::Read_Locked(char *buf, uint32_t count, uint32_t *countRead) 1.819 +{ 1.820 + nsresult rv = EnsureInit(); 1.821 + if (NS_SUCCEEDED(rv)) 1.822 + rv = mInput->Read(buf, count, countRead); 1.823 + 1.824 + CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read " 1.825 + "[entry=%p, wrapper=%p, mInput=%p, rv=%d]", 1.826 + mDescriptor, this, mInput.get(), rv)); 1.827 + 1.828 + return rv; 1.829 +} 1.830 + 1.831 +nsresult nsCacheEntryDescriptor:: 1.832 +nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure, 1.833 + uint32_t count, uint32_t *countRead) 1.834 +{ 1.835 + // cache stream not buffered 1.836 + return NS_ERROR_NOT_IMPLEMENTED; 1.837 +} 1.838 + 1.839 +nsresult nsCacheEntryDescriptor:: 1.840 +nsInputStreamWrapper::IsNonBlocking(bool *result) 1.841 +{ 1.842 + // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK 1.843 + *result = false; 1.844 + return NS_OK; 1.845 +} 1.846 + 1.847 + 1.848 +/****************************************************************************** 1.849 + * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses 1.850 + ******************************************************************************/ 1.851 + 1.852 +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper) 1.853 +NS_IMETHODIMP_(MozExternalRefCountType) 1.854 +nsCacheEntryDescriptor::nsDecompressInputStreamWrapper::Release() 1.855 +{ 1.856 + // Holding a reference to descriptor ensures that cache service won't go 1.857 + // away. Do not grab cache service lock if there is no descriptor. 1.858 + nsRefPtr<nsCacheEntryDescriptor> desc; 1.859 + 1.860 + { 1.861 + mozilla::MutexAutoLock lock(mLock); 1.862 + desc = mDescriptor; 1.863 + } 1.864 + 1.865 + if (desc) 1.866 + nsCacheService::Lock(LOCK_TELEM( 1.867 + NSDECOMPRESSINPUTSTREAMWRAPPER_RELEASE)); 1.868 + 1.869 + nsrefcnt count; 1.870 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.871 + count = --mRefCnt; 1.872 + NS_LOG_RELEASE(this, count, 1.873 + "nsCacheEntryDescriptor::nsDecompressInputStreamWrapper"); 1.874 + 1.875 + if (0 == count) { 1.876 + // don't use desc here since mDescriptor might be already nulled out 1.877 + if (mDescriptor) { 1.878 + NS_ASSERTION(mDescriptor->mInputWrappers.IndexOf(this) != -1, 1.879 + "Wrapper not found in array!"); 1.880 + mDescriptor->mInputWrappers.RemoveElement(this); 1.881 + } 1.882 + 1.883 + if (desc) 1.884 + nsCacheService::Unlock(); 1.885 + 1.886 + mRefCnt = 1; 1.887 + delete (this); 1.888 + return 0; 1.889 + } 1.890 + 1.891 + if (desc) 1.892 + nsCacheService::Unlock(); 1.893 + 1.894 + return count; 1.895 +} 1.896 + 1.897 +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper) 1.898 + NS_INTERFACE_MAP_ENTRY(nsIInputStream) 1.899 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.900 +NS_INTERFACE_MAP_END_THREADSAFE 1.901 + 1.902 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.903 +nsDecompressInputStreamWrapper::Read(char * buf, 1.904 + uint32_t count, 1.905 + uint32_t *countRead) 1.906 +{ 1.907 + mozilla::MutexAutoLock lock(mLock); 1.908 + 1.909 + int zerr = Z_OK; 1.910 + nsresult rv = NS_OK; 1.911 + 1.912 + if (!mStreamInitialized) { 1.913 + rv = InitZstream(); 1.914 + if (NS_FAILED(rv)) { 1.915 + return rv; 1.916 + } 1.917 + } 1.918 + 1.919 + mZstream.next_out = (Bytef*)buf; 1.920 + mZstream.avail_out = count; 1.921 + 1.922 + if (mReadBufferLen < count) { 1.923 + // Allocate a buffer for reading from the input stream. This will 1.924 + // determine the max number of compressed bytes read from the 1.925 + // input stream at one time. Making the buffer size proportional 1.926 + // to the request size is not necessary, but helps minimize the 1.927 + // number of read requests to the input stream. 1.928 + uint32_t newBufLen = std::max(count, (uint32_t)kMinDecompressReadBufLen); 1.929 + unsigned char* newBuf; 1.930 + newBuf = (unsigned char*)nsMemory::Realloc(mReadBuffer, 1.931 + newBufLen); 1.932 + if (newBuf) { 1.933 + mReadBuffer = newBuf; 1.934 + mReadBufferLen = newBufLen; 1.935 + } 1.936 + if (!mReadBuffer) { 1.937 + mReadBufferLen = 0; 1.938 + return NS_ERROR_OUT_OF_MEMORY; 1.939 + } 1.940 + } 1.941 + 1.942 + // read and inflate data until the output buffer is full, or 1.943 + // there is no more data to read 1.944 + while (NS_SUCCEEDED(rv) && 1.945 + zerr == Z_OK && 1.946 + mZstream.avail_out > 0 && 1.947 + count > 0) { 1.948 + if (mZstream.avail_in == 0) { 1.949 + rv = nsInputStreamWrapper::Read_Locked((char*)mReadBuffer, 1.950 + mReadBufferLen, 1.951 + &mZstream.avail_in); 1.952 + if (NS_FAILED(rv) || !mZstream.avail_in) { 1.953 + break; 1.954 + } 1.955 + mZstream.next_in = mReadBuffer; 1.956 + } 1.957 + zerr = inflate(&mZstream, Z_NO_FLUSH); 1.958 + if (zerr == Z_STREAM_END) { 1.959 + // The compressed data may have been stored in multiple 1.960 + // chunks/streams. To allow for this case, re-initialize 1.961 + // the inflate stream and continue decompressing from 1.962 + // the next byte. 1.963 + Bytef * saveNextIn = mZstream.next_in; 1.964 + unsigned int saveAvailIn = mZstream.avail_in; 1.965 + Bytef * saveNextOut = mZstream.next_out; 1.966 + unsigned int saveAvailOut = mZstream.avail_out; 1.967 + inflateReset(&mZstream); 1.968 + mZstream.next_in = saveNextIn; 1.969 + mZstream.avail_in = saveAvailIn; 1.970 + mZstream.next_out = saveNextOut; 1.971 + mZstream.avail_out = saveAvailOut; 1.972 + zerr = Z_OK; 1.973 + } else if (zerr != Z_OK) { 1.974 + rv = NS_ERROR_INVALID_CONTENT_ENCODING; 1.975 + } 1.976 + } 1.977 + if (NS_SUCCEEDED(rv)) { 1.978 + *countRead = count - mZstream.avail_out; 1.979 + } 1.980 + return rv; 1.981 +} 1.982 + 1.983 +nsresult nsCacheEntryDescriptor:: 1.984 +nsDecompressInputStreamWrapper::Close() 1.985 +{ 1.986 + mozilla::MutexAutoLock lock(mLock); 1.987 + 1.988 + if (!mDescriptor) 1.989 + return NS_ERROR_NOT_AVAILABLE; 1.990 + 1.991 + EndZstream(); 1.992 + if (mReadBuffer) { 1.993 + nsMemory::Free(mReadBuffer); 1.994 + mReadBuffer = 0; 1.995 + mReadBufferLen = 0; 1.996 + } 1.997 + return nsInputStreamWrapper::Close_Locked(); 1.998 +} 1.999 + 1.1000 +nsresult nsCacheEntryDescriptor:: 1.1001 +nsDecompressInputStreamWrapper::InitZstream() 1.1002 +{ 1.1003 + if (!mDescriptor) 1.1004 + return NS_ERROR_NOT_AVAILABLE; 1.1005 + 1.1006 + if (mStreamEnded) 1.1007 + return NS_ERROR_FAILURE; 1.1008 + 1.1009 + // Initialize zlib inflate stream 1.1010 + mZstream.zalloc = Z_NULL; 1.1011 + mZstream.zfree = Z_NULL; 1.1012 + mZstream.opaque = Z_NULL; 1.1013 + mZstream.next_out = Z_NULL; 1.1014 + mZstream.avail_out = 0; 1.1015 + mZstream.next_in = Z_NULL; 1.1016 + mZstream.avail_in = 0; 1.1017 + if (inflateInit(&mZstream) != Z_OK) { 1.1018 + return NS_ERROR_FAILURE; 1.1019 + } 1.1020 + mStreamInitialized = true; 1.1021 + return NS_OK; 1.1022 +} 1.1023 + 1.1024 +nsresult nsCacheEntryDescriptor:: 1.1025 +nsDecompressInputStreamWrapper::EndZstream() 1.1026 +{ 1.1027 + if (mStreamInitialized && !mStreamEnded) { 1.1028 + inflateEnd(&mZstream); 1.1029 + mStreamInitialized = false; 1.1030 + mStreamEnded = true; 1.1031 + } 1.1032 + return NS_OK; 1.1033 +} 1.1034 + 1.1035 + 1.1036 +/****************************************************************************** 1.1037 + * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of 1.1038 + * data written to a cache entry. 1.1039 + * - also keeps the cache entry open while referenced. 1.1040 + ******************************************************************************/ 1.1041 + 1.1042 +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsOutputStreamWrapper) 1.1043 +NS_IMETHODIMP_(MozExternalRefCountType) 1.1044 +nsCacheEntryDescriptor::nsOutputStreamWrapper::Release() 1.1045 +{ 1.1046 + // Holding a reference to descriptor ensures that cache service won't go 1.1047 + // away. Do not grab cache service lock if there is no descriptor. 1.1048 + nsRefPtr<nsCacheEntryDescriptor> desc; 1.1049 + 1.1050 + { 1.1051 + mozilla::MutexAutoLock lock(mLock); 1.1052 + desc = mDescriptor; 1.1053 + } 1.1054 + 1.1055 + if (desc) 1.1056 + nsCacheService::Lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_RELEASE)); 1.1057 + 1.1058 + nsrefcnt count; 1.1059 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.1060 + count = --mRefCnt; 1.1061 + NS_LOG_RELEASE(this, count, 1.1062 + "nsCacheEntryDescriptor::nsOutputStreamWrapper"); 1.1063 + 1.1064 + if (0 == count) { 1.1065 + // don't use desc here since mDescriptor might be already nulled out 1.1066 + if (mDescriptor) 1.1067 + mDescriptor->mOutputWrapper = nullptr; 1.1068 + 1.1069 + if (desc) 1.1070 + nsCacheService::Unlock(); 1.1071 + 1.1072 + mRefCnt = 1; 1.1073 + delete (this); 1.1074 + return 0; 1.1075 + } 1.1076 + 1.1077 + if (desc) 1.1078 + nsCacheService::Unlock(); 1.1079 + 1.1080 + return count; 1.1081 +} 1.1082 + 1.1083 +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsOutputStreamWrapper) 1.1084 + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) 1.1085 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.1086 +NS_INTERFACE_MAP_END_THREADSAFE 1.1087 + 1.1088 +nsresult nsCacheEntryDescriptor:: 1.1089 +nsOutputStreamWrapper::LazyInit() 1.1090 +{ 1.1091 + // Check if we have the descriptor. If not we can't even grab the cache 1.1092 + // lock since it is not ensured that the cache service still exists. 1.1093 + if (!mDescriptor) 1.1094 + return NS_ERROR_NOT_AVAILABLE; 1.1095 + 1.1096 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT)); 1.1097 + 1.1098 + nsCacheAccessMode mode; 1.1099 + nsresult rv = mDescriptor->GetAccessGranted(&mode); 1.1100 + if (NS_FAILED(rv)) return rv; 1.1101 + 1.1102 + NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED); 1.1103 + 1.1104 + nsCacheEntry* cacheEntry = mDescriptor->CacheEntry(); 1.1105 + if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE; 1.1106 + 1.1107 + NS_ASSERTION(mOutput == nullptr, "mOutput set in LazyInit"); 1.1108 + 1.1109 + nsCOMPtr<nsIOutputStream> stream; 1.1110 + rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset, 1.1111 + getter_AddRefs(stream)); 1.1112 + if (NS_FAILED(rv)) 1.1113 + return rv; 1.1114 + 1.1115 + nsCacheDevice* device = cacheEntry->CacheDevice(); 1.1116 + if (device) { 1.1117 + // the entry has been truncated to mStartOffset bytes, inform device 1.1118 + int32_t size = cacheEntry->DataSize(); 1.1119 + rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size); 1.1120 + if (NS_SUCCEEDED(rv)) 1.1121 + cacheEntry->SetDataSize(mStartOffset); 1.1122 + } else { 1.1123 + rv = NS_ERROR_NOT_AVAILABLE; 1.1124 + } 1.1125 + 1.1126 + // If anything above failed, clean up internal state and get out of here 1.1127 + // (see bug #654926)... 1.1128 + if (NS_FAILED(rv)) { 1.1129 + nsCacheService::ReleaseObject_Locked(stream.forget().take()); 1.1130 + mDescriptor->mOutputWrapper = nullptr; 1.1131 + nsCacheService::ReleaseObject_Locked(mDescriptor); 1.1132 + mDescriptor = nullptr; 1.1133 + mInitialized = false; 1.1134 + return rv; 1.1135 + } 1.1136 + 1.1137 + mOutput = stream; 1.1138 + mInitialized = true; 1.1139 + return NS_OK; 1.1140 +} 1.1141 + 1.1142 +nsresult nsCacheEntryDescriptor:: 1.1143 +nsOutputStreamWrapper::EnsureInit() 1.1144 +{ 1.1145 + if (mInitialized) { 1.1146 + NS_ASSERTION(mDescriptor, "Bad state"); 1.1147 + return NS_OK; 1.1148 + } 1.1149 + 1.1150 + return LazyInit(); 1.1151 +} 1.1152 + 1.1153 +nsresult nsCacheEntryDescriptor:: 1.1154 +nsOutputStreamWrapper::OnWrite(uint32_t count) 1.1155 +{ 1.1156 + if (count > INT32_MAX) return NS_ERROR_UNEXPECTED; 1.1157 + return mDescriptor->RequestDataSizeChange((int32_t)count); 1.1158 +} 1.1159 + 1.1160 +void nsCacheEntryDescriptor:: 1.1161 +nsOutputStreamWrapper::CloseInternal() 1.1162 +{ 1.1163 + mLock.AssertCurrentThreadOwns(); 1.1164 + if (!mDescriptor) { 1.1165 + NS_ASSERTION(!mInitialized, "Bad state"); 1.1166 + NS_ASSERTION(!mOutput, "Bad state"); 1.1167 + return; 1.1168 + } 1.1169 + 1.1170 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_CLOSEINTERNAL)); 1.1171 + 1.1172 + if (mDescriptor) { 1.1173 + mDescriptor->mOutputWrapper = nullptr; 1.1174 + nsCacheService::ReleaseObject_Locked(mDescriptor); 1.1175 + mDescriptor = nullptr; 1.1176 + } 1.1177 + mInitialized = false; 1.1178 + mOutput = nullptr; 1.1179 +} 1.1180 + 1.1181 + 1.1182 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1183 +nsOutputStreamWrapper::Close() 1.1184 +{ 1.1185 + mozilla::MutexAutoLock lock(mLock); 1.1186 + 1.1187 + return Close_Locked(); 1.1188 +} 1.1189 + 1.1190 +nsresult nsCacheEntryDescriptor:: 1.1191 +nsOutputStreamWrapper::Close_Locked() 1.1192 +{ 1.1193 + nsresult rv = EnsureInit(); 1.1194 + if (NS_SUCCEEDED(rv)) { 1.1195 + rv = mOutput->Close(); 1.1196 + } else { 1.1197 + NS_ASSERTION(!mOutput, 1.1198 + "Shouldn't have mOutput when EnsureInit() failed"); 1.1199 + } 1.1200 + 1.1201 + // Call CloseInternal() even when EnsureInit() failed, e.g. in case we are 1.1202 + // closing streams with nsCacheService::CloseAllStream() 1.1203 + CloseInternal(); 1.1204 + return rv; 1.1205 +} 1.1206 + 1.1207 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1208 +nsOutputStreamWrapper::Flush() 1.1209 +{ 1.1210 + mozilla::MutexAutoLock lock(mLock); 1.1211 + 1.1212 + nsresult rv = EnsureInit(); 1.1213 + if (NS_FAILED(rv)) return rv; 1.1214 + 1.1215 + return mOutput->Flush(); 1.1216 +} 1.1217 + 1.1218 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1219 +nsOutputStreamWrapper::Write(const char * buf, 1.1220 + uint32_t count, 1.1221 + uint32_t * result) 1.1222 +{ 1.1223 + mozilla::MutexAutoLock lock(mLock); 1.1224 + return Write_Locked(buf, count, result); 1.1225 +} 1.1226 + 1.1227 +nsresult nsCacheEntryDescriptor:: 1.1228 +nsOutputStreamWrapper::Write_Locked(const char * buf, 1.1229 + uint32_t count, 1.1230 + uint32_t * result) 1.1231 +{ 1.1232 + nsresult rv = EnsureInit(); 1.1233 + if (NS_FAILED(rv)) return rv; 1.1234 + 1.1235 + rv = OnWrite(count); 1.1236 + if (NS_FAILED(rv)) return rv; 1.1237 + 1.1238 + return mOutput->Write(buf, count, result); 1.1239 +} 1.1240 + 1.1241 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1242 +nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr, 1.1243 + uint32_t count, 1.1244 + uint32_t * result) 1.1245 +{ 1.1246 + return NS_ERROR_NOT_IMPLEMENTED; 1.1247 +} 1.1248 + 1.1249 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1250 +nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader, 1.1251 + void * closure, 1.1252 + uint32_t count, 1.1253 + uint32_t * result) 1.1254 +{ 1.1255 + return NS_ERROR_NOT_IMPLEMENTED; 1.1256 +} 1.1257 + 1.1258 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1259 +nsOutputStreamWrapper::IsNonBlocking(bool *result) 1.1260 +{ 1.1261 + // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK 1.1262 + *result = false; 1.1263 + return NS_OK; 1.1264 +} 1.1265 + 1.1266 + 1.1267 +/****************************************************************************** 1.1268 + * nsCompressOutputStreamWrapper - an output stream wrapper that compresses 1.1269 + * data before it is written 1.1270 + ******************************************************************************/ 1.1271 + 1.1272 +NS_IMPL_ADDREF(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper) 1.1273 +NS_IMETHODIMP_(MozExternalRefCountType) 1.1274 +nsCacheEntryDescriptor::nsCompressOutputStreamWrapper::Release() 1.1275 +{ 1.1276 + // Holding a reference to descriptor ensures that cache service won't go 1.1277 + // away. Do not grab cache service lock if there is no descriptor. 1.1278 + nsRefPtr<nsCacheEntryDescriptor> desc; 1.1279 + 1.1280 + { 1.1281 + mozilla::MutexAutoLock lock(mLock); 1.1282 + desc = mDescriptor; 1.1283 + } 1.1284 + 1.1285 + if (desc) 1.1286 + nsCacheService::Lock(LOCK_TELEM(NSCOMPRESSOUTPUTSTREAMWRAPPER_RELEASE)); 1.1287 + 1.1288 + nsrefcnt count; 1.1289 + NS_PRECONDITION(0 != mRefCnt, "dup release"); 1.1290 + count = --mRefCnt; 1.1291 + NS_LOG_RELEASE(this, count, 1.1292 + "nsCacheEntryDescriptor::nsCompressOutputStreamWrapper"); 1.1293 + 1.1294 + if (0 == count) { 1.1295 + // don't use desc here since mDescriptor might be already nulled out 1.1296 + if (mDescriptor) 1.1297 + mDescriptor->mOutputWrapper = nullptr; 1.1298 + 1.1299 + if (desc) 1.1300 + nsCacheService::Unlock(); 1.1301 + 1.1302 + mRefCnt = 1; 1.1303 + delete (this); 1.1304 + return 0; 1.1305 + } 1.1306 + 1.1307 + if (desc) 1.1308 + nsCacheService::Unlock(); 1.1309 + 1.1310 + return count; 1.1311 +} 1.1312 + 1.1313 +NS_INTERFACE_MAP_BEGIN(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper) 1.1314 + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) 1.1315 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.1316 +NS_INTERFACE_MAP_END_THREADSAFE 1.1317 + 1.1318 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1319 +nsCompressOutputStreamWrapper::Write(const char * buf, 1.1320 + uint32_t count, 1.1321 + uint32_t * result) 1.1322 +{ 1.1323 + mozilla::MutexAutoLock lock(mLock); 1.1324 + 1.1325 + int zerr = Z_OK; 1.1326 + nsresult rv = NS_OK; 1.1327 + 1.1328 + if (!mStreamInitialized) { 1.1329 + rv = InitZstream(); 1.1330 + if (NS_FAILED(rv)) { 1.1331 + return rv; 1.1332 + } 1.1333 + } 1.1334 + 1.1335 + if (!mWriteBuffer) { 1.1336 + // Once allocated, this buffer is referenced by the zlib stream and 1.1337 + // cannot be grown. We use 2x(initial write request) to approximate 1.1338 + // a stream buffer size proportional to request buffers. 1.1339 + mWriteBufferLen = std::max(count*2, (uint32_t)kMinCompressWriteBufLen); 1.1340 + mWriteBuffer = (unsigned char*)nsMemory::Alloc(mWriteBufferLen); 1.1341 + if (!mWriteBuffer) { 1.1342 + mWriteBufferLen = 0; 1.1343 + return NS_ERROR_OUT_OF_MEMORY; 1.1344 + } 1.1345 + mZstream.next_out = mWriteBuffer; 1.1346 + mZstream.avail_out = mWriteBufferLen; 1.1347 + } 1.1348 + 1.1349 + // Compress (deflate) the requested buffer. Keep going 1.1350 + // until the entire buffer has been deflated. 1.1351 + mZstream.avail_in = count; 1.1352 + mZstream.next_in = (Bytef*)buf; 1.1353 + while (mZstream.avail_in > 0) { 1.1354 + zerr = deflate(&mZstream, Z_NO_FLUSH); 1.1355 + if (zerr == Z_STREAM_ERROR) { 1.1356 + deflateEnd(&mZstream); 1.1357 + mStreamEnded = true; 1.1358 + mStreamInitialized = false; 1.1359 + return NS_ERROR_FAILURE; 1.1360 + } 1.1361 + // Note: Z_BUF_ERROR is non-fatal and sometimes expected here. 1.1362 + 1.1363 + // If the compression stream output buffer is filled, write 1.1364 + // it out to the underlying stream wrapper. 1.1365 + if (mZstream.avail_out == 0) { 1.1366 + rv = WriteBuffer(); 1.1367 + if (NS_FAILED(rv)) { 1.1368 + deflateEnd(&mZstream); 1.1369 + mStreamEnded = true; 1.1370 + mStreamInitialized = false; 1.1371 + return rv; 1.1372 + } 1.1373 + } 1.1374 + } 1.1375 + *result = count; 1.1376 + mUncompressedCount += *result; 1.1377 + return NS_OK; 1.1378 +} 1.1379 + 1.1380 +NS_IMETHODIMP nsCacheEntryDescriptor:: 1.1381 +nsCompressOutputStreamWrapper::Close() 1.1382 +{ 1.1383 + mozilla::MutexAutoLock lock(mLock); 1.1384 + 1.1385 + if (!mDescriptor) 1.1386 + return NS_ERROR_NOT_AVAILABLE; 1.1387 + 1.1388 + nsresult retval = NS_OK; 1.1389 + nsresult rv; 1.1390 + int zerr = 0; 1.1391 + 1.1392 + if (mStreamInitialized) { 1.1393 + // complete compression of any data remaining in the zlib stream 1.1394 + do { 1.1395 + zerr = deflate(&mZstream, Z_FINISH); 1.1396 + rv = WriteBuffer(); 1.1397 + if (NS_FAILED(rv)) 1.1398 + retval = rv; 1.1399 + } while (zerr == Z_OK && rv == NS_OK); 1.1400 + deflateEnd(&mZstream); 1.1401 + mStreamInitialized = false; 1.1402 + } 1.1403 + // Do not allow to initialize stream after calling Close(). 1.1404 + mStreamEnded = true; 1.1405 + 1.1406 + if (mDescriptor->CacheEntry()) { 1.1407 + nsAutoCString uncompressedLenStr; 1.1408 + rv = mDescriptor->GetMetaDataElement("uncompressed-len", 1.1409 + getter_Copies(uncompressedLenStr)); 1.1410 + if (NS_SUCCEEDED(rv)) { 1.1411 + int32_t oldCount = uncompressedLenStr.ToInteger(&rv); 1.1412 + if (NS_SUCCEEDED(rv)) { 1.1413 + mUncompressedCount += oldCount; 1.1414 + } 1.1415 + } 1.1416 + uncompressedLenStr.Adopt(0); 1.1417 + uncompressedLenStr.AppendInt(mUncompressedCount); 1.1418 + rv = mDescriptor->SetMetaDataElement("uncompressed-len", 1.1419 + uncompressedLenStr.get()); 1.1420 + if (NS_FAILED(rv)) 1.1421 + retval = rv; 1.1422 + } 1.1423 + 1.1424 + if (mWriteBuffer) { 1.1425 + nsMemory::Free(mWriteBuffer); 1.1426 + mWriteBuffer = 0; 1.1427 + mWriteBufferLen = 0; 1.1428 + } 1.1429 + 1.1430 + rv = nsOutputStreamWrapper::Close_Locked(); 1.1431 + if (NS_FAILED(rv)) 1.1432 + retval = rv; 1.1433 + 1.1434 + return retval; 1.1435 +} 1.1436 + 1.1437 +nsresult nsCacheEntryDescriptor:: 1.1438 +nsCompressOutputStreamWrapper::InitZstream() 1.1439 +{ 1.1440 + if (!mDescriptor) 1.1441 + return NS_ERROR_NOT_AVAILABLE; 1.1442 + 1.1443 + if (mStreamEnded) 1.1444 + return NS_ERROR_FAILURE; 1.1445 + 1.1446 + // Determine compression level: Aggressive compression 1.1447 + // may impact performance on mobile devices, while a 1.1448 + // lower compression level still provides substantial 1.1449 + // space savings for many text streams. 1.1450 + int32_t compressionLevel = nsCacheService::CacheCompressionLevel(); 1.1451 + 1.1452 + // Initialize zlib deflate stream 1.1453 + mZstream.zalloc = Z_NULL; 1.1454 + mZstream.zfree = Z_NULL; 1.1455 + mZstream.opaque = Z_NULL; 1.1456 + if (deflateInit2(&mZstream, compressionLevel, Z_DEFLATED, 1.1457 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) { 1.1458 + return NS_ERROR_FAILURE; 1.1459 + } 1.1460 + mZstream.next_in = Z_NULL; 1.1461 + mZstream.avail_in = 0; 1.1462 + 1.1463 + mStreamInitialized = true; 1.1464 + 1.1465 + return NS_OK; 1.1466 +} 1.1467 + 1.1468 +nsresult nsCacheEntryDescriptor:: 1.1469 +nsCompressOutputStreamWrapper::WriteBuffer() 1.1470 +{ 1.1471 + uint32_t bytesToWrite = mWriteBufferLen - mZstream.avail_out; 1.1472 + uint32_t result = 0; 1.1473 + nsresult rv = nsCacheEntryDescriptor::nsOutputStreamWrapper::Write_Locked( 1.1474 + (const char *)mWriteBuffer, bytesToWrite, &result); 1.1475 + mZstream.next_out = mWriteBuffer; 1.1476 + mZstream.avail_out = mWriteBufferLen; 1.1477 + return rv; 1.1478 +} 1.1479 +