Bug 1093357 P3 Convert Cache to use IPCStream and AutoIPCStream. r=asuth
authorBen Kelly <ben@wanderview.com>
Sun, 15 May 2016 10:32:09 -0700
changeset 297486 47f7004b976a65ae6563da983452dfe0381a562f
parent 297485 f92c3038f215388a5ef8803ca2ab26414d78dcf5
child 297487 898d5f48ff51c7ee571cbda456661f42ce48cc8b
push id30260
push userphilringnalda@gmail.com
push dateMon, 16 May 2016 03:38:04 +0000
treeherdermozilla-central@d0be57e84807 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1093357
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1093357 P3 Convert Cache to use IPCStream and AutoIPCStream. r=asuth * * * Bug 1093357 P3 interdiff 001 Use AutoIPCStream in dom/cache
dom/cache/AutoUtils.cpp
dom/cache/AutoUtils.h
dom/cache/Cache.cpp
dom/cache/Cache.h
dom/cache/CacheChild.cpp
dom/cache/CacheChild.h
dom/cache/CacheOpChild.cpp
dom/cache/CacheOpChild.h
dom/cache/CacheOpParent.cpp
dom/cache/CacheParent.cpp
dom/cache/CacheParent.h
dom/cache/CachePushStreamChild.cpp
dom/cache/CachePushStreamChild.h
dom/cache/CachePushStreamParent.cpp
dom/cache/CachePushStreamParent.h
dom/cache/CacheStorage.cpp
dom/cache/CacheStorage.h
dom/cache/CacheStreamControlChild.cpp
dom/cache/CacheStreamControlChild.h
dom/cache/CacheStreamControlParent.cpp
dom/cache/CacheStreamControlParent.h
dom/cache/CacheTypes.ipdlh
dom/cache/DBSchema.cpp
dom/cache/ManagerId.cpp
dom/cache/PCache.ipdl
dom/cache/PCacheOp.ipdl
dom/cache/PCachePushStream.ipdl
dom/cache/PCacheStorage.ipdl
dom/cache/ReadStream.cpp
dom/cache/ReadStream.h
dom/cache/StreamControl.h
dom/cache/TypeUtils.cpp
dom/cache/TypeUtils.h
dom/cache/moz.build
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -5,156 +5,86 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/AutoUtils.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/InternalRequest.h"
 #include "mozilla/dom/cache/CacheParent.h"
-#include "mozilla/dom/cache/CachePushStreamChild.h"
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/dom/cache/TypeUtils.h"
-#include "mozilla/ipc/FileDescriptorSetChild.h"
-#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "nsCRT.h"
 #include "nsHttp.h"
 
-namespace {
-
 using mozilla::Unused;
-using mozilla::dom::cache::CachePushStreamChild;
 using mozilla::dom::cache::CacheReadStream;
 using mozilla::dom::cache::CacheReadStreamOrVoid;
-using mozilla::ipc::FileDescriptor;
-using mozilla::ipc::FileDescriptorSetChild;
-using mozilla::ipc::FileDescriptorSetParent;
-using mozilla::ipc::OptionalFileDescriptorSet;
+using mozilla::ipc::AutoIPCStream;
+using mozilla::ipc::PBackgroundParent;
+
+namespace {
 
 enum CleanupAction
 {
   Forget,
   Delete
 };
 
 void
-CleanupChildFds(CacheReadStream& aReadStream, CleanupAction aAction)
-{
-  if (aReadStream.fds().type() !=
-      OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-    return;
-  }
-
-  AutoTArray<FileDescriptor, 4> fds;
-
-  FileDescriptorSetChild* fdSetActor =
-    static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
-  MOZ_ASSERT(fdSetActor);
-
-  if (aAction == Delete) {
-    Unused << fdSetActor->Send__delete__(fdSetActor);
-  }
-
-  // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
-  // unconditionally forget them here.  The fds themselves are auto-closed in
-  // ~FileDescriptor since they originated in this process.
-  fdSetActor->ForgetFileDescriptors(fds);
-}
-
-void
-CleanupChildPushStream(CacheReadStream& aReadStream, CleanupAction aAction)
-{
-  if (!aReadStream.pushStreamChild()) {
-    return;
-  }
-
-  auto pushStream =
-    static_cast<CachePushStreamChild*>(aReadStream.pushStreamChild());
-
-  if (aAction == Delete) {
-    pushStream->StartDestroy();
-    return;
-  }
-
-  // If we send the stream, then we need to start it before forgetting about it.
-  pushStream->Start();
-}
-
-void
 CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction)
 {
-  CleanupChildFds(aReadStream, aAction);
-  CleanupChildPushStream(aReadStream, aAction);
+  // fds cleaned up by mStreamCleanupList
+  // PSendStream actors cleaned up by mStreamCleanupList
 }
 
 void
 CleanupChild(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
 {
   if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
 
   CleanupChild(aReadStreamOrVoid.get_CacheReadStream(), aAction);
 }
 
-void
-CleanupParentFds(CacheReadStream& aReadStream, CleanupAction aAction)
-{
-  if (aReadStream.fds().type() !=
-      OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
-    return;
-  }
-
-  AutoTArray<FileDescriptor, 4> fds;
-
-  FileDescriptorSetParent* fdSetActor =
-    static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
-  MOZ_ASSERT(fdSetActor);
-
-  if (aAction == Delete) {
-    Unused << fdSetActor->Send__delete__(fdSetActor);
-  }
-
-  // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
-  // unconditionally forget them here.  The fds themselves are auto-closed in
-  // ~FileDescriptor since they originated in this process.
-  fdSetActor->ForgetFileDescriptors(fds);
-}
-
-void
-CleanupParentFds(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
-{
-  if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
-    return;
-  }
-
-  CleanupParentFds(aReadStreamOrVoid.get_CacheReadStream(), aAction);
-}
-
 } // namespace
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-using mozilla::ipc::PBackgroundParent;
-
 // --------------------------------------------
 
 AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
-                                 const CacheOpArgs& aOpArgs)
+                                 const CacheOpArgs& aOpArgs,
+                                 uint32_t aEntryCount)
   : mTypeUtils(aTypeUtils)
   , mOpArgs(aOpArgs)
   , mSent(false)
 {
   MOZ_ASSERT(mTypeUtils);
+  MOZ_RELEASE_ASSERT(aEntryCount != 0);
+  // We are using AutoIPCStream objects to cleanup target IPCStream
+  // structures embedded in our CacheOpArgs.  These IPCStream structs
+  // must not move once we attach our AutoIPCStream to them.  Therefore,
+  // its important that any arrays containing streams are pre-sized for
+  // the number of entries we have in order to avoid realloc moving
+  // things around on us.
+  if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
+    CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
+    args.requestResponseList().SetCapacity(aEntryCount);
+  } else {
+    MOZ_ASSERT(aEntryCount == 1);
+  }
 }
 
 AutoChildOpArgs::~AutoChildOpArgs()
 {
   CleanupAction action = mSent ? Forget : Delete;
 
   switch(mOpArgs.type()) {
     case CacheOpArgs::TCacheMatchArgs:
@@ -202,62 +132,66 @@ AutoChildOpArgs::~AutoChildOpArgs()
       StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
       CleanupChild(args.request().body(), action);
       break;
     }
     default:
       // Other types do not need cleanup
       break;
   }
+
+  mStreamCleanupList.Clear();
 }
 
 void
 AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
                      SchemeAction aSchemeAction, ErrorResult& aRv)
 {
   MOZ_ASSERT(!mSent);
 
   switch(mOpArgs.type()) {
     case CacheOpArgs::TCacheMatchArgs:
     {
       CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
       mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
-                                 aSchemeAction, aRv);
+                                 aSchemeAction, mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheMatchAllArgs:
     {
       CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
       MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
       args.requestOrVoid() = CacheRequest();
       mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
-                                  aRequest, aBodyAction, aSchemeAction, aRv);
+                                 aRequest, aBodyAction, aSchemeAction,
+                                 mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheDeleteArgs:
     {
       CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
       mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
-                                 aSchemeAction, aRv);
+                                 aSchemeAction, mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheKeysArgs:
     {
       CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
       MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
       args.requestOrVoid() = CacheRequest();
       mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
-                                  aRequest, aBodyAction, aSchemeAction, aRv);
+                                  aRequest, aBodyAction, aSchemeAction,
+                                  mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TStorageMatchArgs:
     {
       StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
       mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
-                                 aSchemeAction, aRv);
+                                 aSchemeAction, mStreamCleanupList, aRv);
       break;
     }
     default:
       MOZ_CRASH("Cache args type cannot send a Request!");
   }
 }
 
 namespace {
@@ -380,31 +314,39 @@ AutoChildOpArgs::Add(InternalRequest* aR
       // Throw an error if a request/response pair would mask another
       // request/response pair in the same PutAll operation.  This is
       // step 2.3.2.3 from the "Batch Cache Operations" spec algorithm.
       if (MatchInPutList(aRequest, args.requestResponseList())) {
         aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
         return;
       }
 
+      // Ensure that we don't realloc the array since this can result
+      // in our AutoIPCStream objects to reference the wrong memory
+      // location.  This should never happen and is a UAF if it does.
+      // Therefore make this a release assertion.
+      MOZ_RELEASE_ASSERT(args.requestResponseList().Length() <
+                         args.requestResponseList().Capacity());
+
       // The FileDescriptorSetChild asserts in its destructor that all fds have
       // been removed.  The copy constructor, however, simply duplicates the
       // fds without removing any.  This means each temporary and copy must be
       // explicitly cleaned up.
       //
       // Avoid a lot of this hassle by making sure we only create one here.  On
       // error we remove it.
       CacheRequestResponse& pair = *args.requestResponseList().AppendElement();
       pair.request().body() = void_t();
       pair.response().body() = void_t();
 
       mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
-                                 aSchemeAction, aRv);
+                                 aSchemeAction, mStreamCleanupList, aRv);
       if (!aRv.Failed()) {
-        mTypeUtils->ToCacheResponse(pair.response(), aResponse, aRv);
+        mTypeUtils->ToCacheResponse(pair.response(), aResponse,
+                                    mStreamCleanupList, aRv);
       }
 
       if (aRv.Failed()) {
         CleanupChild(pair.request().body(), Delete);
         args.requestResponseList().RemoveElementAt(
           args.requestResponseList().Length() - 1);
       }
 
@@ -415,89 +357,75 @@ AutoChildOpArgs::Add(InternalRequest* aR
   }
 }
 
 const CacheOpArgs&
 AutoChildOpArgs::SendAsOpArgs()
 {
   MOZ_ASSERT(!mSent);
   mSent = true;
+  for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
+    autoStream->TakeValue();
+  }
   return mOpArgs;
 }
 
 // --------------------------------------------
 
 AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
-                                       const CacheOpResult& aOpResult)
+                                       const CacheOpResult& aOpResult,
+                                       uint32_t aEntryCount)
   : mManager(aManager)
   , mOpResult(aOpResult)
   , mStreamControl(nullptr)
   , mSent(false)
 {
   MOZ_ASSERT(mManager);
+  MOZ_RELEASE_ASSERT(aEntryCount != 0);
+  // We are using AutoIPCStream objects to cleanup target IPCStream
+  // structures embedded in our CacheOpArgs.  These IPCStream structs
+  // must not move once we attach our AutoIPCStream to them.  Therefore,
+  // its important that any arrays containing streams are pre-sized for
+  // the number of entries we have in order to avoid realloc moving
+  // things around on us.
+  if (mOpResult.type() == CacheOpResult::TCacheMatchAllResult) {
+    CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
+    result.responseList().SetCapacity(aEntryCount);
+  } else if (mOpResult.type() == CacheOpResult::TCacheKeysResult) {
+    CacheKeysResult& result = mOpResult.get_CacheKeysResult();
+    result.requestList().SetCapacity(aEntryCount);
+  } else {
+    MOZ_ASSERT(aEntryCount == 1);
+  }
 }
 
 AutoParentOpResult::~AutoParentOpResult()
 {
   CleanupAction action = mSent ? Forget : Delete;
 
   switch (mOpResult.type()) {
-    case CacheOpResult::TCacheMatchResult:
-    {
-      CacheMatchResult& result = mOpResult.get_CacheMatchResult();
-      if (result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t) {
-        break;
-      }
-      CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
-                       action);
-      break;
-    }
-    case CacheOpResult::TCacheMatchAllResult:
-    {
-      CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
-      for (uint32_t i = 0; i < result.responseList().Length(); ++i) {
-        CleanupParentFds(result.responseList()[i].body(), action);
-      }
-      break;
-    }
-    case CacheOpResult::TCacheKeysResult:
-    {
-      CacheKeysResult& result = mOpResult.get_CacheKeysResult();
-      for (uint32_t i = 0; i < result.requestList().Length(); ++i) {
-        CleanupParentFds(result.requestList()[i].body(), action);
-      }
-      break;
-    }
-    case CacheOpResult::TStorageMatchResult:
-    {
-      StorageMatchResult& result = mOpResult.get_StorageMatchResult();
-      if (result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t) {
-        break;
-      }
-      CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
-                       action);
-      break;
-    }
     case CacheOpResult::TStorageOpenResult:
     {
       StorageOpenResult& result = mOpResult.get_StorageOpenResult();
       if (action == Forget || result.actorParent() == nullptr) {
         break;
       }
       Unused << PCacheParent::Send__delete__(result.actorParent());
       break;
     }
     default:
-      // other types do not need clean up
+      // other types do not need additional clean up
       break;
   }
 
   if (action == Delete && mStreamControl) {
     Unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
   }
+
+  mStreamCleanupList.Clear();
 }
 
 void
 AutoParentOpResult::Add(CacheId aOpenedCacheId, Manager* aManager)
 {
   MOZ_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
   MOZ_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
   mOpResult.get_StorageOpenResult().actorParent() =
@@ -518,16 +446,22 @@ AutoParentOpResult::Add(const SavedRespo
       result.responseOrVoid() = aSavedResponse.mValue;
       SerializeResponseBody(aSavedResponse, aStreamList,
                             &result.responseOrVoid().get_CacheResponse());
       break;
     }
     case CacheOpResult::TCacheMatchAllResult:
     {
       CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
+      // Ensure that we don't realloc the array since this can result
+      // in our AutoIPCStream objects to reference the wrong memory
+      // location.  This should never happen and is a UAF if it does.
+      // Therefore make this a release assertion.
+      MOZ_RELEASE_ASSERT(result.responseList().Length() <
+                         result.responseList().Capacity());
       result.responseList().AppendElement(aSavedResponse.mValue);
       SerializeResponseBody(aSavedResponse, aStreamList,
                             &result.responseList().LastElement());
       break;
     }
     case CacheOpResult::TStorageMatchResult:
     {
       StorageMatchResult& result = mOpResult.get_StorageMatchResult();
@@ -547,16 +481,22 @@ AutoParentOpResult::Add(const SavedReque
                         StreamList* aStreamList)
 {
   MOZ_ASSERT(!mSent);
 
   switch (mOpResult.type()) {
     case CacheOpResult::TCacheKeysResult:
     {
       CacheKeysResult& result = mOpResult.get_CacheKeysResult();
+      // Ensure that we don't realloc the array since this can result
+      // in our AutoIPCStream objects to reference the wrong memory
+      // location.  This should never happen and is a UAF if it does.
+      // Therefore make this a release assertion.
+      MOZ_RELEASE_ASSERT(result.requestList().Length() <
+                         result.requestList().Capacity());
       result.requestList().AppendElement(aSavedRequest.mValue);
       CacheRequest& request = result.requestList().LastElement();
 
       if (!aSavedRequest.mHasBodyId) {
         request.body() = void_t();
         break;
       }
 
@@ -570,16 +510,19 @@ AutoParentOpResult::Add(const SavedReque
   }
 }
 
 const CacheOpResult&
 AutoParentOpResult::SendAsOpResult()
 {
   MOZ_ASSERT(!mSent);
   mSent = true;
+  for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
+    autoStream->TakeValue();
+  }
   return mOpResult;
 }
 
 void
 AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
                                           StreamList* aStreamList,
                                           CacheResponse* aResponseOut)
 {
@@ -616,17 +559,17 @@ AutoParentOpResult::SerializeReadStream(
       NS_WARNING("Cache failed to create stream control actor.");
       return;
     }
   }
 
   aStreamList->SetStreamControl(mStreamControl);
 
   RefPtr<ReadStream> readStream = ReadStream::Create(mStreamControl,
-                                                       aId, stream);
+                                                     aId, stream);
   ErrorResult rv;
-  readStream->Serialize(aReadStreamOut, rv);
+  readStream->Serialize(aReadStreamOut, mStreamCleanupList, rv);
   MOZ_ASSERT(!rv.Failed());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/AutoUtils.h
+++ b/dom/cache/AutoUtils.h
@@ -16,16 +16,17 @@
 struct nsID;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace ipc {
 class PBackgroundParent;
+class AutoIPCStream;
 } // namespace ipc
 
 namespace dom {
 
 class InternalRequest;
 
 namespace cache {
 
@@ -44,37 +45,40 @@ class StreamList;
 // deserialization case is handled by creating a ReadStream object.
 
 class MOZ_STACK_CLASS AutoChildOpArgs final
 {
 public:
   typedef TypeUtils::BodyAction BodyAction;
   typedef TypeUtils::SchemeAction SchemeAction;
 
-  AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs);
+  AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs,
+                  uint32_t aEntryCount);
   ~AutoChildOpArgs();
 
   void Add(InternalRequest* aRequest, BodyAction aBodyAction,
            SchemeAction aSchemeAction, ErrorResult& aRv);
   void Add(InternalRequest* aRequest, BodyAction aBodyAction,
            SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv);
 
   const CacheOpArgs& SendAsOpArgs();
 
 private:
   TypeUtils* mTypeUtils;
   CacheOpArgs mOpArgs;
+  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>> mStreamCleanupList;
   bool mSent;
 };
 
 class MOZ_STACK_CLASS AutoParentOpResult final
 {
 public:
   AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
-                     const CacheOpResult& aOpResult);
+                     const CacheOpResult& aOpResult,
+                     uint32_t aEntryCount);
   ~AutoParentOpResult();
 
   void Add(CacheId aOpenedCacheId, Manager* aManager);
   void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
   void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
 
   const CacheOpResult& SendAsOpResult();
 
@@ -84,16 +88,17 @@ private:
                              CacheResponse* aResponseOut);
 
   void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
                            CacheReadStream* aReadStreamOut);
 
   mozilla::ipc::PBackgroundParent* mManager;
   CacheOpResult mOpResult;
   CacheStreamControlParent* mStreamControl;
+  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>> mStreamCleanupList;
   bool mSent;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_AutoUtils_h
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -10,30 +10,30 @@
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/CacheChild.h"
-#include "mozilla/dom/cache/CachePushStreamChild.h"
 #include "mozilla/dom/cache/Feature.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::WorkerPrivate;
+using mozilla::ipc::PBackgroundChild;
 
 namespace {
 
 bool
 IsValidPutRequestURL(const nsAString& aUrl, ErrorResult& aRv)
 {
   bool validScheme = false;
 
@@ -259,17 +259,17 @@ Cache::Match(const RequestOrUSVString& a
   RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params));
+  AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params), 1);
 
   args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return ExecuteOp(args, aRv);
 }
@@ -283,17 +283,17 @@ Cache::MatchAll(const Optional<RequestOr
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params));
+  AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1);
 
   if (aRequest.WasPassed()) {
     RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
                                                      IgnoreBody, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
@@ -405,17 +405,17 @@ Cache::Put(const RequestOrUSVString& aRe
     return nullptr;
   }
 
   RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  AutoChildOpArgs args(this, CachePutAllArgs());
+  AutoChildOpArgs args(this, CachePutAllArgs(), 1);
 
   args.Add(ir, ReadBody, TypeErrorOnInvalidScheme,
            aResponse, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return ExecuteOp(args, aRv);
@@ -435,17 +435,17 @@ Cache::Delete(const RequestOrUSVString& 
   RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params));
+  AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params), 1);
 
   args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return ExecuteOp(args, aRv);
 }
@@ -459,17 +459,17 @@ Cache::Keys(const Optional<RequestOrUSVS
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params));
+  AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1);
 
   if (aRequest.WasPassed()) {
     RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
                                                      IgnoreBody, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
@@ -535,23 +535,22 @@ Cache::GetGlobalObject() const
 #ifdef DEBUG
 void
 Cache::AssertOwningThread() const
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
 }
 #endif
 
-CachePushStreamChild*
-Cache::CreatePushStream(nsIAsyncInputStream* aStream)
+PBackgroundChild*
+Cache::GetIPCManager()
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
   MOZ_ASSERT(mActor);
-  MOZ_ASSERT(aStream);
-  return mActor->CreatePushStream(this, aStream);
+  return mActor->Manager();
 }
 
 Cache::~Cache()
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
   if (mActor) {
     mActor->StartDestroyFromListener();
     // DestroyInternal() is called synchronously by StartDestroyFromListener().
@@ -636,17 +635,17 @@ Cache::PutAll(const nsTArray<RefPtr<Requ
 
   if (NS_WARN_IF(!mActor)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
-  AutoChildOpArgs args(this, CachePutAllArgs());
+  AutoChildOpArgs args(this, CachePutAllArgs(), aRequestList.Length());
 
   for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
     RefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest();
     args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -77,18 +77,18 @@ public:
   // TypeUtils methods
   virtual nsIGlobalObject*
   GetGlobalObject() const override;
 
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
-  virtual CachePushStreamChild*
-  CreatePushStream(nsIAsyncInputStream* aStream) override;
+  virtual mozilla::ipc::PBackgroundChild*
+  GetIPCManager() override;
 
 private:
   class FetchHandler;
 
   ~Cache();
 
   // Called when we're destroyed or CCed.
   void DisconnectFromActor();
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheChild.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheOpChild.h"
-#include "mozilla/dom/cache/CachePushStreamChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // Declared in ActorUtils.h
 PCacheChild*
 AllocPCacheChild()
@@ -69,26 +68,16 @@ void
 CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
                       nsISupports* aParent, const CacheOpArgs& aArgs)
 {
   mNumChildActors += 1;
   MOZ_ALWAYS_TRUE(SendPCacheOpConstructor(
     new CacheOpChild(GetFeature(), aGlobal, aParent, aPromise), aArgs));
 }
 
-CachePushStreamChild*
-CacheChild::CreatePushStream(nsISupports* aParent, nsIAsyncInputStream* aStream)
-{
-  mNumChildActors += 1;
-  auto actor = SendPCachePushStreamConstructor(
-    new CachePushStreamChild(GetFeature(), aParent, aStream));
-  MOZ_ASSERT(actor);
-  return static_cast<CachePushStreamChild*>(actor);
-}
-
 void
 CacheChild::StartDestroyFromListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
 
   // The listener should be held alive by any async operations, so if it
   // is going away then there must not be any child actors.  This in turn
   // ensures that StartDestroy() will not trigger the delayed path.
@@ -154,31 +143,16 @@ CacheChild::AllocPCacheOpChild(const Cac
 bool
 CacheChild::DeallocPCacheOpChild(PCacheOpChild* aActor)
 {
   delete aActor;
   NoteDeletedActor();
   return true;
 }
 
-PCachePushStreamChild*
-CacheChild::AllocPCachePushStreamChild()
-{
-  MOZ_CRASH("CachePushStreamChild should be manually constructed.");
-  return nullptr;
-}
-
-bool
-CacheChild::DeallocPCachePushStreamChild(PCachePushStreamChild* aActor)
-{
-  delete aActor;
-  NoteDeletedActor();
-  return true;
-}
-
 void
 CacheChild::NoteDeletedActor()
 {
   mNumChildActors -= 1;
   MaybeFlushDelayedDestroy();
 }
 
 void
--- a/dom/cache/CacheChild.h
+++ b/dom/cache/CacheChild.h
@@ -17,17 +17,16 @@ namespace mozilla {
 namespace dom {
 
 class Promise;
 
 namespace cache {
 
 class Cache;
 class CacheOpArgs;
-class CachePushStreamChild;
 
 class CacheChild final : public PCacheChild
                        , public ActorChild
 {
 public:
   class MOZ_RAII AutoLock final
   {
     CacheChild* mActor;
@@ -55,19 +54,16 @@ public:
   // method.  Also, Cache must call StartDestroyFromListener() on the actor in
   // its destructor to trigger ActorDestroy() if it has not been called yet.
   void ClearListener();
 
   void
   ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
             nsISupports* aParent, const CacheOpArgs& aArgs);
 
-  CachePushStreamChild*
-  CreatePushStream(nsISupports* aParent, nsIAsyncInputStream* aStream);
-
   // Our parent Listener object has gone out of scope and is being destroyed.
   void StartDestroyFromListener();
 
 private:
   // ActorChild methods
 
   // Feature is trying to destroy due to worker shutdown.
   virtual void StartDestroy() override;
@@ -77,22 +73,16 @@ private:
   ActorDestroy(ActorDestroyReason aReason) override;
 
   virtual PCacheOpChild*
   AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
 
   virtual bool
   DeallocPCacheOpChild(PCacheOpChild* aActor) override;
 
-  virtual PCachePushStreamChild*
-  AllocPCachePushStreamChild() override;
-
-  virtual bool
-  DeallocPCachePushStreamChild(PCachePushStreamChild* aActor) override;
-
   // utility methods
   void
   NoteDeletedActor();
 
   void
   MaybeFlushDelayedDestroy();
 
   // Methods used to temporarily force the actor alive.  Only called from
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -12,16 +12,18 @@
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheStreamControlChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+using mozilla::ipc::PBackgroundChild;
+
 namespace {
 
 void
 AddFeatureToStreamChild(const CacheReadStream& aReadStream, Feature* aFeature)
 {
   MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
   CacheStreamControlChild* cacheControl =
     static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
@@ -189,20 +191,20 @@ CacheOpChild::GetGlobalObject() const
 #ifdef DEBUG
 void
 CacheOpChild::AssertOwningThread() const
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
 }
 #endif
 
-CachePushStreamChild*
-CacheOpChild::CreatePushStream(nsIAsyncInputStream* aStream)
+PBackgroundChild*
+CacheOpChild::GetIPCManager()
 {
-  MOZ_CRASH("CacheOpChild should never create a push stream actor!");
+  MOZ_CRASH("CacheOpChild does not implement TypeUtils::GetIPCManager()");
 }
 
 void
 CacheOpChild::HandleResponse(const CacheResponseOrVoid& aResponseOrVoid)
 {
   if (aResponseOrVoid.type() == CacheResponseOrVoid::Tvoid_t) {
     mPromise->MaybeResolve(JS::UndefinedHandleValue);
     return;
--- a/dom/cache/CacheOpChild.h
+++ b/dom/cache/CacheOpChild.h
@@ -50,18 +50,18 @@ private:
   virtual nsIGlobalObject*
   GetGlobalObject() const override;
 
 #ifdef DEBUG
   virtual void
   AssertOwningThread() const override;
 #endif
 
-  virtual CachePushStreamChild*
-  CreatePushStream(nsIAsyncInputStream* aStream) override;
+  virtual mozilla::ipc::PBackgroundChild*
+  GetIPCManager() override;
 
   // Utility methods
   void
   HandleResponse(const CacheResponseOrVoid& aResponseOrVoid);
 
   void
   HandleResponseList(const nsTArray<CacheResponse>& aResponseList);
 
--- a/dom/cache/CacheOpParent.cpp
+++ b/dom/cache/CacheOpParent.cpp
@@ -3,28 +3,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheOpParent.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/AutoUtils.h"
-#include "mozilla/dom/cache/CachePushStreamParent.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/SendStream.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::FileDescriptorSetParent;
 using mozilla::ipc::PBackgroundParent;
+using mozilla::ipc::SendStreamParent;
 
 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
                              const CacheOpArgs& aOpArgs)
   : mIpcManager(aIpcManager)
   , mCacheId(aCacheId)
   , mNamespace(INVALID_NAMESPACE)
   , mOpArgs(aOpArgs)
 {
@@ -165,23 +166,27 @@ CacheOpParent::OnOpComplete(ErrorResult&
   // Never send an op-specific result if we have an error.  Instead, send
   // void_t() to ensure that we don't leak actors on the child side.
   if (NS_WARN_IF(aRv.Failed())) {
     Unused << Send__delete__(this, aRv, void_t());
     aRv.SuppressException(); // We serialiazed it, as best we could.
     return;
   }
 
+  uint32_t entryCount = std::max(1lu, static_cast<unsigned long>(
+                                      std::max(aSavedResponseList.Length(),
+                                               aSavedRequestList.Length())));
+
   // The result must contain the appropriate type at this point.  It may
   // or may not contain the additional result data yet.  For types that
   // do not need special processing, it should already be set.  If the
   // result requires actor-specific operations, then we do that below.
   // If the type and data types don't match, then we will trigger an
   // assertion in AutoParentOpResult::Add().
-  AutoParentOpResult result(mIpcManager, aResult);
+  AutoParentOpResult result(mIpcManager, aResult, entryCount);
 
   if (aOpenedCacheId != INVALID_CACHE_ID) {
     result.Add(aOpenedCacheId, mManager);
   }
 
   for (uint32_t i = 0; i < aSavedResponseList.Length(); ++i) {
     result.Add(aSavedResponseList[i], aStreamList);
   }
@@ -198,49 +203,24 @@ CacheOpParent::DeserializeCacheStream(co
 {
   if (aStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
     return nullptr;
   }
 
   nsCOMPtr<nsIInputStream> stream;
   const CacheReadStream& readStream = aStreamOrVoid.get_CacheReadStream();
 
-  // Option 1: A push stream actor was sent for nsPipe data
-  if (readStream.pushStreamParent()) {
-    MOZ_ASSERT(!readStream.controlParent());
-    CachePushStreamParent* pushStream =
-      static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
-    stream = pushStream->TakeReader();
-    MOZ_ASSERT(stream);
-    return stream.forget();
-  }
-
-  // Option 2: One of our own ReadStreams was passed back to us with a stream
+  // Option 1: One of our own ReadStreams was passed back to us with a stream
   //           control actor.
   stream = ReadStream::Create(readStream);
   if (stream) {
     return stream.forget();
   }
 
-  // Option 3: A stream was serialized using normal methods.
-  AutoTArray<FileDescriptor, 4> fds;
-  if (readStream.fds().type() ==
-      OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-
-    FileDescriptorSetParent* fdSetActor =
-      static_cast<FileDescriptorSetParent*>(readStream.fds().get_PFileDescriptorSetParent());
-    MOZ_ASSERT(fdSetActor);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    if (!fdSetActor->Send__delete__(fdSetActor)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to delete fd set actor.");
-    }
-  }
-
-  return DeserializeInputStream(readStream.params(), fds);
+  // Option 2: A stream was serialized using normal methods or passed
+  //           as a PSendStream actor.  Use the standard method for
+  //           extracting the resulting stream.
+  return DeserializeIPCStream(readStream.stream());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheParent.cpp
+++ b/dom/cache/CacheParent.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheParent.h"
 
 #include "mozilla/dom/cache/CacheOpParent.h"
-#include "mozilla/dom/cache/CachePushStreamParent.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // Declared in ActorUtils.h
 void
@@ -70,29 +69,16 @@ bool
 CacheParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
                                      const CacheOpArgs& aOpArgs)
 {
   auto actor = static_cast<CacheOpParent*>(aActor);
   actor->Execute(mManager);
   return true;
 }
 
-PCachePushStreamParent*
-CacheParent::AllocPCachePushStreamParent()
-{
-  return CachePushStreamParent::Create();
-}
-
-bool
-CacheParent::DeallocPCachePushStreamParent(PCachePushStreamParent* aActor)
-{
-  delete aActor;
-  return true;
-}
-
 bool
 CacheParent::RecvTeardown()
 {
   if (!Send__delete__(this)) {
     // child process is gone, warn and allow actor to clean up normally
     NS_WARNING("Cache failed to send delete.");
   }
   return true;
--- a/dom/cache/CacheParent.h
+++ b/dom/cache/CacheParent.h
@@ -31,22 +31,16 @@ private:
 
   virtual bool
   DeallocPCacheOpParent(PCacheOpParent* aActor) override;
 
   virtual bool
   RecvPCacheOpConstructor(PCacheOpParent* actor,
                           const CacheOpArgs& aOpArgs) override;
 
-  virtual PCachePushStreamParent*
-  AllocPCachePushStreamParent() override;
-
-  virtual bool
-  DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
-
   virtual bool
   RecvTeardown() override;
 
   RefPtr<cache::Manager> mManager;
   const CacheId mCacheId;
 };
 
 } // namespace cache
deleted file mode 100644
--- a/dom/cache/CachePushStreamChild.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/cache/CachePushStreamChild.h"
-
-#include "mozilla/unused.h"
-#include "nsIAsyncInputStream.h"
-#include "nsICancelableRunnable.h"
-#include "nsIThread.h"
-#include "nsStreamUtils.h"
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-class CachePushStreamChild::Callback final : public nsIInputStreamCallback
-                                           , public CancelableRunnable
-{
-public:
-  explicit Callback(CachePushStreamChild* aActor)
-    : mActor(aActor)
-    , mOwningThread(NS_GetCurrentThread())
-  {
-    MOZ_ASSERT(mActor);
-  }
-
-  NS_IMETHOD
-  OnInputStreamReady(nsIAsyncInputStream* aStream) override
-  {
-    // any thread
-    if (mOwningThread == NS_GetCurrentThread()) {
-      return Run();
-    }
-
-    // If this fails, then it means the owning thread is a Worker that has
-    // been shutdown.  Its ok to lose the event in this case because the
-    // CachePushStreamChild listens for this event through the Feature.
-    nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch stream readable event to owning thread");
-    }
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    if (mActor) {
-      mActor->OnStreamReady(this);
-    }
-    return NS_OK;
-  }
-
-  nsresult
-  Cancel() override
-  {
-    // Cancel() gets called when the Worker thread is being shutdown.  We have
-    // nothing to do here because CachePushStreamChild handles this case via
-    // the Feature.
-    return NS_OK;
-  }
-
-  void
-  ClearActor()
-  {
-    MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-    MOZ_ASSERT(mActor);
-    mActor = nullptr;
-  }
-
-private:
-  ~Callback()
-  {
-    // called on any thread
-
-    // ClearActor() should be called before the Callback is destroyed
-    MOZ_ASSERT(!mActor);
-  }
-
-  CachePushStreamChild* mActor;
-  nsCOMPtr<nsIThread> mOwningThread;
-
-  NS_DECL_ISUPPORTS_INHERITED
-};
-
-NS_IMPL_ISUPPORTS_INHERITED(CachePushStreamChild::Callback,
-                            CancelableRunnable,
-                            nsIInputStreamCallback);
-
-CachePushStreamChild::CachePushStreamChild(Feature* aFeature,
-                                           nsISupports* aParent,
-                                           nsIAsyncInputStream* aStream)
-  : mParent(aParent)
-  , mStream(aStream)
-  , mClosed(false)
-{
-  MOZ_ASSERT(mParent);
-  MOZ_ASSERT(mStream);
-  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
-  SetFeature(aFeature);
-}
-
-CachePushStreamChild::~CachePushStreamChild()
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-  MOZ_ASSERT(mClosed);
-  MOZ_ASSERT(!mCallback);
-}
-
-void
-CachePushStreamChild::Start()
-{
-  DoRead();
-}
-
-void
-CachePushStreamChild::StartDestroy()
-{
-  // The worker has signaled its shutting down, but continue streaming.  The
-  // Cache is now designed to hold the worker open until all async operations
-  // complete.
-}
-
-void
-CachePushStreamChild::ActorDestroy(ActorDestroyReason aReason)
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-
-  // If the parent side runs into a problem then the actor will be destroyed.
-  // In this case we have not run OnEnd(), so still need to close the input
-  // stream.
-  if (!mClosed) {
-    mStream->CloseWithStatus(NS_ERROR_ABORT);
-    mClosed = true;
-  }
-
-  if (mCallback) {
-    mCallback->ClearActor();
-    mCallback = nullptr;
-  }
-
-  RemoveFeature();
-}
-
-void
-CachePushStreamChild::DoRead()
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-  MOZ_ASSERT(!mClosed);
-  MOZ_ASSERT(!mCallback);
-
-  // The input stream (likely a pipe) probably uses a segment size of
-  // 4kb.  If there is data already buffered it would be nice to aggregate
-  // multiple segments into a single IPC call.  Conversely, don't send too
-  // too large of a buffer in a single call to avoid spiking memory.
-  static const uint64_t kMaxBytesPerMessage = 32 * 1024;
-  static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
-                "kMaxBytesPerMessage must cleanly cast to uint32_t");
-
-  while (!mClosed) {
-    // Use non-auto here as we're unlikely to hit stack storage with the
-    // sizes we are sending.  Also, it would be nice to avoid another copy
-    // to the IPC layer which we avoid if we use COW strings.  Unfortunately
-    // IPC does not seem to support passing dependent storage types.
-    nsCString buffer;
-
-    uint64_t available = 0;
-    nsresult rv = mStream->Available(&available);
-    if (NS_FAILED(rv)) {
-      OnEnd(rv);
-      return;
-    }
-
-    if (available == 0) {
-      Wait();
-      return;
-    }
-
-    uint32_t expectedBytes =
-      static_cast<uint32_t>(std::min(available, kMaxBytesPerMessage));
-
-    buffer.SetLength(expectedBytes);
-
-    uint32_t bytesRead = 0;
-    rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
-    buffer.SetLength(bytesRead);
-
-    // If we read any data from the stream, send it across.
-    if (!buffer.IsEmpty()) {
-      Unused << SendBuffer(buffer);
-    }
-
-    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
-      Wait();
-      return;
-    }
-
-    // Any other error or zero-byte read indicates end-of-stream
-    if (NS_FAILED(rv) || buffer.IsEmpty()) {
-      OnEnd(rv);
-      return;
-    }
-  }
-}
-
-void
-CachePushStreamChild::Wait()
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-  MOZ_ASSERT(!mClosed);
-  MOZ_ASSERT(!mCallback);
-
-  // Set mCallback immediately instead of waiting for success.  Its possible
-  // AsyncWait() will callback synchronously.
-  mCallback = new Callback(this);
-  nsresult rv = mStream->AsyncWait(mCallback, 0, 0, nullptr);
-  if (NS_FAILED(rv)) {
-    OnEnd(rv);
-    return;
-  }
-}
-
-void
-CachePushStreamChild::OnStreamReady(Callback* aCallback)
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-  MOZ_ASSERT(mCallback);
-  MOZ_ASSERT(aCallback == mCallback);
-  mCallback->ClearActor();
-  mCallback = nullptr;
-  DoRead();
-}
-
-void
-CachePushStreamChild::OnEnd(nsresult aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
-  MOZ_ASSERT(aRv != NS_BASE_STREAM_WOULD_BLOCK);
-
-  if (mClosed) {
-    return;
-  }
-
-  mClosed = true;
-
-  mStream->CloseWithStatus(aRv);
-
-  if (aRv == NS_BASE_STREAM_CLOSED) {
-    aRv = NS_OK;
-  }
-
-  // This will trigger an ActorDestroy() from the parent side
-  Unused << SendClose(aRv);
-}
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/cache/CachePushStreamChild.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_cache_CachePushStreamChild_h
-#define mozilla_dom_cache_CachePushStreamChild_h
-
-#include "mozilla/dom/cache/ActorChild.h"
-#include "mozilla/dom/cache/PCachePushStreamChild.h"
-#include "nsCOMPtr.h"
-
-class nsIAsyncInputStream;
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-class CachePushStreamChild final : public PCachePushStreamChild
-                                 , public ActorChild
-{
-  friend class CacheChild;
-
-public:
-  void Start();
-
-  virtual void StartDestroy() override;
-
-private:
-  class Callback;
-
-  // This class must be constructed using CacheChild::CreatePushStream()
-  CachePushStreamChild(Feature* aFeature, nsISupports* aParent,
-                       nsIAsyncInputStream* aStream);
-  ~CachePushStreamChild();
-
-  // PCachePushStreamChild methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
-
-  void DoRead();
-
-  void Wait();
-
-  void OnStreamReady(Callback* aCallback);
-
-  void OnEnd(nsresult aRv);
-
-  nsCOMPtr<nsISupports> mParent;
-  nsCOMPtr<nsIAsyncInputStream> mStream;
-  RefPtr<Callback> mCallback;
-  bool mClosed;
-
-  NS_DECL_OWNINGTHREAD
-};
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_cache_CachePushStreamChild_h
deleted file mode 100644
--- a/dom/cache/CachePushStreamParent.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/cache/CachePushStreamParent.h"
-
-#include "mozilla/unused.h"
-#include "nsIAsyncInputStream.h"
-#include "nsIAsyncOutputStream.h"
-#include "nsIPipe.h"
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-// static
-CachePushStreamParent*
-CachePushStreamParent::Create()
-{
-  // use async versions for both reader and writer even though we are
-  // opening the writer as an infinite stream.  We want to be able to
-  // use CloseWithStatus() to communicate errors through the pipe.
-  nsCOMPtr<nsIAsyncInputStream> reader;
-  nsCOMPtr<nsIAsyncOutputStream> writer;
-
-  // Use an "infinite" pipe because we cannot apply back-pressure through
-  // the async IPC layer at the moment.  Blocking the IPC worker thread
-  // is not desirable, either.
-  nsresult rv = NS_NewPipe2(getter_AddRefs(reader),
-                            getter_AddRefs(writer),
-                            true, true,   // non-blocking
-                            0,            // segment size
-                            UINT32_MAX);  // "infinite" pipe
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  return new CachePushStreamParent(reader, writer);
-}
-
-CachePushStreamParent::~CachePushStreamParent()
-{
-}
-
-already_AddRefed<nsIInputStream>
-CachePushStreamParent::TakeReader()
-{
-  MOZ_ASSERT(mReader);
-  return mReader.forget();
-}
-
-void
-CachePushStreamParent::ActorDestroy(ActorDestroyReason aReason)
-{
-  // If we were gracefully closed we should have gotten RecvClose().  In
-  // that case, the writer will already be closed and this will have no
-  // effect.  This just aborts the writer in the case where the child process
-  // crashes.
-  mWriter->CloseWithStatus(NS_ERROR_ABORT);
-}
-
-bool
-CachePushStreamParent::RecvBuffer(const nsCString& aBuffer)
-{
-  uint32_t numWritten = 0;
-
-  // This should only fail if we hit an OOM condition.
-  nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    RecvClose(rv);
-  }
-
-  return true;
-}
-
-bool
-CachePushStreamParent::RecvClose(const nsresult& aRv)
-{
-  mWriter->CloseWithStatus(aRv);
-  Unused << Send__delete__(this);
-  return true;
-}
-
-CachePushStreamParent::CachePushStreamParent(nsIAsyncInputStream* aReader,
-                                             nsIAsyncOutputStream* aWriter)
-  : mReader(aReader)
-  , mWriter(aWriter)
-{
-  MOZ_ASSERT(mReader);
-  MOZ_ASSERT(mWriter);
-}
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/cache/CachePushStreamParent.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_cache_CachePushStreamParent_h
-#define mozilla_dom_cache_CachePushStreamParent_h
-
-#include "mozilla/dom/cache/PCachePushStreamParent.h"
-
-class nsIAsyncInputStream;
-class nsIAsyncOutputStream;
-class nsIInputStream;
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-class CachePushStreamParent final : public PCachePushStreamParent
-{
-public:
-  static CachePushStreamParent*
-  Create();
-
-  ~CachePushStreamParent();
-
-  already_AddRefed<nsIInputStream>
-  TakeReader();
-
-private:
-  CachePushStreamParent(nsIAsyncInputStream* aReader,
-                        nsIAsyncOutputStream* aWriter);
-
-  // PCachePushStreamParent methods
-  virtual void
-  ActorDestroy(ActorDestroyReason aReason) override;
-
-  virtual bool
-  RecvBuffer(const nsCString& aBuffer) override;
-
-  virtual bool
-  RecvClose(const nsresult& aRv) override;
-
-  nsCOMPtr<nsIAsyncInputStream> mReader;
-  nsCOMPtr<nsIAsyncOutputStream> mWriter;
-
-  NS_DECL_OWNINGTHREAD
-};
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_cache_CachePushStreamParent_h
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -574,21 +574,23 @@ CacheStorage::GetGlobalObject() const
 #ifdef DEBUG
 void
 CacheStorage::AssertOwningThread() const
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 }
 #endif
 
-CachePushStreamChild*
-CacheStorage::CreatePushStream(nsIAsyncInputStream* aStream)
+PBackgroundChild*
+CacheStorage::GetIPCManager()
 {
   // This is true because CacheStorage always uses IgnoreBody for requests.
-  MOZ_CRASH("CacheStorage should never create a push stream.");
+  // So we should never need to get the IPC manager during Request or
+  // Response serialization.
+  MOZ_CRASH("CacheStorage does not implement TypeUtils::GetIPCManager()");
 }
 
 CacheStorage::~CacheStorage()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
   if (mActor) {
     mActor->StartDestroyFromListener();
     // DestroyInternal() is called synchronously by StartDestroyFromListener().
@@ -602,17 +604,17 @@ CacheStorage::MaybeRunPendingRequests()
 {
   if (!mActor) {
     return;
   }
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
     ErrorResult rv;
     nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
-    AutoChildOpArgs args(this, entry->mArgs);
+    AutoChildOpArgs args(this, entry->mArgs, 1);
     if (entry->mRequest) {
       args.Add(entry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv);
     }
     if (NS_WARN_IF(rv.Failed())) {
       entry->mPromise->MaybeReject(rv);
       continue;
     }
     mActor->ExecuteOp(mGlobal, entry->mPromise, this, args.SendAsOpArgs());
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -87,18 +87,18 @@ public:
   void DestroyInternal(CacheStorageChild* aActor);
 
   // TypeUtils methods
   virtual nsIGlobalObject* GetGlobalObject() const override;
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
-  virtual CachePushStreamChild*
-  CreatePushStream(nsIAsyncInputStream* aStream) override;
+  virtual mozilla::ipc::PBackgroundChild*
+  GetIPCManager() override;
 
 private:
   CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
                const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
   explicit CacheStorage(nsresult aFailureResult);
   ~CacheStorage();
 
   void MaybeRunPendingRequests();
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+using mozilla::ipc::AutoIPCStream;
 using mozilla::ipc::FileDescriptor;
 using mozilla::ipc::FileDescriptorSetChild;
 using mozilla::ipc::OptionalFileDescriptorSet;
 using mozilla::ipc::PFileDescriptorSetChild;
 
 // declared in ActorUtils.h
 PCacheStreamControlChild*
 AllocPCacheStreamControlChild()
@@ -88,52 +89,26 @@ void
 CacheStreamControlChild::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   aReadStreamOut->controlParent() = nullptr;
   aReadStreamOut->controlChild() = this;
 }
 
 void
-CacheStreamControlChild::SerializeFds(CacheReadStream* aReadStreamOut,
-                                      const nsTArray<FileDescriptor>& aFds)
+CacheStreamControlChild::SerializeStream(CacheReadStream* aReadStreamOut,
+                                         nsIInputStream* aStream,
+                                         nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
-  PFileDescriptorSetChild* fdSet = nullptr;
-  if (!aFds.IsEmpty()) {
-    fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
-    for (uint32_t i = 1; i < aFds.Length(); ++i) {
-      Unused << fdSet->SendAddFileDescriptor(aFds[i]);
-    }
-  }
-
-  if (fdSet) {
-    aReadStreamOut->fds() = fdSet;
-  } else {
-    aReadStreamOut->fds() = void_t();
-  }
-}
-
-void
-CacheStreamControlChild::DeserializeFds(const CacheReadStream& aReadStream,
-                                        nsTArray<FileDescriptor>& aFdsOut)
-{
-  if (aReadStream.fds().type() !=
-      OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-    return;
-  }
-
-  auto fdSetActor = static_cast<FileDescriptorSetChild*>(
-    aReadStream.fds().get_PFileDescriptorSetChild());
-  MOZ_ASSERT(fdSetActor);
-
-  fdSetActor->ForgetFileDescriptors(aFdsOut);
-  MOZ_ASSERT(!aFdsOut.IsEmpty());
-
-  Unused << fdSetActor->Send__delete__(fdSetActor);
+  MOZ_ASSERT(aReadStreamOut);
+  MOZ_ASSERT(aStream);
+  UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
+  autoStream->Serialize(aStream, Manager());
+  aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   Unused << SendNoteClosed(aId);
 
--- a/dom/cache/CacheStreamControlChild.h
+++ b/dom/cache/CacheStreamControlChild.h
@@ -8,16 +8,19 @@
 #define mozilla_dom_cache_CacheStreamControlChild_h
 
 #include "mozilla/dom/cache/ActorChild.h"
 #include "mozilla/dom/cache/PCacheStreamControlChild.h"
 #include "mozilla/dom/cache/StreamControl.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
+namespace ipc {
+class AutoIPCStream;
+} // namespace ipc
 namespace dom {
 namespace cache {
 
 class ReadStream;
 
 class CacheStreamControlChild final : public PCacheStreamControlChild
                                     , public StreamControl
                                     , public ActorChild
@@ -29,22 +32,18 @@ public:
   // ActorChild methods
   virtual void StartDestroy() override;
 
   // StreamControl methods
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
-  SerializeFds(CacheReadStream* aReadStreamOut,
-               const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
-
-  virtual void
-  DeserializeFds(const CacheReadStream& aReadStream,
-                 nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
+  SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
+                  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) override;
 
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
   AssertOwningThread() override;
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -48,55 +48,25 @@ void
 CacheStreamControlParent::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   aReadStreamOut->controlChild() = nullptr;
   aReadStreamOut->controlParent() = this;
 }
 
 void
-CacheStreamControlParent::SerializeFds(CacheReadStream* aReadStreamOut,
-                                       const nsTArray<FileDescriptor>& aFds)
+CacheStreamControlParent::SerializeStream(CacheReadStream* aReadStreamOut,
+                                          nsIInputStream* aStream,
+                                          nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
-  PFileDescriptorSetParent* fdSet = nullptr;
-  if (!aFds.IsEmpty()) {
-    fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
-    for (uint32_t i = 1; i < aFds.Length(); ++i) {
-      Unused << fdSet->SendAddFileDescriptor(aFds[i]);
-    }
-  }
-
-  if (fdSet) {
-    aReadStreamOut->fds() = fdSet;
-  } else {
-    aReadStreamOut->fds() = void_t();
-  }
-}
-
-void
-CacheStreamControlParent::DeserializeFds(const CacheReadStream& aReadStream,
-                                         nsTArray<FileDescriptor>& aFdsOut)
-{
-  if (aReadStream.fds().type() !=
-      OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
-    return;
-  }
-
-  FileDescriptorSetParent* fdSetActor =
-    static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
-  MOZ_ASSERT(fdSetActor);
-
-  fdSetActor->ForgetFileDescriptors(aFdsOut);
-  MOZ_ASSERT(!aFdsOut.IsEmpty());
-
-  if (!fdSetActor->Send__delete__(fdSetActor)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to delete fd set actor.");
-  }
+  MOZ_ASSERT(aStream);
+  UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
+  autoStream->Serialize(aStream, Manager());
+  aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
 CacheStreamControlParent::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   RecvNoteClosed(aId);
 }
--- a/dom/cache/CacheStreamControlParent.h
+++ b/dom/cache/CacheStreamControlParent.h
@@ -7,16 +7,19 @@
 #ifndef mozilla_dom_cache_CacheStreamControlParent_h
 #define mozilla_dom_cache_CacheStreamControlParent_h
 
 #include "mozilla/dom/cache/PCacheStreamControlParent.h"
 #include "mozilla/dom/cache/StreamControl.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
+namespace ipc {
+class AutoIPCStream;
+} // namespace ipc
 namespace dom {
 namespace cache {
 
 class ReadStream;
 class StreamList;
 
 class CacheStreamControlParent final : public PCacheStreamControlParent
                                      , public StreamControl
@@ -30,22 +33,18 @@ public:
   void CloseAll();
   void Shutdown();
 
   // StreamControl methods
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
-  SerializeFds(CacheReadStream* aReadStreamOut,
-               const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
-
-  virtual void
-  DeserializeFds(const CacheReadStream& aReadStream,
-                 nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
+  SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
+                  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) override;
 
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
   AssertOwningThread() override;
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PCache;
-include protocol PCachePushStream;
 include protocol PCacheStreamControl;
-include InputStreamParams;
+include protocol PSendStream;
+include IPCStream;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
 using ReferrerPolicy from "mozilla/dom/cache/IPCUtils.h";
 using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
 using RequestMode from "mozilla/dom/cache/IPCUtils.h";
 using RequestCache from "mozilla/dom/cache/IPCUtils.h";
@@ -30,20 +30,18 @@ struct CacheQueryParams
   bool ignoreVary;
   bool cacheNameSet;
   nsString cacheName;
 };
 
 struct CacheReadStream
 {
   nsID id;
-  OptionalInputStreamParams params;
-  OptionalFileDescriptorSet fds;
   nullable PCacheStreamControl control;
-  nullable PCachePushStream pushStream;
+  IPCStream stream;
 };
 
 union CacheReadStreamOrVoid
 {
   void_t;
   CacheReadStream;
 };
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -2,35 +2,36 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/DBSchema.h"
 
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/HeadersBinding.h"
 #include "mozilla/dom/InternalHeaders.h"
+#include "mozilla/dom/RequestBinding.h"
+#include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "mozStorageHelper.h"
 #include "nsCOMPtr.h"
-#include "nsTArray.h"
 #include "nsCRT.h"
 #include "nsHttp.h"
+#include "nsIContentPolicy.h"
 #include "nsICryptoHash.h"
 #include "nsNetCID.h"
-#include "mozilla/BasePrincipal.h"
-#include "mozilla/dom/HeadersBinding.h"
-#include "mozilla/dom/RequestBinding.h"
-#include "mozilla/dom/ResponseBinding.h"
-#include "nsIContentPolicy.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 namespace db {
 
 const int32_t kFirstShippedSchemaVersion = 15;
 
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -1,24 +1,27 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/ManagerId.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "nsIPrincipal.h"
 #include "nsProxyRelease.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+using mozilla::dom::quota::QuotaManager;
+
 // static
 nsresult
 ManagerId::Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The QuotaManager::GetInfoFromPrincipal() has special logic for system
   // and about: principals.  We need to use the same modified origin in
--- a/dom/cache/PCache.ipdl
+++ b/dom/cache/PCache.ipdl
@@ -1,34 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCacheOp;
-include protocol PCachePushStream;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PSendStream;
 
 include CacheTypes;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCache
 {
   manager PBackground;
   manages PCacheOp;
-  manages PCachePushStream;
 
 parent:
   async PCacheOp(CacheOpArgs aOpArgs);
-  async PCachePushStream();
   async Teardown();
 
 child:
   async __delete__();
 };
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/PCacheOp.ipdl
+++ b/dom/cache/PCacheOp.ipdl
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PCache;
-include protocol PCachePushStream;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PSendStream;
 
 include CacheTypes;
 
 using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
deleted file mode 100644
--- a/dom/cache/PCachePushStream.ipdl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-include protocol PCache;
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-protocol PCachePushStream
-{
-  manager PCache;
-
-parent:
-  async Buffer(nsCString aBuffer);
-  async Close(nsresult aRv);
-
-child:
-  // Stream is always destroyed from the parent side.  This occurs if the
-  // parent encounters an error while writing to its pipe or if the child
-  // signals the stream should close by SendClose().
-  async __delete__();
-};
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
--- a/dom/cache/PCacheStorage.ipdl
+++ b/dom/cache/PCacheStorage.ipdl
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCache;
 include protocol PCacheOp;
-include protocol PCachePushStream;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PSendStream;
 
 include CacheTypes;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCacheStorage
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -5,45 +5,49 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/ReadStream.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/CacheStreamControlChild.h"
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 #include "mozilla/dom/cache/CacheTypes.h"
-#include "mozilla/ipc/FileDescriptor.h"
-#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/SnappyUncompressInputStream.h"
 #include "nsIAsyncInputStream.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::Unused;
-using mozilla::ipc::FileDescriptor;
+using mozilla::ipc::AutoIPCStream;
+using mozilla::ipc::IPCStream;
 
 // ----------------------------------------------------------------------------
 
 // The inner stream class.  This is where all of the real work is done.  As
 // an invariant Inner::Close() must be called before ~Inner().  This is
 // guaranteed by our outer ReadStream class.
 class ReadStream::Inner final : public ReadStream::Controllable
 {
 public:
   Inner(StreamControl* aControl, const nsID& aId,
         nsIInputStream* aStream);
 
   void
-  Serialize(CacheReadStreamOrVoid* aReadStreamOut, ErrorResult& aRv);
+  Serialize(CacheReadStreamOrVoid* aReadStreamOut,
+            nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+            ErrorResult& aRv);
 
   void
-  Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv);
+  Serialize(CacheReadStream* aReadStreamOut,
+            nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+            ErrorResult& aRv);
 
   // ReadStream::Controllable methods
   virtual void
   CloseStream() override;
 
   virtual void
   CloseStreamWithoutReporting() override;
 
@@ -193,50 +197,46 @@ ReadStream::Inner::Inner(StreamControl* 
 {
   MOZ_ASSERT(mStream);
   MOZ_ASSERT(mControl);
   mControl->AddReadStream(this);
 }
 
 void
 ReadStream::Inner::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
+                             nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                              ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
   MOZ_ASSERT(aReadStreamOut);
-  CacheReadStream stream;
-  Serialize(&stream, aRv);
-  *aReadStreamOut = stream;
+  *aReadStreamOut = CacheReadStream();
+  Serialize(&aReadStreamOut->get_CacheReadStream(), aStreamCleanupList, aRv);
 }
 
 void
-ReadStream::Inner::Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv)
+ReadStream::Inner::Serialize(CacheReadStream* aReadStreamOut,
+                             nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+                             ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
   MOZ_ASSERT(aReadStreamOut);
 
   if (mState != Open) {
     aRv.ThrowTypeError<MSG_CACHE_STREAM_CLOSED>();
     return;
   }
 
   MOZ_ASSERT(mControl);
 
-  // If we are sending a ReadStream, then we never want to set the
-  // pushStream actors at the same time.
-  aReadStreamOut->pushStreamChild() = nullptr;
-  aReadStreamOut->pushStreamParent() = nullptr;
-
   aReadStreamOut->id() = mId;
   mControl->SerializeControl(aReadStreamOut);
+  mControl->SerializeStream(aReadStreamOut, mStream, aStreamCleanupList);
 
-  AutoTArray<FileDescriptor, 4> fds;
-  SerializeInputStream(mStream, aReadStreamOut->params(), fds);
-
-  mControl->SerializeFds(aReadStreamOut, fds);
+  MOZ_ASSERT(aReadStreamOut->stream().type() ==
+             IPCStream::TInputStreamParamsWithFds);
 
   // We're passing ownership across the IPC barrier with the control, so
   // do not signal that the stream is closed here.
   Forget();
 }
 
 void
 ReadStream::Inner::CloseStream()
@@ -437,36 +437,32 @@ ReadStream::Create(const CacheReadStream
 {
   // The parameter may or may not be for a Cache created stream.  The way we
   // tell is by looking at the stream control actor.  If the actor exists,
   // then we know the Cache created it.
   if (!aReadStream.controlChild() && !aReadStream.controlParent()) {
     return nullptr;
   }
 
-  MOZ_ASSERT(!aReadStream.pushStreamChild());
-  MOZ_ASSERT(!aReadStream.pushStreamParent());
+  MOZ_ASSERT(aReadStream.stream().type() ==
+             IPCStream::TInputStreamParamsWithFds);
 
   // Control is guaranteed to survive this method as ActorDestroy() cannot
   // run on this thread until we complete.
   StreamControl* control;
   if (aReadStream.controlChild()) {
     auto actor = static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
     control = actor;
   } else {
     auto actor = static_cast<CacheStreamControlParent*>(aReadStream.controlParent());
     control = actor;
   }
   MOZ_ASSERT(control);
 
-  AutoTArray<FileDescriptor, 4> fds;
-  control->DeserializeFds(aReadStream, fds);
-
-  nsCOMPtr<nsIInputStream> stream =
-    DeserializeInputStream(aReadStream.params(), fds);
+  nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
   MOZ_ASSERT(stream);
 
   // Currently we expect all cache read streams to be blocking file streams.
 #ifdef DEBUG
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
   MOZ_ASSERT(!asyncStream);
 #endif
 
@@ -483,25 +479,29 @@ ReadStream::Create(PCacheStreamControlPa
   MOZ_ASSERT(aControl);
   auto actor = static_cast<CacheStreamControlParent*>(aControl);
   RefPtr<Inner> inner = new Inner(actor, aId, aStream);
   RefPtr<ReadStream> ref = new ReadStream(inner);
   return ref.forget();
 }
 
 void
-ReadStream::Serialize(CacheReadStreamOrVoid* aReadStreamOut, ErrorResult& aRv)
+ReadStream::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
+                      nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+                      ErrorResult& aRv)
 {
-  mInner->Serialize(aReadStreamOut, aRv);
+  mInner->Serialize(aReadStreamOut, aStreamCleanupList, aRv);
 }
 
 void
-ReadStream::Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv)
+ReadStream::Serialize(CacheReadStream* aReadStreamOut,
+                      nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+                      ErrorResult& aRv)
 {
-  mInner->Serialize(aReadStreamOut, aRv);
+  mInner->Serialize(aReadStreamOut, aStreamCleanupList, aRv);
 }
 
 ReadStream::ReadStream(ReadStream::Inner* aInner)
   : mInner(aInner)
 {
   MOZ_ASSERT(mInner);
 }
 
--- a/dom/cache/ReadStream.h
+++ b/dom/cache/ReadStream.h
@@ -11,16 +11,19 @@
 #include "nsCOMPtr.h"
 #include "nsID.h"
 #include "nsIInputStream.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/RefPtr.h"
 #include "nsTArrayForwardDeclare.h"
 
 namespace mozilla {
+namespace ipc {
+class AutoIPCStream;
+} // namespace ipc
 namespace dom {
 namespace cache {
 
 class CacheReadStream;
 class CacheReadStreamOrVoid;
 class PCacheStreamControlParent;
 
 // IID for the dom::cache::ReadStream interface
@@ -76,18 +79,22 @@ public:
 
   static already_AddRefed<ReadStream>
   Create(const CacheReadStream& aReadStream);
 
   static already_AddRefed<ReadStream>
   Create(PCacheStreamControlParent* aControl, const nsID& aId,
          nsIInputStream* aStream);
 
-  void Serialize(CacheReadStreamOrVoid* aReadStreamOut, ErrorResult& aRv);
-  void Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv);
+  void Serialize(CacheReadStreamOrVoid* aReadStreamOut,
+                 nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
+                 ErrorResult& aRv);
+  void Serialize(CacheReadStream* aReadStreamOut,
+                 nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
+                 ErrorResult& aRv);
 
 private:
   class Inner;
 
   explicit ReadStream(Inner* aInner);
   ~ReadStream();
 
   // Hold a strong ref to an inner class that actually implements the
--- a/dom/cache/StreamControl.h
+++ b/dom/cache/StreamControl.h
@@ -10,17 +10,17 @@
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/RefPtr.h"
 #include "nsTObserverArray.h"
 
 struct nsID;
 
 namespace mozilla {
 namespace ipc {
- class FileDescriptor;
+class AutoIPCStream;
 } // namespace ipc
 namespace dom {
 namespace cache {
 
 class CacheReadStream;
 
 // Abstract class to help implement the stream control Child and Parent actors.
 // This provides an interface to partly help with serialization of IPC types,
@@ -28,22 +28,18 @@ class CacheReadStream;
 class StreamControl
 {
 public:
   // abstract interface that must be implemented by child class
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) = 0;
 
   virtual void
-  SerializeFds(CacheReadStream* aReadStreamOut,
-               const nsTArray<mozilla::ipc::FileDescriptor>& aFds) = 0;
-
-  virtual void
-  DeserializeFds(const CacheReadStream& aReadStream,
-                 nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) = 0;
+  SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
+                  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) = 0;
 
   // inherited implementation of the ReadStream::Controllable list
 
   // Begin controlling the given ReadStream.  This causes a strong ref to
   // be held by the control.  The ReadStream must call NoteClosed() or
   // ForgetReadStream() to release this ref.
   void
   AddReadStream(ReadStream::Controllable* aReadStream);
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -6,39 +6,40 @@
 
 #include "mozilla/dom/cache/TypeUtils.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/InternalRequest.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
-#include "mozilla/dom/cache/CachePushStreamChild.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/SendStream.h"
 #include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsQueryObject.h"
 #include "nsPromiseFlatString.h"
 #include "nsStreamUtils.h"
 #include "nsString.h"
 #include "nsURLParsers.h"
 #include "nsCRT.h"
 #include "nsHttp.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+using mozilla::ipc::AutoIPCStream;
 using mozilla::ipc::BackgroundChild;
 using mozilla::ipc::FileDescriptor;
 using mozilla::ipc::PBackgroundChild;
 using mozilla::ipc::PFileDescriptorSetChild;
 
 namespace {
 
 static bool
@@ -60,41 +61,16 @@ HasVaryStar(mozilla::dom::InternalHeader
         return true;
       }
     }
   }
   return false;
 }
 
 void
-SerializeNormalStream(nsIInputStream* aStream, CacheReadStream& aReadStreamOut)
-{
-  AutoTArray<FileDescriptor, 4> fds;
-  SerializeInputStream(aStream, aReadStreamOut.params(), fds);
-
-  PFileDescriptorSetChild* fdSet = nullptr;
-  if (!fds.IsEmpty()) {
-    // We should not be serializing until we have an actor ready
-    PBackgroundChild* manager = BackgroundChild::GetForCurrentThread();
-    MOZ_ASSERT(manager);
-
-    fdSet = manager->SendPFileDescriptorSetConstructor(fds[0]);
-    for (uint32_t i = 1; i < fds.Length(); ++i) {
-      Unused << fdSet->SendAddFileDescriptor(fds[i]);
-    }
-  }
-
-  if (fdSet) {
-    aReadStreamOut.fds() = fdSet;
-  } else {
-    aReadStreamOut.fds() = void_t();
-  }
-}
-
-void
 ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
 {
   MOZ_ASSERT(aHeaders);
 
   AutoTArray<InternalHeaders::Entry, 16> entryList;
   aHeaders->GetEntries(entryList);
 
   for (uint32_t i = 0; i < entryList.Length(); ++i) {
@@ -140,16 +116,17 @@ TypeUtils::ToInternalRequest(const Ownin
   }
 
   return ToInternalRequest(aIn.GetAsUSVString(), aRv);
 }
 
 void
 TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
                           BodyAction aBodyAction, SchemeAction aSchemeAction,
+                          nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                           ErrorResult& aRv)
 {
   MOZ_ASSERT(aIn);
 
   aIn->GetMethod(aOut.method());
 
   nsAutoCString url;
   aIn->GetURL(url);
@@ -186,17 +163,17 @@ TypeUtils::ToCacheRequest(CacheRequest& 
     aOut.body() = void_t();
     return;
   }
 
   // BodyUsed flag is checked and set previously in ToInternalRequest()
 
   nsCOMPtr<nsIInputStream> stream;
   aIn->GetBody(getter_AddRefs(stream));
-  SerializeCacheStream(stream, &aOut.body(), aRv);
+  SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
 void
 TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
                                       InternalResponse& aIn, ErrorResult& aRv)
@@ -228,17 +205,19 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
   if (aIn.GetPrincipalInfo()) {
     aOut.principalInfo() = *aIn.GetPrincipalInfo();
   } else {
     aOut.principalInfo() = void_t();
   }
 }
 
 void
-TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv)
+TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
+                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
+                           ErrorResult& aRv)
 {
   if (aIn.BodyUsed()) {
     aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
     return;
   }
 
   RefPtr<InternalResponse> ir = aIn.GetInternalResponse();
   ToCacheResponseWithoutBody(aOut, *ir, aRv);
@@ -247,17 +226,17 @@ TypeUtils::ToCacheResponse(CacheResponse
   }
 
   nsCOMPtr<nsIInputStream> stream;
   ir->GetUnfilteredBody(getter_AddRefs(stream));
   if (stream) {
     aIn.SetBodyUsed();
   }
 
-  SerializeCacheStream(stream, &aOut.body(), aRv);
+  SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
 // static
 void
 TypeUtils::ToCacheQueryParams(CacheQueryParams& aOut,
@@ -495,75 +474,37 @@ TypeUtils::ToInternalRequest(const nsASt
   if (NS_WARN_IF(aRv.Failed())) { return nullptr; }
 
   return request->GetInternalRequest();
 }
 
 void
 TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
                                 CacheReadStreamOrVoid* aStreamOut,
+                                nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                                 ErrorResult& aRv)
 {
   *aStreamOut = void_t();
   if (!aStream) {
     return;
   }
 
-  // Option 1: Send a cache-specific ReadStream if we can.
   RefPtr<ReadStream> controlled = do_QueryObject(aStream);
   if (controlled) {
-    controlled->Serialize(aStreamOut, aRv);
+    controlled->Serialize(aStreamOut, aStreamCleanupList, aRv);
     return;
   }
 
-  CacheReadStream readStream;
-  readStream.controlChild() = nullptr;
-  readStream.controlParent() = nullptr;
-  readStream.pushStreamChild() = nullptr;
-  readStream.pushStreamParent() = nullptr;
-
-  // Option 2: Do normal stream serialization if its supported.
-  nsCOMPtr<nsIIPCSerializableInputStream> serial = do_QueryInterface(aStream);
-  if (serial) {
-    SerializeNormalStream(aStream, readStream);
-
-  // Option 3: As a last resort push data across manually.  Should only be
-  //           needed for nsPipe input stream.  Only works for async,
-  //           non-blocking streams.
-  } else {
-    SerializePushStream(aStream, readStream, aRv);
-    if (NS_WARN_IF(aRv.Failed())) { return; }
-  }
-
-  *aStreamOut = readStream;
-}
+  *aStreamOut = CacheReadStream();
+  CacheReadStream& cacheStream = aStreamOut->get_CacheReadStream();
 
-void
-TypeUtils::SerializePushStream(nsIInputStream* aStream,
-                               CacheReadStream& aReadStreamOut,
-                               ErrorResult& aRv)
-{
-  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
-  if (NS_WARN_IF(!asyncStream)) {
-    aRv = NS_ERROR_FAILURE;
-    return;
-  }
+  cacheStream.controlChild() = nullptr;
+  cacheStream.controlParent() = nullptr;
 
-  bool nonBlocking = false;
-  aRv = asyncStream->IsNonBlocking(&nonBlocking);
-  if (NS_WARN_IF(aRv.Failed())) { return; }
-  if (NS_WARN_IF(!nonBlocking)) {
-    aRv = NS_ERROR_FAILURE;
-    return;
-  }
+  UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(cacheStream.stream()));
+  autoStream->Serialize(aStream, GetIPCManager());
 
-  aReadStreamOut.pushStreamChild() = CreatePushStream(asyncStream);
-  MOZ_ASSERT(aReadStreamOut.pushStreamChild());
-  aReadStreamOut.params() = void_t();
-  aReadStreamOut.fds() = void_t();
-
-  // CachePushStreamChild::Start() must be called after sending the stream
-  // across to the parent side.
+  aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/TypeUtils.h
+++ b/dom/cache/TypeUtils.h
@@ -12,29 +12,35 @@
 #include "mozilla/dom/InternalHeaders.h"
 #include "nsError.h"
 
 class nsIGlobalObject;
 class nsIAsyncInputStream;
 class nsIInputStream;
 
 namespace mozilla {
+
+namespace ipc {
+class PBackgroundChild;
+class SendStreamChild;
+class AutoIPCStream;
+}
+
 namespace dom {
 
 struct CacheQueryOptions;
 class InternalRequest;
 class InternalResponse;
 class OwningRequestOrUSVString;
 class Request;
 class RequestOrUSVString;
 class Response;
 
 namespace cache {
 
-class CachePushStreamChild;
 class CacheQueryParams;
 class CacheReadStream;
 class CacheReadStreamOrVoid;
 class CacheRequest;
 class CacheResponse;
 class HeadersEntry;
 
 class TypeUtils
@@ -55,38 +61,45 @@ public:
   ~TypeUtils() { }
   virtual nsIGlobalObject* GetGlobalObject() const = 0;
 #ifdef DEBUG
   virtual void AssertOwningThread() const = 0;
 #else
   inline void AssertOwningThread() const { }
 #endif
 
-  virtual CachePushStreamChild*
-  CreatePushStream(nsIAsyncInputStream* aStream) = 0;
+  // This is mainly declared to support serializing body streams.  Some
+  // TypeUtils implementations do not expect to be used for this kind of
+  // serialization.  These classes will MOZ_CRASH() if you try to call
+  // GetIPCManager().
+  virtual mozilla::ipc::PBackgroundChild*
+  GetIPCManager() = 0;
 
   already_AddRefed<InternalRequest>
   ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction,
                     ErrorResult& aRv);
 
   already_AddRefed<InternalRequest>
   ToInternalRequest(const OwningRequestOrUSVString& aIn, BodyAction aBodyAction,
                     ErrorResult& aRv);
 
   void
   ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
                  BodyAction aBodyAction, SchemeAction aSchemeAction,
+                 nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
                  ErrorResult& aRv);
 
   void
   ToCacheResponseWithoutBody(CacheResponse& aOut, InternalResponse& aIn,
                              ErrorResult& aRv);
 
   void
-  ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv);
+  ToCacheResponse(CacheResponse& aOut, Response& aIn,
+                  nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
+                  ErrorResult& aRv);
 
   void
   ToCacheQueryParams(CacheQueryParams& aOut, const CacheQueryOptions& aIn);
 
   already_AddRefed<Response>
   ToResponse(const CacheResponse& aIn);
 
   already_AddRefed<InternalRequest>
@@ -124,20 +137,21 @@ private:
   CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
                       ErrorResult& aRv);
 
   already_AddRefed<InternalRequest>
   ToInternalRequest(const nsAString& aIn, ErrorResult& aRv);
 
   void
   SerializeCacheStream(nsIInputStream* aStream, CacheReadStreamOrVoid* aStreamOut,
+                       nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
                        ErrorResult& aRv);
 
   void
-  SerializePushStream(nsIInputStream* aStream, CacheReadStream& aReadStreamOut,
+  SerializeSendStream(nsIInputStream* aStream, CacheReadStream& aReadStreamOut,
                       ErrorResult& aRv);
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_TypesUtils_h
--- a/dom/cache/moz.build
+++ b/dom/cache/moz.build
@@ -9,18 +9,16 @@ EXPORTS.mozilla.dom.cache += [
     'ActorChild.h',
     'ActorUtils.h',
     'AutoUtils.h',
     'Cache.h',
     'CacheChild.h',
     'CacheOpChild.h',
     'CacheOpParent.h',
     'CacheParent.h',
-    'CachePushStreamChild.h',
-    'CachePushStreamParent.h',
     'CacheStorage.h',
     'CacheStorageChild.h',
     'CacheStorageParent.h',
     'CacheStreamControlChild.h',
     'CacheStreamControlParent.h',
     'Connection.h',
     'Context.h',
     'DBAction.h',
@@ -44,18 +42,16 @@ UNIFIED_SOURCES += [
     'Action.cpp',
     'ActorChild.cpp',
     'AutoUtils.cpp',
     'Cache.cpp',
     'CacheChild.cpp',
     'CacheOpChild.cpp',
     'CacheOpParent.cpp',
     'CacheParent.cpp',
-    'CachePushStreamChild.cpp',
-    'CachePushStreamParent.cpp',
     'CacheStorage.cpp',
     'CacheStorageChild.cpp',
     'CacheStorageParent.cpp',
     'CacheStreamControlChild.cpp',
     'CacheStreamControlParent.cpp',
     'Connection.cpp',
     'Context.cpp',
     'DBAction.cpp',
@@ -71,17 +67,16 @@ UNIFIED_SOURCES += [
     'StreamList.cpp',
     'TypeUtils.cpp',
 ]
 
 IPDL_SOURCES += [
     'CacheTypes.ipdlh',
     'PCache.ipdl',
     'PCacheOp.ipdl',
-    'PCachePushStream.ipdl',
     'PCacheStorage.ipdl',
     'PCacheStreamControl.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '../workers',