Bug 1167809 - Add skip size check flag to cache for use with ServiceWorkers. r?mayhemer draft
authorNikhil Marathe <nsm.nikhil@gmail.com>
Thu, 03 Sep 2015 16:05:42 -0700
changeset 290210 3c85a5022eaf8aa6cc4e3ec8f48f65bd8a514def
parent 288895 7db14bebae9196d780b1d64d2fd32d1bda26828b
child 509009 c0064e1fe9f8d7a6ebc5a741a90c67ffb2ef76d6
push id5106
push usernsm.nikhil@gmail.com
push dateThu, 03 Sep 2015 23:07:11 +0000
reviewersmayhemer
bugs1167809
milestone43.0a1
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.
netwerk/cache2/CacheEntry.cpp
netwerk/cache2/CacheEntry.h
netwerk/cache2/CacheFile.cpp
netwerk/cache2/CacheFile.h
netwerk/cache2/CacheFileOutputStream.cpp
netwerk/cache2/CacheStorage.cpp
netwerk/cache2/CacheStorage.h
netwerk/cache2/CacheStorageService.cpp
netwerk/cache2/CacheStorageService.h
netwerk/cache2/nsICacheStorageService.idl
netwerk/protocol/http/nsHttpChannel.cpp
--- 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));
     }