Bug 1167809 - Add skip size check flag to cache for use with ServiceWorkers. r?mayhemer
For non-e10s Service Worker, we use Cache entries to achieve interception.
While this is a temporary measure, the fact that cache enforces size limits on
cache entries (which make sense for the purpose it was designed) prevents large
files from being served via a Service Worker. This patch adds a skip size check
flag to CacheStorage that is relayed to CacheEntry and CacheFile. It is set to
false by default leading to normal cache behaviour.
The patch also adds nsICacheStorageService.synthesizedCacheStorage() that
retrieves a cache storage with this flag set to true, which is used by
nsHttpChannel in case of possible interception.
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -158,25 +158,27 @@ nsresult CacheEntry::Callback::OnAvailTh
NS_IMPL_ISUPPORTS(CacheEntry,
nsICacheEntry,
nsIRunnable,
CacheFileListener)
CacheEntry::CacheEntry(const nsACString& aStorageID,
nsIURI* aURI,
const nsACString& aEnhanceID,
- bool aUseDisk)
+ bool aUseDisk,
+ bool aSkipSizeCheck)
: mFrecency(0)
, mSortingExpirationTime(uint32_t(-1))
, mLock("CacheEntry")
, mFileStatus(NS_ERROR_NOT_INITIALIZED)
, mURI(aURI)
, mEnhanceID(aEnhanceID)
, mStorageID(aStorageID)
, mUseDisk(aUseDisk)
+, mSkipSizeCheck(aSkipSizeCheck)
, mIsDoomed(false)
, mSecurityInfoLoaded(false)
, mPreventCallbacks(false)
, mHasData(false)
, mState(NOTLOADED)
, mRegistration(NEVERREGISTERED)
, mWriter(nullptr)
, mPredictedDataSize(0)
@@ -386,16 +388,17 @@ bool CacheEntry::Load(bool aTruncate, bo
CacheFileUtils::DetailedCacheHitTelemetry::MISS, mLoadStart);
}
LOG((" performing load, file=%p", mFile.get()));
if (NS_SUCCEEDED(rv)) {
rv = mFile->Init(fileKey,
aTruncate,
!mUseDisk,
+ mSkipSizeCheck,
aPriority,
directLoad ? nullptr : this);
}
if (NS_FAILED(rv)) {
mFileStatus = rv;
AsyncDoom(nullptr);
return false;
@@ -481,16 +484,17 @@ already_AddRefed<CacheEntryHandle> Cache
nsRefPtr<CacheEntry> newEntry;
{
mozilla::MutexAutoUnlock unlock(mLock);
// The following call dooms this entry (calls DoomAlreadyRemoved on us)
nsresult rv = CacheStorageService::Self()->AddStorageEntry(
GetStorageID(), GetURI(), GetEnhanceID(),
mUseDisk && !aMemoryOnly,
+ mSkipSizeCheck,
true, // always create
true, // truncate existing (this one)
getter_AddRefs(handle));
if (NS_SUCCEEDED(rv)) {
newEntry = handle->Entry();
LOG((" exchanged entry %p by entry %p, rv=0x%08x", this, newEntry.get(), rv));
newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE);
@@ -1137,17 +1141,17 @@ NS_IMETHODIMP CacheEntry::GetPredictedDa
{
*aPredictedDataSize = mPredictedDataSize;
return NS_OK;
}
NS_IMETHODIMP CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize)
{
mPredictedDataSize = aPredictedDataSize;
- if (CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) {
+ if (!mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) {
LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
AsyncDoom(nullptr);
return NS_ERROR_FILE_TOO_BIG;
}
return NS_OK;
}
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -50,17 +50,17 @@ class CacheEntry final : public nsICache
, public CacheFileListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICACHEENTRY
NS_DECL_NSIRUNNABLE
CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID,
- bool aUseDisk);
+ bool aUseDisk, bool aSkipSizeCheck = false);
void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags);
CacheEntryHandle* NewHandle();
public:
uint32_t GetMetadataMemoryConsumption();
nsCString const &GetStorageID() const { return mStorageID; }
@@ -271,16 +271,19 @@ private:
::mozilla::Atomic<nsresult, ::mozilla::ReleaseAcquire> mFileStatus;
nsCOMPtr<nsIURI> mURI;
nsCString mEnhanceID;
nsCString mStorageID;
// Whether it's allowed to persist the data to disk
bool const mUseDisk;
+ // Whether it should skip max size check.
+ bool const mSkipSizeCheck;
+
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
// Left as a standalone flag to not bother with locking (there is no need).
bool mIsDoomed;
// Following flags are all synchronized with the cache entry lock.
// Whether security info has already been looked up in metadata.
bool mSecurityInfoLoaded : 1;
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -178,16 +178,17 @@ NS_INTERFACE_MAP_BEGIN(CacheFile)
mozilla::net::CacheFileChunkListener)
NS_INTERFACE_MAP_END_THREADSAFE
CacheFile::CacheFile()
: mLock("CacheFile.mLock")
, mOpeningFile(false)
, mReady(false)
, mMemoryOnly(false)
+ , mSkipSizeCheck(false)
, mOpenAsMemoryOnly(false)
, mPriority(false)
, mDataAccessed(false)
, mDataIsDirty(false)
, mWritingMetadata(false)
, mPreloadWithoutInputStreams(true)
, mPreloadChunkCount(0)
, mStatus(NS_OK)
@@ -207,26 +208,28 @@ CacheFile::~CacheFile()
WriteMetadataIfNeededLocked(true);
}
}
nsresult
CacheFile::Init(const nsACString &aKey,
bool aCreateNew,
bool aMemoryOnly,
+ bool aSkipSizeCheck,
bool aPriority,
CacheFileListener *aCallback)
{
MOZ_ASSERT(!mListener);
MOZ_ASSERT(!mHandle);
nsresult rv;
mKey = aKey;
mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly;
+ mSkipSizeCheck = aSkipSizeCheck;
mPriority = aPriority;
// Some consumers (at least nsHTTPCompressConv) assume that Read() can read
// such amount of data that was announced by Available().
// CacheFileInputStream::Available() uses also preloaded chunks to compute
// number of available bytes in the input stream, so we have to make sure the
// preloadChunkCount won't change during CacheFile's lifetime since otherwise
// we could potentially release some cached chunks that was used to calculate
--- a/netwerk/cache2/CacheFile.h
+++ b/netwerk/cache2/CacheFile.h
@@ -51,16 +51,17 @@ class CacheFile final : public CacheFile
public:
NS_DECL_THREADSAFE_ISUPPORTS
CacheFile();
nsresult Init(const nsACString &aKey,
bool aCreateNew,
bool aMemoryOnly,
+ bool aSkipSizeCheck,
bool aPriority,
CacheFileListener *aCallback);
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override;
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override;
NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
CacheFileChunk *aChunk) override;
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk) override;
@@ -188,16 +189,17 @@ private:
void SetError(nsresult aStatus);
nsresult InitIndexEntry();
mozilla::Mutex mLock;
bool mOpeningFile;
bool mReady;
bool mMemoryOnly;
+ bool mSkipSizeCheck;
bool mOpenAsMemoryOnly;
bool mPriority;
bool mDataAccessed;
bool mDataIsDirty;
bool mWritingMetadata;
bool mPreloadWithoutInputStreams;
uint32_t mPreloadChunkCount;
nsresult mStatus;
--- a/netwerk/cache2/CacheFileOutputStream.cpp
+++ b/netwerk/cache2/CacheFileOutputStream.cpp
@@ -89,17 +89,17 @@ CacheFileOutputStream::Write(const char
if (mClosed) {
LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
"status=0x%08x]", this, mStatus));
return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
}
- if (CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) {
+ if (!mFile->mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) {
LOG(("CacheFileOutputStream::Write() - Entry is too big, failing and "
"dooming the entry. [this=%p]", this));
mFile->DoomLocked(nullptr);
CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
return NS_ERROR_FILE_TOO_BIG;
}
--- a/netwerk/cache2/CacheStorage.cpp
+++ b/netwerk/cache2/CacheStorage.cpp
@@ -20,20 +20,22 @@
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(CacheStorage, nsICacheStorage)
CacheStorage::CacheStorage(nsILoadContextInfo* aInfo,
bool aAllowDisk,
- bool aLookupAppCache)
+ bool aLookupAppCache,
+ bool aSkipSizeCheck)
: mLoadContextInfo(GetLoadContextInfo(aInfo))
, mWriteToDisk(aAllowDisk)
, mLookupAppCache(aLookupAppCache)
+, mSkipSizeCheck(aSkipSizeCheck)
{
}
CacheStorage::~CacheStorage()
{
}
NS_IMETHODIMP CacheStorage::AsyncOpenURI(nsIURI *aURI,
--- a/netwerk/cache2/CacheStorage.h
+++ b/netwerk/cache2/CacheStorage.h
@@ -47,29 +47,32 @@ private:
class CacheStorage : public nsICacheStorage
{
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICACHESTORAGE
public:
CacheStorage(nsILoadContextInfo* aInfo,
bool aAllowDisk,
- bool aLookupAppCache);
+ bool aLookupAppCache,
+ bool aSkipSizeCheck = false);
protected:
virtual ~CacheStorage();
nsresult ChooseApplicationCache(nsIURI* aURI, nsIApplicationCache** aCache);
nsRefPtr<LoadContextInfo> mLoadContextInfo;
bool mWriteToDisk : 1;
bool mLookupAppCache : 1;
+ bool mSkipSizeCheck: 1;
public:
nsILoadContextInfo* LoadInfo() const { return mLoadContextInfo; }
bool WriteToDisk() const { return mWriteToDisk && !mLoadContextInfo->IsPrivate(); }
bool LookupAppCache() const { return mLookupAppCache; }
+ bool SkipSizeCheck() const { return mSkipSizeCheck; }
};
} // namespace net
} // namespace mozilla
#endif
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -732,16 +732,34 @@ NS_IMETHODIMP CacheStorageService::AppCa
else {
storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache);
}
storage.forget(_retval);
return NS_OK;
}
+NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo,
+ nsICacheStorage * *_retval)
+{
+ NS_ENSURE_ARG(aLoadContextInfo);
+ NS_ENSURE_ARG(_retval);
+
+ nsCOMPtr<nsICacheStorage> storage;
+ if (CacheObserver::UseNewCache()) {
+ storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */);
+ }
+ else {
+ storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr);
+ }
+
+ storage.forget(_retval);
+ return NS_OK;
+}
+
NS_IMETHODIMP CacheStorageService::Clear()
{
nsresult rv;
if (CacheObserver::UseNewCache()) {
{
mozilla::MutexAutoLock lock(mLock);
@@ -1334,25 +1352,27 @@ CacheStorageService::AddStorageEntry(Cac
NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_ARG(aStorage);
nsAutoCString contextKey;
CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
return AddStorageEntry(contextKey, aURI, aIdExtension,
- aStorage->WriteToDisk(), aCreateIfNotExist, aReplace,
+ aStorage->WriteToDisk(), aStorage->SkipSizeCheck(),
+ aCreateIfNotExist, aReplace,
aResult);
}
nsresult
CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
nsIURI* aURI,
const nsACString & aIdExtension,
bool aWriteToDisk,
+ bool aSkipSizeCheck,
bool aCreateIfNotExist,
bool aReplace,
CacheEntryHandle** aResult)
{
NS_ENSURE_ARG(aURI);
nsresult rv;
@@ -1405,17 +1425,17 @@ CacheStorageService::AddStorageEntry(nsC
entry = nullptr;
entryExists = false;
}
// Ensure entry for the particular URL, if not read/only
if (!entryExists && (aCreateIfNotExist || aReplace)) {
// Entry is not in the hashtable or has just been truncated...
- entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk);
+ entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck);
entries->Put(entryKey, entry);
LOG((" new entry %p for %s", entry.get(), entryKey.get()));
}
if (entry) {
// Here, if this entry was not for a long time referenced by any consumer,
// gets again first 'handles count' reference.
handle = entry->NewHandle();
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -273,16 +273,17 @@ private:
nsresult DoomStorageEntries(nsCSubstring const& aContextKey,
nsILoadContextInfo* aContext,
bool aDiskStorage,
nsICacheEntryDoomCallback* aCallback);
nsresult AddStorageEntry(nsCSubstring const& aContextKey,
nsIURI* aURI,
const nsACString & aIdExtension,
bool aWriteToDisk,
+ bool aSkipSizeCheck,
bool aCreateIfNotExist,
bool aReplace,
CacheEntryHandle** aResult);
static CacheStorageService* sSelf;
mozilla::Mutex mLock;
mozilla::Mutex mForcedValidEntriesLock;
--- a/netwerk/cache2/nsICacheStorageService.idl
+++ b/netwerk/cache2/nsICacheStorageService.idl
@@ -51,16 +51,23 @@ interface nsICacheStorageService : nsISu
* @param aApplicationCache
* Optional reference to an existing appcache. When left null, this will
* work with offline cache as a whole.
*/
nsICacheStorage appCacheStorage(in nsILoadContextInfo aLoadContextInfo,
in nsIApplicationCache aApplicationCache);
/**
+ * Get storage for synthesized cache entries that we currently use for ServiceWorker interception in non-e10s mode.
+ *
+ * This cache storage has no limits on file size to allow the ServiceWorker to intercept large files.
+ */
+ nsICacheStorage synthesizedCacheStorage(in nsILoadContextInfo aLoadContextInfo);
+
+ /**
* Evict the whole cache.
*/
void clear();
/**
* Purge only data of disk backed entries. Metadata are left for
* performance purposes.
*/
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2931,18 +2931,21 @@ nsHttpChannel::OpenCacheEntry(bool isHtt
cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY
| nsICacheStorage::CHECK_MULTITHREADED;
}
if (!mPostID && mApplicationCache) {
rv = cacheStorageService->AppCacheStorage(info,
mApplicationCache,
getter_AddRefs(cacheStorage));
- }
- else if (PossiblyIntercepted() || mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
+ } else if (PossiblyIntercepted()) {
+ // The synthesized cache has less restrictions on file size and so on.
+ rv = cacheStorageService->SynthesizedCacheStorage(info,
+ getter_AddRefs(cacheStorage));
+ } else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well...
getter_AddRefs(cacheStorage));
}
else {
rv = cacheStorageService->DiskCacheStorage(info,
!mPostID && (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)),
getter_AddRefs(cacheStorage));
}