Bug 1110485 P1 Refactor Cache IPC requests to use a separate actor. r=baku
authorBen Kelly <ben@wanderview.com>
Thu, 16 Apr 2015 12:00:15 -0700
changeset 239643 94bc6eb2b5d4e793be6a9638aecb80cfe812a497
parent 239642 913ce28d019658d56aba583f8de24fbff7e636f6
child 239644 a8c670dd748801342ce44d7cc4ad6a6a8a142489
push id12444
push userryanvm@gmail.com
push dateFri, 17 Apr 2015 20:04:42 +0000
treeherderfx-team@560a202db924 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1110485
milestone40.0a1
Bug 1110485 P1 Refactor Cache IPC requests to use a separate actor. r=baku
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/CacheInitData.ipdlh
dom/cache/CacheOpChild.cpp
dom/cache/CacheOpChild.h
dom/cache/CacheOpParent.cpp
dom/cache/CacheOpParent.h
dom/cache/CacheParent.cpp
dom/cache/CacheParent.h
dom/cache/CacheStorage.cpp
dom/cache/CacheStorage.h
dom/cache/CacheStorageChild.cpp
dom/cache/CacheStorageChild.h
dom/cache/CacheStorageParent.cpp
dom/cache/CacheStorageParent.h
dom/cache/DBAction.cpp
dom/cache/DBAction.h
dom/cache/FetchPut.cpp
dom/cache/FetchPut.h
dom/cache/Manager.cpp
dom/cache/Manager.h
dom/cache/PCache.ipdl
dom/cache/PCacheOp.ipdl
dom/cache/PCacheStorage.ipdl
dom/cache/PCacheTypes.ipdlh
dom/cache/PrincipalVerifier.cpp
dom/cache/PrincipalVerifier.h
dom/cache/Types.h
dom/cache/moz.build
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -2,16 +2,17 @@
 /* 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/AutoUtils.h"
 
 #include "mozilla/unused.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"
@@ -131,181 +132,391 @@ CleanupParentFds(PCacheReadStreamOrVoid&
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::PBackgroundParent;
 
-AutoChildBase::AutoChildBase(TypeUtils* aTypeUtils)
+// --------------------------------------------
+
+AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
+                                 const CacheOpArgs& aOpArgs)
   : mTypeUtils(aTypeUtils)
+  , mOpArgs(aOpArgs)
   , mSent(false)
 {
   MOZ_ASSERT(mTypeUtils);
 }
 
-AutoChildBase::~AutoChildBase()
-{
-}
-
-// --------------------------------------------
-
-AutoChildRequest::AutoChildRequest(TypeUtils* aTypeUtils)
-  : AutoChildBase(aTypeUtils)
-{
-  mRequestOrVoid = void_t();
-}
-
-AutoChildRequest::~AutoChildRequest()
-{
-  if (mRequestOrVoid.type() != PCacheRequestOrVoid::TPCacheRequest) {
-    return;
-  }
-
-  CleanupAction action = mSent ? Forget : Delete;
-  CleanupChild(mRequestOrVoid.get_PCacheRequest().body(), action);
-}
-
-void
-AutoChildRequest::Add(InternalRequest* aRequest, BodyAction aBodyAction,
-                      ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
-                      ErrorResult& aRv)
-{
-  MOZ_ASSERT(!mSent);
-  MOZ_ASSERT(mRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t);
-  mRequestOrVoid = PCacheRequest();
-  mTypeUtils->ToPCacheRequest(mRequestOrVoid.get_PCacheRequest(), aRequest,
-                              aBodyAction, aReferrerAction, aSchemeAction, aRv);
-}
-
-const PCacheRequest&
-AutoChildRequest::SendAsRequest()
-{
-  MOZ_ASSERT(mRequestOrVoid.type() == PCacheRequestOrVoid::TPCacheRequest);
-  return mRequestOrVoid.get_PCacheRequest();
-}
-
-const PCacheRequestOrVoid&
-AutoChildRequest::SendAsRequestOrVoid()
-{
-  return mRequestOrVoid;
-}
-
-// --------------------------------------------
-
-AutoChildRequestList::AutoChildRequestList(TypeUtils* aTypeUtils,
-                                           uint32_t aCapacity)
-  : AutoChildBase(aTypeUtils)
-{
-  mRequestList.SetCapacity(aCapacity);
-}
-
-AutoChildRequestList::~AutoChildRequestList()
+AutoChildOpArgs::~AutoChildOpArgs()
 {
   CleanupAction action = mSent ? Forget : Delete;
-  for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
-    CleanupChild(mRequestList[i].body(), action);
+
+  switch(mOpArgs.type()) {
+    case CacheOpArgs::TCacheMatchArgs:
+    {
+      CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
+      CleanupChild(args.request().body(), action);
+      break;
+    }
+    case CacheOpArgs::TCacheMatchAllArgs:
+    {
+      CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
+      if (args.requestOrVoid().type() == PCacheRequestOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupChild(args.requestOrVoid().get_PCacheRequest().body(), action);
+      break;
+    }
+    case CacheOpArgs::TCacheAddAllArgs:
+    {
+      CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
+      auto& list = args.requestList();
+      for (uint32_t i = 0; i < list.Length(); ++i) {
+        CleanupChild(list[i].body(), action);
+      }
+      break;
+    }
+    case CacheOpArgs::TCachePutAllArgs:
+    {
+      CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
+      auto& list = args.requestResponseList();
+      for (uint32_t i = 0; i < list.Length(); ++i) {
+        CleanupChild(list[i].request().body(), action);
+        CleanupChild(list[i].response().body(), action);
+      }
+      break;
+    }
+    case CacheOpArgs::TCacheDeleteArgs:
+    {
+      CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
+      CleanupChild(args.request().body(), action);
+      break;
+    }
+    case CacheOpArgs::TCacheKeysArgs:
+    {
+      CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
+      if (args.requestOrVoid().type() == PCacheRequestOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupChild(args.requestOrVoid().get_PCacheRequest().body(), action);
+      break;
+    }
+    case CacheOpArgs::TStorageMatchArgs:
+    {
+      StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
+      CleanupChild(args.request().body(), action);
+      break;
+    }
+    default:
+      // Other types do not need cleanup
+      break;
   }
 }
 
 void
-AutoChildRequestList::Add(InternalRequest* aRequest, BodyAction aBodyAction,
-                          ReferrerAction aReferrerAction,
-                          SchemeAction aSchemeAction, ErrorResult& aRv)
+AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
+                     ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
+                     ErrorResult& aRv)
 {
   MOZ_ASSERT(!mSent);
 
-  // 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.
+  switch(mOpArgs.type()) {
+    case CacheOpArgs::TCacheMatchArgs:
+    {
+      CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
+      mTypeUtils->ToPCacheRequest(args.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TCacheMatchAllArgs:
+    {
+      CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
+      MOZ_ASSERT(args.requestOrVoid().type() == PCacheRequestOrVoid::Tvoid_t);
+      args.requestOrVoid() = PCacheRequest();
+      mTypeUtils->ToPCacheRequest(args.requestOrVoid().get_PCacheRequest(),
+                                  aRequest, aBodyAction, aReferrerAction,
+                                  aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TCacheAddAllArgs:
+    {
+      CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
+
+      // 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.
+      PCacheRequest& request = *args.requestList().AppendElement();
 
-  PCacheRequest* request = mRequestList.AppendElement();
-  mTypeUtils->ToPCacheRequest(*request, aRequest, aBodyAction, aReferrerAction,
-                              aSchemeAction, aRv);
-  if (aRv.Failed()) {
-    mRequestList.RemoveElementAt(mRequestList.Length() - 1);
+      mTypeUtils->ToPCacheRequest(request, aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      if (aRv.Failed()) {
+        args.requestList().RemoveElementAt(args.requestList().Length() - 1);
+      }
+      break;
+    }
+    case CacheOpArgs::TCacheDeleteArgs:
+    {
+      CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
+      mTypeUtils->ToPCacheRequest(args.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TCacheKeysArgs:
+    {
+      CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
+      MOZ_ASSERT(args.requestOrVoid().type() == PCacheRequestOrVoid::Tvoid_t);
+      args.requestOrVoid() = PCacheRequest();
+      mTypeUtils->ToPCacheRequest(args.requestOrVoid().get_PCacheRequest(),
+                                  aRequest, aBodyAction, aReferrerAction,
+                                  aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TStorageMatchArgs:
+    {
+      StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
+      mTypeUtils->ToPCacheRequest(args.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      break;
+    }
+    default:
+      MOZ_CRASH("Cache args type cannot send a Request!");
   }
 }
 
-const nsTArray<PCacheRequest>&
-AutoChildRequestList::SendAsRequestList()
+void
+AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
+                     ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
+                     Response& aResponse, ErrorResult& aRv)
+{
+  MOZ_ASSERT(!mSent);
+
+  switch(mOpArgs.type()) {
+    case CacheOpArgs::TCachePutAllArgs:
+    {
+      CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
+
+      // 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->ToPCacheRequest(pair.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      if (!aRv.Failed()) {
+        mTypeUtils->ToPCacheResponse(pair.response(), aResponse, aRv);
+      }
+
+      if (aRv.Failed()) {
+        CleanupChild(pair.request().body(), Delete);
+        args.requestResponseList().RemoveElementAt(
+          args.requestResponseList().Length() - 1);
+      }
+
+      break;
+    }
+    default:
+      MOZ_CRASH("Cache args type cannot send a Request/Response pair!");
+  }
+}
+
+const CacheOpArgs&
+AutoChildOpArgs::SendAsOpArgs()
 {
   MOZ_ASSERT(!mSent);
   mSent = true;
-  return mRequestList;
+  return mOpArgs;
 }
 
 // --------------------------------------------
 
-AutoChildRequestResponse::AutoChildRequestResponse(TypeUtils* aTypeUtils)
-  : AutoChildBase(aTypeUtils)
-{
-  // Default IPC-generated constructor does not initialize these correctly
-  // and we check them later when cleaning up.
-  mRequestResponse.request().body() = void_t();
-  mRequestResponse.response().body() = void_t();
-}
-
-AutoChildRequestResponse::~AutoChildRequestResponse()
-{
-  CleanupAction action = mSent ? Forget : Delete;
-  CleanupChild(mRequestResponse.request().body(), action);
-  CleanupChild(mRequestResponse.response().body(), action);
-}
-
-void
-AutoChildRequestResponse::Add(InternalRequest* aRequest, BodyAction aBodyAction,
-                              ReferrerAction aReferrerAction,
-                              SchemeAction aSchemeAction, ErrorResult& aRv)
-{
-  MOZ_ASSERT(!mSent);
-  mTypeUtils->ToPCacheRequest(mRequestResponse.request(), aRequest, aBodyAction,
-                              aReferrerAction, aSchemeAction, aRv);
-}
-
-void
-AutoChildRequestResponse::Add(Response& aResponse, ErrorResult& aRv)
-{
-  MOZ_ASSERT(!mSent);
-  mTypeUtils->ToPCacheResponse(mRequestResponse.response(), aResponse, aRv);
-}
-
-const CacheRequestResponse&
-AutoChildRequestResponse::SendAsRequestResponse()
-{
-  MOZ_ASSERT(!mSent);
-  mSent = true;
-  return mRequestResponse;
-}
-
-// --------------------------------------------
-
-AutoParentBase::AutoParentBase(PBackgroundParent* aManager)
+AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
+                                       const CacheOpResult& aOpResult)
   : mManager(aManager)
+  , mOpResult(aOpResult)
   , mStreamControl(nullptr)
   , mSent(false)
 {
   MOZ_ASSERT(mManager);
 }
 
-AutoParentBase::~AutoParentBase()
+AutoParentOpResult::~AutoParentOpResult()
 {
-  if (!mSent && mStreamControl) {
+  CleanupAction action = mSent ? Forget : Delete;
+
+  switch (mOpResult.type()) {
+    case CacheOpResult::TCacheMatchResult:
+    {
+      CacheMatchResult& result = mOpResult.get_CacheMatchResult();
+      if (result.responseOrVoid().type() == PCacheResponseOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupParentFds(result.responseOrVoid().get_PCacheResponse().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() == PCacheResponseOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupParentFds(result.responseOrVoid().get_PCacheResponse().body(),
+                       action);
+      break;
+    }
+    case CacheOpResult::TStorageOpenResult:
+    {
+      StorageOpenResult& result = mOpResult.get_StorageOpenResult();
+      if (action == Forget || result.actorParent() == nullptr) {
+        break;
+      }
+      unused << PCacheParent::Send__delete__(result.actorParent());
+    }
+    default:
+      // other types do not need clean up
+      break;
+  }
+
+  if (action == Delete && mStreamControl) {
     unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
   }
 }
 
 void
-AutoParentBase::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
-                                    PCacheReadStream* aReadStreamOut)
+AutoParentOpResult::Add(CacheId aOpenedCacheId, Manager* aManager)
+{
+  MOZ_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
+  MOZ_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
+  mOpResult.get_StorageOpenResult().actorParent() =
+    mManager->SendPCacheConstructor(new CacheParent(aManager, aOpenedCacheId));
+}
+
+void
+AutoParentOpResult::Add(const SavedResponse& aSavedResponse,
+                        StreamList* aStreamList)
+{
+  MOZ_ASSERT(!mSent);
+
+  switch (mOpResult.type()) {
+    case CacheOpResult::TCacheMatchResult:
+    {
+      CacheMatchResult& result = mOpResult.get_CacheMatchResult();
+      MOZ_ASSERT(result.responseOrVoid().type() == PCacheResponseOrVoid::Tvoid_t);
+      result.responseOrVoid() = aSavedResponse.mValue;
+      SerializeResponseBody(aSavedResponse, aStreamList,
+                            &result.responseOrVoid().get_PCacheResponse());
+      break;
+    }
+    case CacheOpResult::TCacheMatchAllResult:
+    {
+      CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
+      result.responseList().AppendElement(aSavedResponse.mValue);
+      SerializeResponseBody(aSavedResponse, aStreamList,
+                            &result.responseList().LastElement());
+      break;
+    }
+    case CacheOpResult::TStorageMatchResult:
+    {
+      StorageMatchResult& result = mOpResult.get_StorageMatchResult();
+      MOZ_ASSERT(result.responseOrVoid().type() == PCacheResponseOrVoid::Tvoid_t);
+      result.responseOrVoid() = aSavedResponse.mValue;
+      SerializeResponseBody(aSavedResponse, aStreamList,
+                            &result.responseOrVoid().get_PCacheResponse());
+      break;
+    }
+    default:
+      MOZ_CRASH("Cache result type cannot handle returning a Response!");
+  }
+}
+
+void
+AutoParentOpResult::Add(const SavedRequest& aSavedRequest,
+                        StreamList* aStreamList)
+{
+  MOZ_ASSERT(!mSent);
+
+  switch (mOpResult.type()) {
+    case CacheOpResult::TCacheKeysResult:
+    {
+      CacheKeysResult& result = mOpResult.get_CacheKeysResult();
+      result.requestList().AppendElement(aSavedRequest.mValue);
+      PCacheRequest& request = result.requestList().LastElement();
+
+      if (!aSavedRequest.mHasBodyId) {
+        request.body() = void_t();
+        break;
+      }
+
+      request.body() = PCacheReadStream();
+      SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
+                          &request.body().get_PCacheReadStream());
+      break;
+    }
+    default:
+      MOZ_CRASH("Cache result type cannot handle returning a Request!");
+  }
+}
+
+const CacheOpResult&
+AutoParentOpResult::SendAsOpResult()
+{
+  MOZ_ASSERT(!mSent);
+  mSent = true;
+  return mOpResult;
+}
+
+void
+AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
+                                          StreamList* aStreamList,
+                                          PCacheResponse* aResponseOut)
+{
+  MOZ_ASSERT(aResponseOut);
+
+  if (!aSavedResponse.mHasBodyId) {
+    aResponseOut->body() = void_t();
+    return;
+  }
+
+  aResponseOut->body() = PCacheReadStream();
+  SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
+                      &aResponseOut->body().get_PCacheReadStream());
+}
+
+void
+AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
+                                        PCacheReadStream* aReadStreamOut)
 {
   MOZ_ASSERT(aStreamList);
   MOZ_ASSERT(aReadStreamOut);
   MOZ_ASSERT(!mSent);
 
   nsCOMPtr<nsIInputStream> stream = aStreamList->Extract(aId);
   MOZ_ASSERT(stream);
 
@@ -323,144 +534,11 @@ AutoParentBase::SerializeReadStream(cons
 
   aStreamList->SetStreamControl(mStreamControl);
 
   nsRefPtr<ReadStream> readStream = ReadStream::Create(mStreamControl,
                                                        aId, stream);
   readStream->Serialize(aReadStreamOut);
 }
 
-// --------------------------------------------
-
-AutoParentRequestList::AutoParentRequestList(PBackgroundParent* aManager,
-                                             uint32_t aCapacity)
-  : AutoParentBase(aManager)
-{
-  mRequestList.SetCapacity(aCapacity);
-}
-
-AutoParentRequestList::~AutoParentRequestList()
-{
-  CleanupAction action = mSent ? Forget : Delete;
-  for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
-    CleanupParentFds(mRequestList[i].body(), action);
-  }
-}
-
-void
-AutoParentRequestList::Add(const SavedRequest& aSavedRequest,
-                           StreamList* aStreamList)
-{
-  MOZ_ASSERT(!mSent);
-
-  mRequestList.AppendElement(aSavedRequest.mValue);
-  PCacheRequest& request = mRequestList.LastElement();
-
-  if (!aSavedRequest.mHasBodyId) {
-    request.body() = void_t();
-    return;
-  }
-
-  request.body() = PCacheReadStream();
-  SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
-                      &request.body().get_PCacheReadStream());
-}
-
-const nsTArray<PCacheRequest>&
-AutoParentRequestList::SendAsRequestList()
-{
-  MOZ_ASSERT(!mSent);
-  mSent = true;
-  return mRequestList;
-}
-
-// --------------------------------------------
-
-AutoParentResponseList::AutoParentResponseList(PBackgroundParent* aManager,
-                                               uint32_t aCapacity)
-  : AutoParentBase(aManager)
-{
-  mResponseList.SetCapacity(aCapacity);
-}
-
-AutoParentResponseList::~AutoParentResponseList()
-{
-  CleanupAction action = mSent ? Forget : Delete;
-  for (uint32_t i = 0; i < mResponseList.Length(); ++i) {
-    CleanupParentFds(mResponseList[i].body(), action);
-  }
-}
-
-void
-AutoParentResponseList::Add(const SavedResponse& aSavedResponse,
-                            StreamList* aStreamList)
-{
-  MOZ_ASSERT(!mSent);
-
-  mResponseList.AppendElement(aSavedResponse.mValue);
-  PCacheResponse& response = mResponseList.LastElement();
-
-  if (!aSavedResponse.mHasBodyId) {
-    response.body() = void_t();
-    return;
-  }
-
-  response.body() = PCacheReadStream();
-  SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
-                      &response.body().get_PCacheReadStream());
-}
-
-const nsTArray<PCacheResponse>&
-AutoParentResponseList::SendAsResponseList()
-{
-  MOZ_ASSERT(!mSent);
-  mSent = true;
-  return mResponseList;
-}
-
-// --------------------------------------------
-
-AutoParentResponseOrVoid::AutoParentResponseOrVoid(ipc::PBackgroundParent* aManager)
-  : AutoParentBase(aManager)
-{
-  mResponseOrVoid = void_t();
-}
-
-AutoParentResponseOrVoid::~AutoParentResponseOrVoid()
-{
-  if (mResponseOrVoid.type() != PCacheResponseOrVoid::TPCacheResponse) {
-    return;
-  }
-
-  CleanupAction action = mSent ? Forget : Delete;
-  CleanupParentFds(mResponseOrVoid.get_PCacheResponse().body(), action);
-}
-
-void
-AutoParentResponseOrVoid::Add(const SavedResponse& aSavedResponse,
-                              StreamList* aStreamList)
-{
-  MOZ_ASSERT(!mSent);
-
-  mResponseOrVoid = aSavedResponse.mValue;
-  PCacheResponse& response = mResponseOrVoid.get_PCacheResponse();
-
-  if (!aSavedResponse.mHasBodyId) {
-    response.body() = void_t();
-    return;
-  }
-
-  response.body() = PCacheReadStream();
-  SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
-                      &response.body().get_PCacheReadStream());
-}
-
-const PCacheResponseOrVoid&
-AutoParentResponseOrVoid::SendAsResponseOrVoid()
-{
-  MOZ_ASSERT(!mSent);
-  mSent = true;
-  return mResponseOrVoid;
-}
-
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/AutoUtils.h
+++ b/dom/cache/AutoUtils.h
@@ -4,16 +4,17 @@
  * 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_AutoUtils_h
 #define mozilla_dom_cache_AutoUtils_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "nsTArray.h"
 
 struct nsID;
 
 namespace mozilla {
 
 class ErrorResult;
@@ -24,150 +25,78 @@ class PBackgroundParent;
 
 namespace dom {
 
 class InternalRequest;
 
 namespace cache {
 
 class CacheStreamControlParent;
+class Manager;
 struct SavedRequest;
 struct SavedResponse;
 class StreamList;
 
 // A collection of RAII-style helper classes to ensure that IPC
 // FileDescriptorSet actors are properly cleaned up.  The user of these actors
 // must manually either Forget() the Fds or Send__delete__() the actor
 // depending on if the descriptors were actually sent.
 //
 // Note, these should only be used when *sending* streams across IPC.  The
 // deserialization case is handled by creating a ReadStream object.
 
-class MOZ_STACK_CLASS AutoChildBase
+class MOZ_STACK_CLASS AutoChildOpArgs final
 {
-protected:
+public:
   typedef TypeUtils::BodyAction BodyAction;
   typedef TypeUtils::ReferrerAction ReferrerAction;
   typedef TypeUtils::SchemeAction SchemeAction;
 
-  AutoChildBase(TypeUtils* aTypeUtils);
-  virtual ~AutoChildBase() = 0;
-
-  TypeUtils* mTypeUtils;
-  bool mSent;
-};
-
-class MOZ_STACK_CLASS AutoChildRequest final : public AutoChildBase
-{
-public:
-  explicit AutoChildRequest(TypeUtils* aTypeUtils);
-  ~AutoChildRequest();
+  AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs);
+  ~AutoChildOpArgs();
 
   void Add(InternalRequest* aRequest, BodyAction aBodyAction,
            ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
            ErrorResult& aRv);
-
-  const PCacheRequest& SendAsRequest();
-  const PCacheRequestOrVoid& SendAsRequestOrVoid();
-
-private:
-  PCacheRequestOrVoid mRequestOrVoid;
-};
-
-class MOZ_STACK_CLASS AutoChildRequestList final : public AutoChildBase
-{
-public:
-  AutoChildRequestList(TypeUtils* aTypeUtils, uint32_t aCapacity);
-  ~AutoChildRequestList();
-
   void Add(InternalRequest* aRequest, BodyAction aBodyAction,
            ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
-           ErrorResult& aRv);
+           Response& aResponse, ErrorResult& aRv);
 
-  const nsTArray<PCacheRequest>& SendAsRequestList();
+  const CacheOpArgs& SendAsOpArgs();
 
 private:
-  // Allocates ~5k inline in the stack-only class
-  nsAutoTArray<PCacheRequest, 32> mRequestList;
+  TypeUtils* mTypeUtils;
+  CacheOpArgs mOpArgs;
+  bool mSent;
 };
 
-class MOZ_STACK_CLASS AutoChildRequestResponse final : public AutoChildBase
+class MOZ_STACK_CLASS AutoParentOpResult final
 {
 public:
-  explicit AutoChildRequestResponse(TypeUtils* aTypeUtils);
-  ~AutoChildRequestResponse();
+  AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
+                     const CacheOpResult& aOpResult);
+  ~AutoParentOpResult();
 
-  void Add(InternalRequest* aRequest, BodyAction aBodyAction,
-           ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
-           ErrorResult& aRv);
-  void Add(Response& aResponse, ErrorResult& aRv);
+  void Add(CacheId aOpenedCacheId, Manager* aManager);
+  void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
+  void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
 
-  const CacheRequestResponse& SendAsRequestResponse();
+  const CacheOpResult& SendAsOpResult();
 
 private:
-  CacheRequestResponse mRequestResponse;
-};
-
-class MOZ_STACK_CLASS AutoParentBase
-{
-protected:
-  explicit AutoParentBase(mozilla::ipc::PBackgroundParent* aManager);
-  virtual ~AutoParentBase() = 0;
+  void SerializeResponseBody(const SavedResponse& aSavedResponse,
+                             StreamList* aStreamList,
+                             PCacheResponse* aResponseOut);
 
   void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
                            PCacheReadStream* aReadStreamOut);
 
   mozilla::ipc::PBackgroundParent* mManager;
+  CacheOpResult mOpResult;
   CacheStreamControlParent* mStreamControl;
   bool mSent;
 };
 
-class MOZ_STACK_CLASS AutoParentRequestList final : public AutoParentBase
-{
-public:
-  AutoParentRequestList(mozilla::ipc::PBackgroundParent* aManager,
-                        uint32_t aCapacity);
-  ~AutoParentRequestList();
-
-  void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
-
-  const nsTArray<PCacheRequest>& SendAsRequestList();
-
-private:
-  // Allocates ~5k inline in the stack-only class
-  nsAutoTArray<PCacheRequest, 32> mRequestList;
-};
-
-class MOZ_STACK_CLASS AutoParentResponseList final : public AutoParentBase
-{
-public:
-  AutoParentResponseList(mozilla::ipc::PBackgroundParent* aManager,
-                         uint32_t aCapacity);
-  ~AutoParentResponseList();
-
-  void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
-
-  const nsTArray<PCacheResponse>& SendAsResponseList();
-
-private:
-  // Allocates ~4k inline in the stack-only class
-  nsAutoTArray<PCacheResponse, 32> mResponseList;
-};
-
-class MOZ_STACK_CLASS AutoParentResponseOrVoid final : public AutoParentBase
-{
-public:
-  explicit AutoParentResponseOrVoid(mozilla::ipc::PBackgroundParent* aManager);
-  ~AutoParentResponseOrVoid();
-
-  void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
-
-  const PCacheResponseOrVoid& SendAsResponseOrVoid();
-
-private:
-  PCacheResponseOrVoid mResponseOrVoid;
-};
-
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_AutoUtils_h
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -9,19 +9,19 @@
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/Promise.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/CacheOpChild.h"
 #include "mozilla/dom/cache/CachePushStreamChild.h"
 #include "mozilla/dom/cache/ReadStream.h"
-#include "mozilla/dom/cache/TypeUtils.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsIGlobalObject.h"
 #include "nsNetUtil.h"
 
 namespace {
 
@@ -74,27 +74,17 @@ namespace cache {
 
 using mozilla::ErrorResult;
 using mozilla::unused;
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::WorkerPrivate;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::Cache);
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache);
-NS_IMPL_CYCLE_COLLECTION_CLASS(mozilla::dom::cache::Cache)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::Cache)
-  tmp->DisconnectFromActor();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::Cache)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::Cache)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::Cache, mGlobal);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
   : mGlobal(aGlobal)
@@ -106,260 +96,199 @@ Cache::Cache(nsIGlobalObject* aGlobal, C
 }
 
 already_AddRefed<Promise>
 Cache::Match(const RequestOrUSVString& aRequest,
              const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  AutoChildRequest request(this);
-
-  request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   PCacheQueryParams params;
   ToPCacheQueryParams(params, aOptions);
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
+  AutoChildOpArgs args(this, CacheMatchArgs(PCacheRequest(), params));
 
-  unused << mActor->SendMatch(requestId, request.SendAsRequest(), params);
+  args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
 
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
                 const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
+  PCacheQueryParams params;
+  ToPCacheQueryParams(params, aOptions);
 
-  AutoChildRequest request(this);
+  AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params));
 
   if (aRequest.WasPassed()) {
     nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
                                                      IgnoreBody, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
-    request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+    args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  PCacheQueryParams params;
-  ToPCacheQueryParams(params, aOptions);
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendMatchAll(requestId, request.SendAsRequestOrVoid(),
-                                 params);
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::Add(const RequestOrUSVString& aRequest, ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
   if (!IsValidPutRequestMethod(aRequest, aRv)) {
     return nullptr;
   }
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  AutoChildRequestList requests(this, 1);
-  requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv);
+  AutoChildOpArgs args(this, CacheAddAllArgs());
+
+  args.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendAddAll(requestId, requests.SendAsRequestList());
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
               ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   // If there is no work to do, then resolve immediately
   if (aRequests.IsEmpty()) {
+    nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
+    if (!promise) {
+      return nullptr;
+    }
+
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
 
-  AutoChildRequestList requests(this, aRequests.Length());
+  AutoChildOpArgs args(this, CacheAddAllArgs());
 
   for (uint32_t i = 0; i < aRequests.Length(); ++i) {
     if (!IsValidPutRequestMethod(aRequests[i], aRv)) {
       return nullptr;
     }
 
     nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequests[i], ReadBody,
                                                      aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
-    requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme,
-                 aRv);
+    args.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendAddAll(requestId, requests.SendAsRequestList());
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
            ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
   if (!IsValidPutRequestMethod(aRequest, aRv)) {
     return nullptr;
   }
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  AutoChildRequestResponse put(this);
-  put.Add(ir, ReadBody, PassThroughReferrer, TypeErrorOnInvalidScheme, aRv);
+  AutoChildOpArgs args(this, CachePutAllArgs());
+
+  args.Add(ir, ReadBody, PassThroughReferrer, TypeErrorOnInvalidScheme,
+           aResponse, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  put.Add(aResponse, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendPut(requestId, put.SendAsRequestResponse());
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::Delete(const RequestOrUSVString& aRequest,
               const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  AutoChildRequest request(this);
-  request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
   PCacheQueryParams params;
   ToPCacheQueryParams(params, aOptions);
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
+  AutoChildOpArgs args(this, CacheDeleteArgs(PCacheRequest(), params));
 
-  unused << mActor->SendDelete(requestId, request.SendAsRequest(), params);
+  args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
 
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 already_AddRefed<Promise>
 Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
             const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(mActor);
 
-  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
-  if (!promise) {
-    return nullptr;
-  }
+  PCacheQueryParams params;
+  ToPCacheQueryParams(params, aOptions);
 
-  AutoChildRequest request(this);
+  AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params));
 
   if (aRequest.WasPassed()) {
     nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
                                                      IgnoreBody, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
-    request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+    args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  PCacheQueryParams params;
-  ToPCacheQueryParams(params, aOptions);
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendKeys(requestId, request.SendAsRequestOrVoid(), params);
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 // static
 bool
 Cache::PrefEnabled(JSContext* aCx, JSObject* aObj)
 {
   using mozilla::dom::workers::WorkerPrivate;
   using mozilla::dom::workers::GetWorkerPrivateFromContext;
@@ -396,133 +325,16 @@ void
 Cache::DestroyInternal(CacheChild* aActor)
 {
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(mActor == aActor);
   mActor->ClearListener();
   mActor = nullptr;
 }
 
-void
-Cache::RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                         const PCacheResponseOrVoid& aResponse)
-{
-  // Convert the response immediately if its present.  This ensures that
-  // any stream actors are cleaned up, even if we error out below.
-  nsRefPtr<Response> response;
-  if (aResponse.type() == PCacheResponseOrVoid::TPCacheResponse) {
-    response = ToResponse(aResponse);
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  if (!response) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
-    return;
-  }
-
-  promise->MaybeResolve(response);
-}
-
-void
-Cache::RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
-                            const nsTArray<PCacheResponse>& aResponses)
-{
-  // Convert responses immediately.  This ensures that any stream actors are
-  // cleaned up, even if we error out below.
-  nsAutoTArray<nsRefPtr<Response>, 256> responses;
-  responses.SetCapacity(aResponses.Length());
-
-  for (uint32_t i = 0; i < aResponses.Length(); ++i) {
-    nsRefPtr<Response> response = ToResponse(aResponses[i]);
-    responses.AppendElement(response.forget());
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(responses);
-}
-
-void
-Cache::RecvAddAllResponse(RequestId aRequestId,
-                          const mozilla::ErrorResult& aError)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (aError.Failed()) {
-    // TODO: Remove this const_cast (bug 1152078).
-    // It is safe for now since this ErrorResult is handed off to us by IPDL
-    // and is thrown into the trash afterwards.
-    promise->MaybeReject(const_cast<ErrorResult&>(aError));
-    return;
-  }
-
-  promise->MaybeResolve(JS::UndefinedHandleValue);
-}
-
-void
-Cache::RecvPutResponse(RequestId aRequestId, nsresult aRv)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(JS::UndefinedHandleValue);
-}
-
-void
-Cache::RecvDeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(aSuccess);
-}
-
-void
-Cache::RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                        const nsTArray<PCacheRequest>& aRequests)
-{
-  // Convert requests immediately.  This ensures that any stream actors are
-  // cleaned up, even if we error out below.
-  nsAutoTArray<nsRefPtr<Request>, 256> requests;
-  requests.SetCapacity(aRequests.Length());
-
-  for (uint32_t i = 0; i < aRequests.Length(); ++i) {
-    nsRefPtr<Request> request = ToRequest(aRequests[i]);
-    requests.AppendElement(request.forget());
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(requests);
-}
-
 nsIGlobalObject*
 Cache::GetGlobalObject() const
 {
   return mGlobal;
 }
 
 #ifdef DEBUG
 void
@@ -539,82 +351,37 @@ Cache::CreatePushStream(nsIAsyncInputStr
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(aStream);
   auto actor = mActor->SendPCachePushStreamConstructor(
     new CachePushStreamChild(mActor->GetFeature(), aStream));
   MOZ_ASSERT(actor);
   return static_cast<CachePushStreamChild*>(actor);
 }
 
-void
-Cache::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
-{
-  // Do nothing.  The Promise will automatically drop the ref to us after
-  // calling the callback.  This is what we want as we only registered in order
-  // to be held alive via the Promise handle.
-}
-
-void
-Cache::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
-{
-  // Do nothing.  The Promise will automatically drop the ref to us after
-  // calling the callback.  This is what we want as we only registered in order
-  // to be held alive via the Promise handle.
-}
-
 Cache::~Cache()
 {
-  DisconnectFromActor();
-}
-
-void
-Cache::DisconnectFromActor()
-{
+  NS_ASSERT_OWNINGTHREAD(Cache);
   if (mActor) {
     mActor->StartDestroy();
     // DestroyInternal() is called synchronously by StartDestroy().  So we
     // should have already cleared the mActor.
     MOZ_ASSERT(!mActor);
   }
 }
 
-RequestId
-Cache::AddRequestPromise(Promise* aPromise, ErrorResult& aRv)
+already_AddRefed<Promise>
+Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv)
 {
-  MOZ_ASSERT(aPromise);
-  MOZ_ASSERT(!mRequestPromises.Contains(aPromise));
-
-  // Register ourself as a promise handler so that the promise will hold us
-  // alive.  This allows the client code to drop the ref to the Cache
-  // object and just keep their promise.  This is fairly common in promise
-  // chaining code.
-  aPromise->AppendNativeHandler(this);
-
-  mRequestPromises.AppendElement(aPromise);
-
-  // (Ab)use the promise pointer as our request ID.  This is a fast, thread-safe
-  // way to get a unique ID for the promise to be resolved later.
-  return reinterpret_cast<RequestId>(aPromise);
-}
+  nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
+  if (!promise) {
+    return nullptr;
+  }
 
-already_AddRefed<Promise>
-Cache::RemoveRequestPromise(RequestId aRequestId)
-{
-  MOZ_ASSERT(aRequestId != INVALID_REQUEST_ID);
+  unused << mActor->SendPCacheOpConstructor(
+    new CacheOpChild(mActor->GetFeature(), mGlobal, this, promise),
+    aOpArgs.SendAsOpArgs());
 
-  for (uint32_t i = 0; i < mRequestPromises.Length(); ++i) {
-    nsRefPtr<Promise>& promise = mRequestPromises.ElementAt(i);
-    // To be safe, only cast promise pointers to our integer RequestId
-    // type and never cast an integer to a pointer.
-    if (aRequestId == reinterpret_cast<RequestId>(promise.get())) {
-      nsRefPtr<Promise> ref;
-      ref.swap(promise);
-      mRequestPromises.RemoveElementAt(i);
-      return ref.forget();
-    }
-  }
-  MOZ_ASSERT_UNREACHABLE("Received response without a matching promise!");
-  return nullptr;
+  return promise.forget();
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -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/. */
 
 #ifndef mozilla_dom_cache_Cache_h
 #define mozilla_dom_cache_Cache_h
 
-#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsIGlobalObject;
@@ -28,22 +27,20 @@ class Promise;
 struct CacheQueryOptions;
 class RequestOrUSVString;
 class Response;
 template<typename T> class Optional;
 template<typename T> class Sequence;
 
 namespace cache {
 
+class AutoChildOpArgs;
 class CacheChild;
-class PCacheRequest;
-class PCacheResponse;
-class PCacheResponseOrVoid;
 
-class Cache final : public PromiseNativeHandler
+class Cache final : public nsISupports
                   , public nsWrapperCache
                   , public TypeUtils
 {
 public:
   Cache(nsIGlobalObject* aGlobal, CacheChild* aActor);
 
   // webidl interface methods
   already_AddRefed<Promise>
@@ -71,61 +68,38 @@ public:
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
 
   nsISupports* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto) override;
 
   // Called when CacheChild actor is being destroyed
   void DestroyInternal(CacheChild* aActor);
 
-  // methods forwarded from CacheChild
-  void RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                         const PCacheResponseOrVoid& aResponse);
-  void RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
-                            const nsTArray<PCacheResponse>& aResponses);
-  void RecvAddAllResponse(RequestId aRequestId,
-                          const mozilla::ErrorResult& aError);
-  void RecvPutResponse(RequestId aRequestId, nsresult aRv);
-
-  void RecvDeleteResponse(RequestId aRequestId, nsresult aRv,
-                          bool aSuccess);
-  void RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                        const nsTArray<PCacheRequest>& aRequests);
-
   // TypeUtils methods
   virtual nsIGlobalObject*
   GetGlobalObject() const override;
 
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual CachePushStreamChild*
   CreatePushStream(nsIAsyncInputStream* aStream) override;
 
-  // PromiseNativeHandler methods
-  virtual void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
-  virtual void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
 private:
   ~Cache();
 
   // Called when we're destroyed or CCed.
   void DisconnectFromActor();
 
-  // TODO: Replace with actor-per-request model during refactor (bug 1110485)
-  RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
-  already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
+  already_AddRefed<Promise>
+  ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv);
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
   CacheChild* mActor;
-  nsTArray<nsRefPtr<Promise>> mRequestPromises;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Cache)
 };
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -4,16 +4,17 @@
  * 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/CacheChild.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/Cache.h"
+#include "mozilla/dom/cache/PCacheOpChild.h"
 #include "mozilla/dom/cache/PCachePushStreamChild.h"
 #include "mozilla/dom/cache/StreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // Declared in ActorUtils.h
@@ -67,16 +68,18 @@ CacheChild::StartDestroy()
 
   // StartDestroy() can get called from either Cache or the Feature.
   // Theoretically we can get double called if the right race happens.  Handle
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
+  // TODO: only destroy if there are no ops or push streams still running
+
   listener->DestroyInternal(this);
 
   // Cache listener should call ClearListener() in DestroyInternal()
   MOZ_ASSERT(!mListener);
 
   // Start actor destruction from parent process
   unused << SendTeardown();
 }
@@ -90,114 +93,39 @@ CacheChild::ActorDestroy(ActorDestroyRea
     listener->DestroyInternal(this);
     // Cache listener should call ClearListener() in DestroyInternal()
     MOZ_ASSERT(!mListener);
   }
 
   RemoveFeature();
 }
 
+PCacheOpChild*
+CacheChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
+{
+  MOZ_CRASH("CacheOpChild should be manually constructed.");
+  return nullptr;
+}
+
+bool
+CacheChild::DeallocPCacheOpChild(PCacheOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PCachePushStreamChild*
 CacheChild::AllocPCachePushStreamChild()
 {
   MOZ_CRASH("CachePushStreamChild should be manually constructed.");
   return nullptr;
 }
 
 bool
 CacheChild::DeallocPCachePushStreamChild(PCachePushStreamChild* aActor)
 {
   delete aActor;
   return true;
 }
 
-bool
-CacheChild::RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
-                              const PCacheResponseOrVoid& aResponse)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-
-  AddFeatureToStreamChild(aResponse, GetFeature());
-
-  nsRefPtr<Cache> listener = mListener;
-  if (!listener) {
-    StartDestroyStreamChild(aResponse);
-    return true;
-  }
-
-  listener->RecvMatchResponse(requestId, aRv, aResponse);
-  return true;
-}
-
-bool
-CacheChild::RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
-                                 nsTArray<PCacheResponse>&& aResponses)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-
-  AddFeatureToStreamChild(aResponses, GetFeature());
-
-  nsRefPtr<Cache> listener = mListener;
-  if (!listener) {
-    StartDestroyStreamChild(aResponses);
-    return true;
-  }
-
-  listener->RecvMatchAllResponse(requestId, aRv, aResponses);
-  return true;
-}
-
-bool
-CacheChild::RecvAddAllResponse(const RequestId& requestId,
-                               const mozilla::ErrorResult& aError)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvAddAllResponse(requestId, aError);
-  }
-  return true;
-}
-
-bool
-CacheChild::RecvPutResponse(const RequestId& aRequestId, const nsresult& aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvPutResponse(aRequestId, aRv);
-  }
-  return true;
-}
-
-bool
-CacheChild::RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
-                               const bool& result)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvDeleteResponse(requestId, aRv, result);
-  }
-  return true;
-}
-
-bool
-CacheChild::RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
-                             nsTArray<PCacheRequest>&& aRequests)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-
-  AddFeatureToStreamChild(aRequests, GetFeature());
-
-  nsRefPtr<Cache> listener = mListener;
-  if (!listener) {
-    StartDestroyStreamChild(aRequests);
-    return true;
-  }
-
-  listener->RecvKeysResponse(requestId, aRv, aRequests);
-  return true;
-}
-
 } // namespace cache
 } // namespace dom
 } // namesapce mozilla
--- a/dom/cache/CacheChild.h
+++ b/dom/cache/CacheChild.h
@@ -36,41 +36,28 @@ public:
   // actor destruction asynchronously from the parent-side.
   virtual void StartDestroy() override;
 
 private:
   // PCacheChild methods
   virtual void
   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;
 
-  virtual bool
-  RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
-                    const PCacheResponseOrVoid& aResponse) override;
-  virtual bool
-  RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
-                       nsTArray<PCacheResponse>&& responses) override;
-  virtual bool
-  RecvAddAllResponse(const RequestId& requestId,
-                     const mozilla::ErrorResult& aError) override;
-  virtual bool
-  RecvPutResponse(const RequestId& aRequestId,
-                  const nsresult& aRv) override;
-  virtual bool
-  RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
-                     const bool& result) override;
-  virtual bool
-  RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
-                   nsTArray<PCacheRequest>&& requests) override;
-
   // Use a weak ref so actor does not hold DOM object alive past content use.
   // The Cache object must call ClearListener() to null this before its
   // destroyed.
   Cache* MOZ_NON_OWNING_REF mListener;
 
   NS_DECL_OWNINGTHREAD
 };
 
deleted file mode 100644
--- a/dom/cache/CacheInitData.ipdlh
+++ /dev/null
@@ -1,24 +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 PBackgroundSharedTypes;
-
-using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
-
-namespace mozilla {
-namespace dom {
-namespace cache {
-
-// Data needed to initialize a CacheStorage or Cache backend.  Don't put
-// this with the other types in PCacheTypes.ipdlh since we want to import
-// it into PBackground.ipdl.
-struct CacheInitData
-{
-  Namespace namespaceEnum;
-  PrincipalInfo principalInfo;
-};
-
-} // namespace cache
-} // namespace dom
-} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpChild.cpp
@@ -0,0 +1,204 @@
+/* -*- 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/CacheOpChild.h"
+
+#include "mozilla/dom/cache/Cache.h"
+#include "mozilla/dom/cache/CacheChild.h"
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
+                           nsISupports* aParent, Promise* aPromise)
+  : mGlobal(aGlobal)
+  , mParent(aParent)
+  , mPromise(aPromise)
+{
+  MOZ_ASSERT(mGlobal);
+  MOZ_ASSERT(mParent);
+  MOZ_ASSERT(mPromise);
+
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  SetFeature(aFeature);
+}
+
+CacheOpChild::~CacheOpChild()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+  MOZ_ASSERT(!mPromise);
+}
+
+void
+CacheOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  // If the actor was terminated for some unknown reason, then indicate the
+  // operation is dead.
+  if (mPromise) {
+    mPromise->MaybeReject(NS_ERROR_FAILURE);
+    mPromise = nullptr;
+  }
+
+  RemoveFeature();
+}
+
+bool
+CacheOpChild::Recv__delete__(const ErrorResult& aRv,
+                             const CacheOpResult& aResult)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  if (aRv.Failed()) {
+    // TODO: Remove this const_cast (bug 1152078).
+    // It is safe for now since this ErrorResult is handed off to us by IPDL
+    // and is thrown into the trash afterwards.
+    mPromise->MaybeReject(const_cast<ErrorResult&>(aRv));
+    mPromise = nullptr;
+    return true;
+  }
+
+  switch (aResult.type()) {
+    case CacheOpResult::TCacheMatchResult:
+    {
+      HandleResponse(aResult.get_CacheMatchResult().responseOrVoid());
+      break;
+    }
+    case CacheOpResult::TCacheMatchAllResult:
+    {
+      HandleResponseList(aResult.get_CacheMatchAllResult().responseList());
+      break;
+    }
+    case CacheOpResult::TCacheAddAllResult:
+    case CacheOpResult::TCachePutAllResult:
+    {
+      mPromise->MaybeResolve(JS::UndefinedHandleValue);
+      break;
+    }
+    case CacheOpResult::TCacheDeleteResult:
+    {
+      mPromise->MaybeResolve(aResult.get_CacheDeleteResult().success());
+      break;
+    }
+    case CacheOpResult::TCacheKeysResult:
+    {
+      HandleRequestList(aResult.get_CacheKeysResult().requestList());
+      break;
+    }
+    case CacheOpResult::TStorageMatchResult:
+    {
+      HandleResponse(aResult.get_StorageMatchResult().responseOrVoid());
+      break;
+    }
+    case CacheOpResult::TStorageHasResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
+      break;
+    }
+    case CacheOpResult::TStorageOpenResult:
+    {
+      auto actor = static_cast<CacheChild*>(
+        aResult.get_StorageOpenResult().actorChild());
+      actor->SetFeature(GetFeature());
+      nsRefPtr<Cache> cache = new Cache(mGlobal, actor);
+      mPromise->MaybeResolve(cache);
+      break;
+    }
+    case CacheOpResult::TStorageDeleteResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
+      break;
+    }
+    case CacheOpResult::TStorageKeysResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageKeysResult().keyList());
+      break;
+    }
+    default:
+      MOZ_CRASH("Unknown Cache op result type!");
+  }
+
+  mPromise = nullptr;
+
+  return true;
+}
+
+void
+CacheOpChild::StartDestroy()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  // Do not cancel on-going operations when Feature calls this.  Instead, keep
+  // the Worker alive until we are done.
+}
+
+nsIGlobalObject*
+CacheOpChild::GetGlobalObject() const
+{
+  return mGlobal;
+}
+
+#ifdef DEBUG
+void
+CacheOpChild::AssertOwningThread() const
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+}
+#endif
+
+CachePushStreamChild*
+CacheOpChild::CreatePushStream(nsIAsyncInputStream* aStream)
+{
+  MOZ_CRASH("CacheOpChild should never create a push stream actor!");
+}
+
+void
+CacheOpChild::HandleResponse(const PCacheResponseOrVoid& aResponseOrVoid)
+{
+  nsRefPtr<Response> response;
+  if (aResponseOrVoid.type() == PCacheResponseOrVoid::TPCacheResponse) {
+    response = ToResponse(aResponseOrVoid);
+  }
+
+  if (!response) {
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    return;
+  }
+
+  mPromise->MaybeResolve(response);
+}
+
+void
+CacheOpChild::HandleResponseList(const nsTArray<PCacheResponse>& aResponseList)
+{
+  nsAutoTArray<nsRefPtr<Response>, 256> responses;
+  responses.SetCapacity(aResponseList.Length());
+
+  for (uint32_t i = 0; i < aResponseList.Length(); ++i) {
+    responses.AppendElement(ToResponse(aResponseList[i]));
+  }
+
+  mPromise->MaybeResolve(responses);
+}
+
+void
+CacheOpChild::HandleRequestList(const nsTArray<PCacheRequest>& aRequestList)
+{
+  nsAutoTArray<nsRefPtr<Request>, 256> requests;
+  requests.SetCapacity(aRequestList.Length());
+
+  for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
+    requests.AppendElement(ToRequest(aRequestList[i]));
+  }
+
+  mPromise->MaybeResolve(requests);
+}
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpChild.h
@@ -0,0 +1,78 @@
+/* -*- 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_CacheOpChild_h
+#define mozilla_dom_cache_CacheOpChild_h
+
+#include "mozilla/dom/cache/ActorChild.h"
+#include "mozilla/dom/cache/PCacheOpChild.h"
+#include "mozilla/dom/cache/TypeUtils.h"
+#include "nsRefPtr.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+
+namespace cache {
+
+class CacheOpChild final : public PCacheOpChild
+                         , public ActorChild
+                         , public TypeUtils
+{
+public:
+  CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
+               nsISupports* aParent, Promise* aPromise);
+  ~CacheOpChild();
+
+private:
+  // PCacheOpChild methods
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  virtual bool
+  Recv__delete__(const ErrorResult& aRv, const CacheOpResult& aResult) override;
+
+  // ActorChild methods
+  virtual void
+  StartDestroy() override;
+
+  // TypeUtils methods
+  virtual nsIGlobalObject*
+  GetGlobalObject() const override;
+
+#ifdef DEBUG
+  virtual void
+  AssertOwningThread() const override;
+#endif
+
+  virtual CachePushStreamChild*
+  CreatePushStream(nsIAsyncInputStream* aStream) override;
+
+  // Utility methods
+  void
+  HandleResponse(const PCacheResponseOrVoid& aResponseOrVoid);
+
+  void
+  HandleResponseList(const nsTArray<PCacheResponse>& aResponseList);
+
+  void
+  HandleRequestList(const nsTArray<PCacheRequest>& aRequestList);
+
+  nsCOMPtr<nsIGlobalObject> mGlobal;
+  nsCOMPtr<nsISupports> mParent;
+  nsRefPtr<Promise> mPromise;
+
+  NS_DECL_OWNINGTHREAD
+};
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_cache_CacheOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpParent.cpp
@@ -0,0 +1,287 @@
+/* -*- 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/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"
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+using mozilla::ipc::FileDescriptorSetParent;
+using mozilla::ipc::PBackgroundParent;
+
+CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
+                             const CacheOpArgs& aOpArgs)
+  : mIpcManager(aIpcManager)
+  , mCacheId(aCacheId)
+  , mNamespace(INVALID_NAMESPACE)
+  , mOpArgs(aOpArgs)
+{
+  MOZ_ASSERT(mIpcManager);
+}
+
+CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager,
+                             Namespace aNamespace, const CacheOpArgs& aOpArgs)
+  : mIpcManager(aIpcManager)
+  , mCacheId(INVALID_CACHE_ID)
+  , mNamespace(aNamespace)
+  , mOpArgs(aOpArgs)
+{
+  MOZ_ASSERT(mIpcManager);
+}
+
+CacheOpParent::~CacheOpParent()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+}
+
+void
+CacheOpParent::Execute(ManagerId* aManagerId)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  nsRefPtr<Manager> manager;
+  nsresult rv = Manager::GetOrCreate(aManagerId, getter_AddRefs(manager));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    unused << Send__delete__(this, ErrorResult(rv), void_t());
+    return;
+  }
+
+  Execute(manager);
+}
+
+void
+CacheOpParent::Execute(Manager* aManager)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  mManager = aManager;
+
+  // Handle add/addAll op with a FetchPut object
+  if (mOpArgs.type() == CacheOpArgs::TCacheAddAllArgs) {
+    MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
+
+    const CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
+    const nsTArray<PCacheRequest>& list = args.requestList();
+
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
+    for (uint32_t i = 0; i < list.Length(); ++i) {
+      requestStreamList.AppendElement(DeserializeCacheStream(list[i].body()));
+    }
+
+    nsRefPtr<FetchPut> fetchPut;
+    nsresult rv = FetchPut::Create(this, mManager, mCacheId, list,
+                                   requestStreamList, getter_AddRefs(fetchPut));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      OnOpComplete(ErrorResult(rv), CacheAddAllResult());
+      return;
+    }
+
+    mFetchPutList.AppendElement(fetchPut.forget());
+    return;
+  }
+
+  // Handle put op
+  if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
+    MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
+
+    const CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
+    const nsTArray<CacheRequestResponse>& list = args.requestResponseList();
+
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> responseStreamList;
+
+    for (uint32_t i = 0; i < list.Length(); ++i) {
+      requestStreamList.AppendElement(
+        DeserializeCacheStream(list[i].request().body()));
+      responseStreamList.AppendElement(
+        DeserializeCacheStream(list[i].response().body()));
+    }
+
+    mManager->ExecutePutAll(this, mCacheId, args.requestResponseList(),
+                            requestStreamList, responseStreamList);
+    return;
+  }
+
+  // Handle all other cache ops
+  if (mCacheId != INVALID_CACHE_ID) {
+    MOZ_ASSERT(mNamespace == INVALID_NAMESPACE);
+    mManager->ExecuteCacheOp(this, mCacheId, mOpArgs);
+    return;
+  }
+
+  // Handle all storage ops
+  MOZ_ASSERT(mNamespace != INVALID_NAMESPACE);
+  mManager->ExecuteStorageOp(this, mNamespace, mOpArgs);
+}
+
+void
+CacheOpParent::WaitForVerification(PrincipalVerifier* aVerifier)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  mVerifier = aVerifier;
+  mVerifier->AddListener(this);
+}
+
+void
+CacheOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+
+  if (mVerifier) {
+    mVerifier->RemoveListener(this);
+    mVerifier = nullptr;
+  }
+
+  for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
+    mFetchPutList[i]->ClearListener();
+  }
+  mFetchPutList.Clear();
+
+  if (mManager) {
+    mManager->RemoveListener(this);
+    mManager = nullptr;
+  }
+
+  mIpcManager = nullptr;
+}
+
+void
+CacheOpParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+
+  mVerifier->RemoveListener(this);
+  mVerifier = nullptr;
+
+  if (NS_WARN_IF(NS_FAILED(aRv))) {
+    unused << Send__delete__(this, ErrorResult(aRv), void_t());
+    return;
+  }
+
+  Execute(aManagerId);
+}
+
+void
+CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                            CacheId aOpenedCacheId,
+                            const nsTArray<SavedResponse>& aSavedResponseList,
+                            const nsTArray<SavedRequest>& aSavedRequestList,
+                            StreamList* aStreamList)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(mIpcManager);
+  MOZ_ASSERT(mManager);
+
+  // 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);
+
+  if (aRv.Failed()) {
+    // TODO: send full ErrorCode
+    unused << Send__delete__(this, aRv, result.SendAsOpResult());
+    aRv.ClearMessage(); // This may contain a TypeError.
+    return;
+  }
+
+  if (aOpenedCacheId != INVALID_CACHE_ID) {
+    result.Add(aOpenedCacheId, mManager);
+  }
+
+  for (uint32_t i = 0; i < aSavedResponseList.Length(); ++i) {
+    result.Add(aSavedResponseList[i], aStreamList);
+  }
+
+  for (uint32_t i = 0; i < aSavedRequestList.Length(); ++i) {
+    result.Add(aSavedRequestList[i], aStreamList);
+  }
+
+  // TODO: send full ErrorCode
+  unused << Send__delete__(this, aRv, result.SendAsOpResult());
+}
+
+void
+CacheOpParent::OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(aFetchPut);
+
+  aFetchPut->ClearListener();
+  MOZ_ALWAYS_TRUE(mFetchPutList.RemoveElement(aFetchPut));
+
+  OnOpComplete(Move(aRv), CacheAddAllResult());
+}
+
+already_AddRefed<nsIInputStream>
+CacheOpParent::DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid)
+{
+  if (aStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
+
+  // 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
+  //           control actor.
+  stream = ReadStream::Create(readStream);
+  if (stream) {
+    return stream.forget();
+  }
+
+  // Option 3: A stream was serialized using normal methods.
+  nsAutoTArray<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);
+}
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpParent.h
@@ -0,0 +1,87 @@
+/* -*- 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_CacheOpParent_h
+#define mozilla_dom_cache_CacheOpParent_h
+
+#include "mozilla/dom/cache/FetchPut.h"
+#include "mozilla/dom/cache/Manager.h"
+#include "mozilla/dom/cache/PCacheOpParent.h"
+#include "mozilla/dom/cache/PrincipalVerifier.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace ipc {
+class PBackgroundParent;
+}
+namespace dom {
+namespace cache {
+
+class CacheOpParent final : public PCacheOpParent
+                          , public PrincipalVerifier::Listener
+                          , public Manager::Listener
+                          , public FetchPut::Listener
+{
+  // to allow use of convenience overrides
+  using Manager::Listener::OnOpComplete;
+
+public:
+  CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager, CacheId aCacheId,
+                const CacheOpArgs& aOpArgs);
+  CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager,
+                Namespace aNamespace, const CacheOpArgs& aOpArgs);
+  ~CacheOpParent();
+
+  void
+  Execute(ManagerId* aManagerId);
+
+  void
+  Execute(Manager* aManager);
+
+  void
+  WaitForVerification(PrincipalVerifier* aVerifier);
+
+private:
+  // PCacheOpParent methods
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  // PrincipalVerifier::Listener methods
+  virtual void
+  OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId) override;
+
+  // Manager::Listener methods
+  virtual void
+  OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+               CacheId aOpenedCacheId,
+               const nsTArray<SavedResponse>& aSavedResponseList,
+               const nsTArray<SavedRequest>& aSavedRequestList,
+               StreamList* aStreamList) override;
+
+  // FetchPut::Listener methods
+  virtual void
+  OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) override;
+
+  // utility methods
+  already_AddRefed<nsIInputStream>
+  DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid);
+
+  mozilla::ipc::PBackgroundParent* mIpcManager;
+  const CacheId mCacheId;
+  const Namespace mNamespace;
+  const CacheOpArgs mOpArgs;
+  nsRefPtr<Manager> mManager;
+  nsRefPtr<PrincipalVerifier> mVerifier;
+  nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
+
+  NS_DECL_OWNINGTHREAD
+};
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_cache_CacheOpParent_h
--- a/dom/cache/CacheParent.cpp
+++ b/dom/cache/CacheParent.cpp
@@ -1,37 +1,24 @@
 /* -*- 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/CacheParent.h"
 
-#include "mozilla/DebugOnly.h"
-#include "mozilla/dom/cache/AutoUtils.h"
+#include "mozilla/dom/cache/CacheOpParent.h"
 #include "mozilla/dom/cache/CachePushStreamParent.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/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/PBackgroundParent.h"
-#include "mozilla/ipc/FileDescriptorSetParent.h"
-#include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-using mozilla::dom::ErrNum;
-using mozilla::ipc::FileDescriptorSetParent;
-using mozilla::ipc::PFileDescriptorSetParent;
-
 // Declared in ActorUtils.h
 void
 DeallocPCacheParent(PCacheParent* aActor)
 {
   delete aActor;
 }
 
 CacheParent::CacheParent(cache::Manager* aManager, CacheId aCacheId)
@@ -42,32 +29,58 @@ CacheParent::CacheParent(cache::Manager*
   MOZ_ASSERT(mManager);
   mManager->AddRefCacheId(mCacheId);
 }
 
 CacheParent::~CacheParent()
 {
   MOZ_COUNT_DTOR(cache::CacheParent);
   MOZ_ASSERT(!mManager);
-  MOZ_ASSERT(mFetchPutList.IsEmpty());
 }
 
 void
 CacheParent::ActorDestroy(ActorDestroyReason aReason)
 {
   MOZ_ASSERT(mManager);
-  for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
-    mFetchPutList[i]->ClearListener();
-  }
-  mFetchPutList.Clear();
-  mManager->RemoveListener(this);
   mManager->ReleaseCacheId(mCacheId);
   mManager = nullptr;
 }
 
+PCacheOpParent*
+CacheParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
+{
+  if (aOpArgs.type() != CacheOpArgs::TCacheMatchArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheMatchAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCachePutAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheDeleteArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheKeysArgs)
+  {
+    MOZ_CRASH("Invalid operation sent to Cache actor!");
+  }
+
+  return new CacheOpParent(Manager(), mCacheId, aOpArgs);
+}
+
+bool
+CacheParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+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)
@@ -81,243 +94,11 @@ 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;
 }
 
-bool
-CacheParent::RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
-                       const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheMatch(this, aRequestId, mCacheId, aRequest,
-                       aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvMatchAll(const RequestId& aRequestId,
-                          const PCacheRequestOrVoid& aRequest,
-                          const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheMatchAll(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvAddAll(const RequestId& aRequestId,
-                        nsTArray<PCacheRequest>&& aRequests)
-{
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreams;
-  requestStreams.SetCapacity(aRequests.Length());
-
-  for (uint32_t i = 0; i < aRequests.Length(); ++i) {
-    requestStreams.AppendElement(DeserializeCacheStream(aRequests[i].body()));
-  }
-
-  nsRefPtr<FetchPut> fetchPut;
-  nsresult rv = FetchPut::Create(this, mManager, aRequestId, mCacheId,
-                                 aRequests, requestStreams,
-                                 getter_AddRefs(fetchPut));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR);
-    ErrorResult error;
-    error.Throw(rv);
-    if (!SendAddAllResponse(aRequestId, error)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to send AddAll response.");
-    }
-    return true;
-  }
-
-  mFetchPutList.AppendElement(fetchPut.forget());
-
-  return true;
-}
-
-bool
-CacheParent::RecvPut(const RequestId& aRequestId,
-                     const CacheRequestResponse& aPut)
-{
-  MOZ_ASSERT(mManager);
-
-  nsAutoTArray<CacheRequestResponse, 1> putList;
-  putList.AppendElement(aPut);
-
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> requestStreamList;
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> responseStreamList;
-
-  requestStreamList.AppendElement(
-    DeserializeCacheStream(aPut.request().body()));
-  responseStreamList.AppendElement(
-    DeserializeCacheStream(aPut.response().body()));
-
-
-  mManager->CachePutAll(this, aRequestId, mCacheId, putList, requestStreamList,
-                        responseStreamList);
-
-  return true;
-}
-
-bool
-CacheParent::RecvDelete(const RequestId& aRequestId,
-                        const PCacheRequest& aRequest,
-                        const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheDelete(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvKeys(const RequestId& aRequestId,
-                      const PCacheRequestOrVoid& aRequest,
-                      const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheKeys(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-void
-CacheParent::OnCacheMatch(RequestId aRequestId, nsresult aRv,
-                          const SavedResponse* aSavedResponse,
-                          StreamList* aStreamList)
-{
-  AutoParentResponseOrVoid response(Manager());
-
-  // no match
-  if (NS_FAILED(aRv) || !aSavedResponse || !aStreamList) {
-    if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to send Match response.");
-    }
-    return;
-  }
-
-  if (aSavedResponse) {
-    response.Add(*aSavedResponse, aStreamList);
-  }
-
-  if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Match response.");
-  }
-}
-
-void
-CacheParent::OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
-                             const nsTArray<SavedResponse>& aSavedResponses,
-                             StreamList* aStreamList)
-{
-  AutoParentResponseList responses(Manager(), aSavedResponses.Length());
-
-  for (uint32_t i = 0; i < aSavedResponses.Length(); ++i) {
-    responses.Add(aSavedResponses[i], aStreamList);
-  }
-
-  if (!SendMatchAllResponse(aRequestId, aRv, responses.SendAsResponseList())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send MatchAll response.");
-  }
-}
-
-void
-CacheParent::OnCachePutAll(RequestId aRequestId, nsresult aRv)
-{
-  if (!SendPutResponse(aRequestId, aRv)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Put response.");
-  }
-}
-
-void
-CacheParent::OnCacheDelete(RequestId aRequestId, nsresult aRv, bool aSuccess)
-{
-  if (!SendDeleteResponse(aRequestId, aRv, aSuccess)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Delete response.");
-  }
-}
-
-void
-CacheParent::OnCacheKeys(RequestId aRequestId, nsresult aRv,
-                         const nsTArray<SavedRequest>& aSavedRequests,
-                         StreamList* aStreamList)
-{
-  AutoParentRequestList requests(Manager(), aSavedRequests.Length());
-
-  for (uint32_t i = 0; i < aSavedRequests.Length(); ++i) {
-    requests.Add(aSavedRequests[i], aStreamList);
-  }
-
-  if (!SendKeysResponse(aRequestId, aRv, requests.SendAsRequestList())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Keys response.");
-  }
-}
-
-void
-CacheParent::OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, const ErrorResult& aRv)
-{
-  aFetchPut->ClearListener();
-  mFetchPutList.RemoveElement(aFetchPut);
-  if (!SendAddAllResponse(aRequestId, aRv)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send AddAll response.");
-  }
-}
-
-already_AddRefed<nsIInputStream>
-CacheParent::DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid)
-{
-  if (aStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIInputStream> stream;
-  const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
-
-  // 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
-  //           control actor.
-  stream = ReadStream::Create(readStream);
-  if (stream) {
-    return stream.forget();
-  }
-
-  // Option 3: A stream was serialized using normal methods.
-  nsAutoTArray<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);
-}
-
 } // namespace cache
 } // namespace dom
 } // namesapce mozilla
--- a/dom/cache/CacheParent.h
+++ b/dom/cache/CacheParent.h
@@ -2,86 +2,55 @@
 /* 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_CacheParent_h
 #define mozilla_dom_cache_CacheParent_h
 
-#include "mozilla/dom/cache/FetchPut.h"
-#include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/PCacheParent.h"
 #include "mozilla/dom/cache/Types.h"
 
-struct nsID;
-template <class T> class nsRefPtr;
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-struct SavedResponse;
+class Manager;
 
 class CacheParent final : public PCacheParent
-                        , public Manager::Listener
-                        , public FetchPut::Listener
 {
 public:
   CacheParent(cache::Manager* aManager, CacheId aCacheId);
   virtual ~CacheParent();
 
 private:
-  // PCacheParent method
+  // PCacheParent methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
-  virtual PCachePushStreamParent* AllocPCachePushStreamParent() override;
-  virtual bool DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
-  virtual bool RecvTeardown() override;
-  virtual bool
-  RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
-            const PCacheQueryParams& aParams) override;
+
+  virtual PCacheOpParent*
+  AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
+
   virtual bool
-  RecvMatchAll(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
-               const PCacheQueryParams& aParams) override;
-  virtual bool
-  RecvAddAll(const RequestId& aRequestId,
-             nsTArray<PCacheRequest>&& aRequests) override;
-  virtual bool
-  RecvPut(const RequestId& aRequestId,
-          const CacheRequestResponse& aPut) override;
-  virtual bool
-  RecvDelete(const RequestId& aRequestId, const PCacheRequest& aRequest,
-             const PCacheQueryParams& aParams) override;
+  DeallocPCacheOpParent(PCacheOpParent* aActor) override;
+
   virtual bool
-  RecvKeys(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
-           const PCacheQueryParams& aParams) override;
+  RecvPCacheOpConstructor(PCacheOpParent* actor,
+                          const CacheOpArgs& aOpArgs) override;
+
+  virtual PCachePushStreamParent*
+  AllocPCachePushStreamParent() override;
 
-  // Manager::Listener methods
-  virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
-                            const SavedResponse* aSavedResponse,
-                            StreamList* aStreamList) override;
-  virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
-                               const nsTArray<SavedResponse>& aSavedResponses,
-                               StreamList* aStreamList) override;
-  virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
-  virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
-                             bool aSuccess) override;
-  virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
-                           const nsTArray<SavedRequest>& aSavedRequests,
-                           StreamList* aStreamList) override;
+  virtual bool
+  DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
 
-  // FetchPut::Listener methods
-  virtual void OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId,
-                          const mozilla::ErrorResult& aRv) override;
-
-  already_AddRefed<nsIInputStream>
-  DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid);
+  virtual bool
+  RecvTeardown() override;
 
   nsRefPtr<cache::Manager> mManager;
   const CacheId mCacheId;
-  nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_CacheParent_h
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/CacheStorageBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheChild.h"
+#include "mozilla/dom/cache/CacheOpChild.h"
 #include "mozilla/dom/cache/CacheStorageChild.h"
 #include "mozilla/dom/cache/Feature.h"
 #include "mozilla/dom/cache/PCacheChild.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
@@ -36,34 +37,36 @@ using mozilla::dom::workers::WorkerPriva
 using mozilla::ipc::BackgroundChild;
 using mozilla::ipc::PBackgroundChild;
 using mozilla::ipc::IProtocol;
 using mozilla::ipc::PrincipalInfo;
 using mozilla::ipc::PrincipalToPrincipalInfo;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::CacheStorage);
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::CacheStorage);
-NS_IMPL_CYCLE_COLLECTION_CLASS(mozilla::dom::cache::CacheStorage)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::CacheStorage)
-  tmp->DisconnectFromActor();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::CacheStorage)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::CacheStorage)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::CacheStorage,
+                                      mGlobal);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIIPCBackgroundChildCreateCallback)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
 NS_INTERFACE_MAP_END
 
+// We cannot reference IPC types in a webidl binding implementation header.  So
+// define this in the .cpp and use heap storage in the mPendingRequests list.
+struct CacheStorage::Entry final
+{
+  nsRefPtr<Promise> mPromise;
+  CacheOpArgs mArgs;
+  // We cannot add the requests until after the actor is present.  So store
+  // the request data separately for now.
+  nsRefPtr<InternalRequest> mRequest;
+};
+
 // static
 already_AddRefed<CacheStorage>
 CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
                                  nsIPrincipal* aPrincipal, ErrorResult& aRv)
 {
   MOZ_ASSERT(aGlobal);
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(NS_IsMainThread());
@@ -170,146 +173,141 @@ CacheStorage::CacheStorage(Namespace aNa
 }
 
 already_AddRefed<Promise>
 CacheStorage::Match(const RequestOrUSVString& aRequest,
                     const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
+  if (mFailedActor) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsRefPtr<InternalRequest> request = ToInternalRequest(aRequest, IgnoreBody,
+                                                        aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
   nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mFailedActor) {
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
-    return promise.forget();
-  }
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
+  PCacheQueryParams params;
+  ToPCacheQueryParams(params, aOptions);
 
-  Entry entry;
-  entry.mRequestId = requestId;
-  entry.mOp = OP_MATCH;
-  entry.mOptions = aOptions;
-  entry.mRequest = ToInternalRequest(aRequest, IgnoreBody, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
+  nsAutoPtr<Entry> entry(new Entry());
+  entry->mPromise = promise;
+  entry->mArgs = StorageMatchArgs(PCacheRequest(), params);
+  entry->mRequest = request;
 
-  mPendingRequests.AppendElement(entry);
-
+  mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 CacheStorage::Has(const nsAString& aKey, ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
+  if (mFailedActor) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
   nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mFailedActor) {
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
-    return promise.forget();
-  }
+  nsAutoPtr<Entry> entry(new Entry());
+  entry->mPromise = promise;
+  entry->mArgs = StorageHasArgs(nsString(aKey));
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  Entry* entry = mPendingRequests.AppendElement();
-  entry->mRequestId = requestId;
-  entry->mOp = OP_HAS;
-  entry->mKey = aKey;
-
+  mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 CacheStorage::Open(const nsAString& aKey, ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
+  if (mFailedActor) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
   nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mFailedActor) {
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
-    return promise.forget();
-  }
+  nsAutoPtr<Entry> entry(new Entry());
+  entry->mPromise = promise;
+  entry->mArgs = StorageOpenArgs(nsString(aKey));
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  Entry* entry = mPendingRequests.AppendElement();
-  entry->mRequestId = requestId;
-  entry->mOp = OP_OPEN;
-  entry->mKey = aKey;
-
+  mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 CacheStorage::Delete(const nsAString& aKey, ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
+  if (mFailedActor) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
   nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mFailedActor) {
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
-    return promise.forget();
-  }
+  nsAutoPtr<Entry> entry(new Entry());
+  entry->mPromise = promise;
+  entry->mArgs = StorageDeleteArgs(nsString(aKey));
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  Entry* entry = mPendingRequests.AppendElement();
-  entry->mRequestId = requestId;
-  entry->mOp = OP_DELETE;
-  entry->mKey = aKey;
-
+  mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 CacheStorage::Keys(ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
+  if (mFailedActor) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
   nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mFailedActor) {
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
-    return promise.forget();
-  }
+  nsAutoPtr<Entry> entry(new Entry());
+  entry->mPromise = promise;
+  entry->mArgs = StorageKeysArgs();
 
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  Entry* entry = mPendingRequests.AppendElement();
-  entry->mRequestId = requestId;
-  entry->mOp = OP_KEYS;
-
+  mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
 // static
 bool
 CacheStorage::PrefEnabled(JSContext* aCx, JSObject* aObj)
@@ -366,19 +364,18 @@ CacheStorage::ActorFailed()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
   MOZ_ASSERT(!mFailedActor);
 
   mFailedActor = true;
   mFeature = nullptr;
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
-    RequestId requestId = mPendingRequests[i].mRequestId;
-    nsRefPtr<Promise> promise = RemoveRequestPromise(requestId);
-    promise->MaybeReject(NS_ERROR_UNEXPECTED);
+    nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
+    entry->mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
   }
   mPendingRequests.Clear();
 }
 
 void
 CacheStorage::DestroyInternal(CacheStorageChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
@@ -387,127 +384,16 @@ CacheStorage::DestroyInternal(CacheStora
   mActor->ClearListener();
   mActor = nullptr;
 
   // Note that we will never get an actor again in case another request is
   // made before this object is destructed.
   ActorFailed();
 }
 
-void
-CacheStorage::RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                                const PCacheResponseOrVoid& aResponse)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
-  // Convert the response immediately if its present.  This ensures that
-  // any stream actors are cleaned up, even if we error out below.
-  nsRefPtr<Response> response;
-  if (aResponse.type() == PCacheResponseOrVoid::TPCacheResponse) {
-    response = ToResponse(aResponse);
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  // If cache name was specified in the request options and the cache does
-  // not exist, then an error code will already have been set.  If we
-  // still do not have a response, then we just resolve undefined like a
-  // normal Cache::Match.
-  if (!response) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
-    return;
-  }
-
-  promise->MaybeResolve(response);
-}
-
-void
-CacheStorage::RecvHasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-
-  }
-
-  promise->MaybeResolve(aSuccess);
-}
-
-void
-CacheStorage::RecvOpenResponse(RequestId aRequestId, nsresult aRv,
-                               CacheChild* aActor)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
-  // Unlike most of our async callback Recv*() methods, this one gets back
-  // an actor.  We need to make sure to clean it up in case of error.
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    if (aActor) {
-      // We cannot use the CacheChild::StartDestroy() method because there
-      // is no Cache object associated with the actor yet.  Instead, just
-      // send the underlying Teardown message.
-      unused << aActor->SendTeardown();
-    }
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  if (!aActor) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-
-  nsRefPtr<Cache> cache = new Cache(mGlobal, aActor);
-  promise->MaybeResolve(cache);
-}
-
-void
-CacheStorage::RecvDeleteResponse(RequestId aRequestId, nsresult aRv,
-                                 bool aSuccess)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(aSuccess);
-}
-
-void
-CacheStorage::RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                               const nsTArray<nsString>& aKeys)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(aKeys);
-}
-
 nsIGlobalObject*
 CacheStorage::GetGlobalObject() const
 {
   return mGlobal;
 }
 
 #ifdef DEBUG
 void
@@ -519,136 +405,48 @@ CacheStorage::AssertOwningThread() const
 
 CachePushStreamChild*
 CacheStorage::CreatePushStream(nsIAsyncInputStream* aStream)
 {
   // This is true because CacheStorage always uses IgnoreBody for requests.
   MOZ_CRASH("CacheStorage should never create a push stream.");
 }
 
-void
-CacheStorage::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
-{
-  // Do nothing.  The Promise will automatically drop the ref to us after
-  // calling the callback.  This is what we want as we only registered in order
-  // to be held alive via the Promise handle.
-}
-
-void
-CacheStorage::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
-{
-  // Do nothing.  The Promise will automatically drop the ref to us after
-  // calling the callback.  This is what we want as we only registered in order
-  // to be held alive via the Promise handle.
-}
-
 CacheStorage::~CacheStorage()
 {
-  DisconnectFromActor();
-}
-
-void
-CacheStorage::DisconnectFromActor()
-{
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
-
   if (mActor) {
     mActor->StartDestroy();
     // DestroyInternal() is called synchronously by StartDestroy().  So we
     // should have already cleared the mActor.
     MOZ_ASSERT(!mActor);
   }
 }
 
 void
 CacheStorage::MaybeRunPendingRequests()
 {
   if (!mActor) {
     return;
   }
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
-    // Note, the entry can be modified below due to Request/Response body
-    // being marked used.
-    Entry& entry = mPendingRequests[i];
-    RequestId requestId = entry.mRequestId;
-    switch(entry.mOp) {
-      case OP_MATCH:
-      {
-        AutoChildRequest request(this);
-        ErrorResult rv;
-        request.Add(entry.mRequest, IgnoreBody, PassThroughReferrer,
-                    IgnoreInvalidScheme, rv);
-        if (NS_WARN_IF(rv.Failed())) {
-          nsRefPtr<Promise> promise = RemoveRequestPromise(requestId);
-          promise->MaybeReject(rv);
-          break;
-        }
-
-        PCacheQueryParams params;
-        ToPCacheQueryParams(params, entry.mOptions);
-
-        unused << mActor->SendMatch(requestId, request.SendAsRequest(), params);
-        break;
-      }
-      case OP_HAS:
-        unused << mActor->SendHas(requestId, entry.mKey);
-        break;
-      case OP_OPEN:
-        unused << mActor->SendOpen(requestId, entry.mKey);
-        break;
-      case OP_DELETE:
-        unused << mActor->SendDelete(requestId, entry.mKey);
-        break;
-      case OP_KEYS:
-        unused << mActor->SendKeys(requestId);
-        break;
-      default:
-        MOZ_ASSERT_UNREACHABLE("Unknown pending CacheStorage op.");
+    ErrorResult rv;
+    nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
+    AutoChildOpArgs args(this, entry->mArgs);
+    if (entry->mRequest) {
+      args.Add(entry->mRequest, IgnoreBody, PassThroughReferrer,
+               IgnoreInvalidScheme, rv);
     }
+    if (rv.Failed()) {
+      entry->mPromise->MaybeReject(rv);
+      continue;
+    }
+    unused << mActor->SendPCacheOpConstructor(
+      new CacheOpChild(mActor->GetFeature(), mGlobal, this, entry->mPromise),
+      args.SendAsOpArgs());
   }
   mPendingRequests.Clear();
 }
 
-RequestId
-CacheStorage::AddRequestPromise(Promise* aPromise, ErrorResult& aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_ASSERT(aPromise);
-  MOZ_ASSERT(!mRequestPromises.Contains(aPromise));
-
-  // Register ourself as a promise handler so that the promise will hold us
-  // alive.  This allows the client code to drop the ref to the CacheStorage
-  // object and just keep their promise.  This is fairly common in promise
-  // chaining code.
-  aPromise->AppendNativeHandler(this);
-
-  mRequestPromises.AppendElement(aPromise);
-
-  // (Ab)use the promise pointer as our request ID.  This is a fast, thread-safe
-  // way to get a unique ID for the promise to be resolved later.
-  return reinterpret_cast<RequestId>(aPromise);
-}
-
-already_AddRefed<Promise>
-CacheStorage::RemoveRequestPromise(RequestId aRequestId)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_ASSERT(aRequestId != INVALID_REQUEST_ID);
-
-  for (uint32_t i = 0; i < mRequestPromises.Length(); ++i) {
-    nsRefPtr<Promise>& promise = mRequestPromises.ElementAt(i);
-    // To be safe, only cast promise pointers to our integer RequestId
-    // type and never cast an integer to a pointer.
-    if (aRequestId == reinterpret_cast<RequestId>(promise.get())) {
-      nsRefPtr<Promise> ref;
-      ref.swap(promise);
-      mRequestPromises.RemoveElementAt(i);
-      return ref.forget();
-    }
-  }
-  MOZ_ASSERT_UNREACHABLE("Received response without a matching promise!");
-  return nullptr;
-}
-
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -3,17 +3,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/. */
 
 #ifndef mozilla_dom_cache_CacheStorage_h
 #define mozilla_dom_cache_CacheStorage_h
 
 #include "mozilla/dom/CacheBinding.h"
-#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
@@ -41,17 +40,16 @@ namespace cache {
 class CacheChild;
 class CacheStorageChild;
 class Feature;
 class PCacheResponseOrVoid;
 
 class CacheStorage final : public nsIIPCBackgroundChildCreateCallback
                          , public nsWrapperCache
                          , public TypeUtils
-                         , public PromiseNativeHandler
 {
   typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
 
 public:
   static already_AddRefed<CacheStorage>
   CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
                      nsIPrincipal* aPrincipal, ErrorResult& aRv);
 
@@ -76,89 +74,43 @@ public:
 
   // nsIIPCbackgroundChildCreateCallback methods
   virtual void ActorCreated(PBackgroundChild* aActor) override;
   virtual void ActorFailed() override;
 
   // Called when CacheStorageChild actor is being destroyed
   void DestroyInternal(CacheStorageChild* aActor);
 
-  // Methods forwarded from CacheStorageChild
-  void RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                         const PCacheResponseOrVoid& aResponse);
-  void RecvHasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
-  void RecvOpenResponse(RequestId aRequestId, nsresult aRv,
-                        CacheChild* aActor);
-  void RecvDeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
-  void RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                        const nsTArray<nsString>& aKeys);
-
   // TypeUtils methods
   virtual nsIGlobalObject* GetGlobalObject() const override;
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual CachePushStreamChild*
   CreatePushStream(nsIAsyncInputStream* aStream) override;
 
-  // PromiseNativeHandler methods
-  virtual void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
-  virtual void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
 private:
   CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
                const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
   ~CacheStorage();
 
-  // Called when we're destroyed or CCed.
-  void DisconnectFromActor();
-
   void MaybeRunPendingRequests();
 
-  RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
-  already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
-
-  // Would like to use CacheInitData here, but we cannot because
-  // its an IPC struct which breaks webidl by including windows.h.
   const Namespace mNamespace;
   nsCOMPtr<nsIGlobalObject> mGlobal;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
   nsRefPtr<Feature> mFeature;
+
+  // weak ref cleared in DestroyInternal
   CacheStorageChild* mActor;
-  nsTArray<nsRefPtr<Promise>> mRequestPromises;
-
-  enum Op
-  {
-    OP_MATCH,
-    OP_HAS,
-    OP_OPEN,
-    OP_DELETE,
-    OP_KEYS
-  };
 
-  struct Entry
-  {
-    RequestId mRequestId;
-    Op mOp;
-    // Would prefer to use PCacheRequest/PCacheCacheQueryOptions, but can't
-    // because they introduce a header dependency on windows.h which
-    // breaks the bindings build.
-    nsRefPtr<InternalRequest> mRequest;
-    CacheQueryOptions mOptions;
-    // It would also be nice to union the key with the match args above,
-    // but VS2013 doesn't like these types in unions because of copy
-    // constructors.
-    nsString mKey;
-  };
+  struct Entry;
+  nsTArray<nsAutoPtr<Entry>> mPendingRequests;
 
-  nsTArray<Entry> mPendingRequests;
   bool mFailedActor;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CacheStorage,
                                            nsIIPCBackgroundChildCreateCallback)
 };
 
--- a/dom/cache/CacheStorageChild.cpp
+++ b/dom/cache/CacheStorageChild.cpp
@@ -4,16 +4,17 @@
  * 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/CacheStorageChild.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheStorage.h"
+#include "mozilla/dom/cache/PCacheOpChild.h"
 #include "mozilla/dom/cache/StreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // declared in ActorUtils.h
 void
@@ -55,16 +56,18 @@ CacheStorageChild::StartDestroy()
 
   // StartDestroy() can get called from either CacheStorage or the Feature.
   // Theoretically we can get double called if the right race happens.  Handle
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
+  // TODO: don't destroy if we have outstanding ops
+
   listener->DestroyInternal(this);
 
   // CacheStorage listener should call ClearListener() in DestroyInternal()
   MOZ_ASSERT(!mListener);
 
   // Start actor destruction from parent process
   unused << SendTeardown();
 }
@@ -78,99 +81,25 @@ CacheStorageChild::ActorDestroy(ActorDes
     listener->DestroyInternal(this);
     // CacheStorage listener should call ClearListener() in DestroyInternal()
     MOZ_ASSERT(!mListener);
   }
 
   RemoveFeature();
 }
 
-bool
-CacheStorageChild::RecvMatchResponse(const RequestId& aRequestId,
-                                     const nsresult& aRv,
-                                     const PCacheResponseOrVoid& aResponseOrVoid)
+PCacheOpChild*
+CacheStorageChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
 {
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-
-  AddFeatureToStreamChild(aResponseOrVoid, GetFeature());
-
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (!listener) {
-    StartDestroyStreamChild(aResponseOrVoid);
-    return true;
-  }
-
-  listener->RecvMatchResponse(aRequestId, aRv, aResponseOrVoid);
-
-  return true;
-}
-
-bool
-CacheStorageChild::RecvHasResponse(const RequestId& aRequestId,
-                                   const nsresult& aRv,
-                                   const bool& aSuccess)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (listener) {
-    listener->RecvHasResponse(aRequestId, aRv, aSuccess);
-  }
-  return true;
+  MOZ_CRASH("CacheOpChild should be manually constructed.");
+  return nullptr;
 }
 
 bool
-CacheStorageChild::RecvOpenResponse(const RequestId& aRequestId,
-                                    const nsresult& aRv,
-                                    PCacheChild* aActor)
+CacheStorageChild::DeallocPCacheOpChild(PCacheOpChild* aActor)
 {
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (!listener || FeatureNotified()) {
-    if (aActor) {
-      unused << aActor->SendTeardown();
-    }
-    return true;
-  }
-
-  CacheChild* cacheChild = static_cast<CacheChild*>(aActor);
-
-  // Since FeatureNotified() returned false above, we are guaranteed that
-  // the feature won't try to shutdown the actor until after we create the
-  // Cache DOM object in the listener's RecvOpenResponse() method.  This
-  // is important because StartShutdown() expects a Cache object listener.
-  if (cacheChild) {
-    cacheChild->SetFeature(GetFeature());
-  }
-
-  listener->RecvOpenResponse(aRequestId, aRv, cacheChild);
-  return true;
-}
-
-bool
-CacheStorageChild::RecvDeleteResponse(const RequestId& aRequestId,
-                                      const nsresult& aRv,
-                                      const bool& aResult)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (listener) {
-    listener->RecvDeleteResponse(aRequestId, aRv, aResult);
-  }
-  return true;
-}
-
-bool
-CacheStorageChild::RecvKeysResponse(const RequestId& aRequestId,
-                                    const nsresult& aRv,
-                                    nsTArray<nsString>&& aKeys)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (listener) {
-    listener->RecvKeysResponse(aRequestId, aRv, aKeys);
-  }
+  delete aActor;
   return true;
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorageChild.h
+++ b/dom/cache/CacheStorageChild.h
@@ -22,46 +22,36 @@ class Feature;
 class CacheStorageChild final : public PCacheStorageChild
                               , public ActorChild
 {
 public:
   CacheStorageChild(CacheStorage* aListener, Feature* aFeature);
   ~CacheStorageChild();
 
   // Must be called by the associated CacheStorage listener in its
-  // ActorDestroy() method.  Also, CacheStorage must Send__delete__() the
+  // ActorDestroy() method.  Also, CacheStorage must call SendDestroy() on the
   // actor in its destructor to trigger ActorDestroy() if it has not been
   // called yet.
   void ClearListener();
 
   // ActorChild methods
 
   // Synchronously call ActorDestroy on our CacheStorage listener and then start
   // the actor destruction asynchronously from the parent-side.
   virtual void StartDestroy() override;
 
 private:
   // PCacheStorageChild methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
 
-  virtual bool RecvMatchResponse(const RequestId& aRequestId,
-                                 const nsresult& aRv,
-                                 const PCacheResponseOrVoid& response) override;
-  virtual bool RecvHasResponse(const cache::RequestId& aRequestId,
-                               const nsresult& aRv,
-                               const bool& aSuccess) override;
-  virtual bool RecvOpenResponse(const cache::RequestId& aRequestId,
-                                const nsresult& aRv,
-                                PCacheChild* aActor) override;
-  virtual bool RecvDeleteResponse(const cache::RequestId& aRequestId,
-                                  const nsresult& aRv,
-                                  const bool& aResult) override;
-  virtual bool RecvKeysResponse(const cache::RequestId& aRequestId,
-                                const nsresult& aRv,
-                                nsTArray<nsString>&& aKeys) override;
+  virtual PCacheOpChild*
+  AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  DeallocPCacheOpChild(PCacheOpChild* aActor) override;
 
   // Use a weak ref so actor does not hold DOM object alive past content use.
   // The CacheStorage object must call ClearListener() to null this before its
   // destroyed.
   CacheStorage* MOZ_NON_OWNING_REF mListener;
 
   NS_DECL_OWNINGTHREAD
 };
--- a/dom/cache/CacheStorageParent.cpp
+++ b/dom/cache/CacheStorageParent.cpp
@@ -1,38 +1,28 @@
 /* -*- 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/CacheStorageParent.h"
 
+#include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/cache/ActorUtils.h"
-#include "mozilla/dom/cache/AutoUtils.h"
-#include "mozilla/dom/cache/CacheParent.h"
-#include "mozilla/dom/cache/CacheStreamControlParent.h"
-#include "mozilla/dom/cache/Manager.h"
+#include "mozilla/dom/cache/CacheOpParent.h"
 #include "mozilla/dom/cache/ManagerId.h"
-#include "mozilla/dom/cache/ReadStream.h"
-#include "mozilla/dom/cache/SavedTypes.h"
-#include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/ipc/PBackgroundParent.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/PFileDescriptorSetParent.h"
-#include "mozilla/DebugOnly.h"
-#include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::PBackgroundParent;
-using mozilla::ipc::PFileDescriptorSetParent;
 using mozilla::ipc::PrincipalInfo;
 
 // declared in ActorUtils.h
 PCacheStorageParent*
 AllocPCacheStorageParent(PBackgroundParent* aManagingActor,
                          Namespace aNamespace,
                          const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
 {
@@ -60,391 +50,93 @@ CacheStorageParent::CacheStorageParent(P
                                                    aPrincipalInfo);
   MOZ_ASSERT(mVerifier);
 }
 
 CacheStorageParent::~CacheStorageParent()
 {
   MOZ_COUNT_DTOR(cache::CacheStorageParent);
   MOZ_ASSERT(!mVerifier);
-  MOZ_ASSERT(!mManager);
 }
 
 void
 CacheStorageParent::ActorDestroy(ActorDestroyReason aReason)
 {
   if (mVerifier) {
-    mVerifier->ClearListener();
+    mVerifier->RemoveListener(this);
     mVerifier = nullptr;
   }
+}
+
+PCacheOpParent*
+CacheStorageParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
+{
+  if (aOpArgs.type() != CacheOpArgs::TStorageMatchArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageHasArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageOpenArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageDeleteArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageKeysArgs)
+  {
+    MOZ_CRASH("Invalid operation sent to CacheStorage actor!");
+  }
 
-  if (mManager) {
-    MOZ_ASSERT(!mActiveRequests.IsEmpty());
-    mManager->RemoveListener(this);
-    mManager = nullptr;
+  return new CacheOpParent(Manager(), mNamespace, aOpArgs);
+}
+
+bool
+CacheStorageParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+CacheStorageParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
+                                            const CacheOpArgs& aOpArgs)
+{
+  auto actor = static_cast<CacheOpParent*>(aActor);
+
+  if (mVerifier) {
+    MOZ_ASSERT(!mManagerId);
+    actor->WaitForVerification(mVerifier);
+    return true;
   }
+
+  if (NS_FAILED(mVerifiedStatus)) {
+    unused << CacheOpParent::Send__delete__(actor, ErrorResult(mVerifiedStatus),
+                                            void_t());
+    return true;
+  }
+
+  MOZ_ASSERT(mManagerId);
+  actor->Execute(mManagerId);
+  return true;
 }
 
 bool
 CacheStorageParent::RecvTeardown()
 {
   if (!Send__delete__(this)) {
     // child process is gone, warn and allow actor to clean up normally
     NS_WARNING("CacheStorage failed to delete actor.");
   }
   return true;
 }
 
-bool
-CacheStorageParent::RecvMatch(const RequestId& aRequestId,
-                              const PCacheRequest& aRequest,
-                              const PCacheQueryParams& aParams)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendMatchResponse(aRequestId, mVerifiedStatus, void_t())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_MATCH;
-    entry->mRequestId = aRequestId;
-    entry->mRequest = aRequest;
-    entry->mParams = aParams;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendMatchResponse(aRequestId, rv, void_t())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return true;
-  }
-
-  manager->StorageMatch(this, aRequestId, mNamespace, aRequest,
-                        aParams);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvHas(const RequestId& aRequestId, const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendHasResponse(aRequestId, mVerifiedStatus, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Has response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_HAS;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendHasResponse(aRequestId, rv, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Has response.");
-    }
-    return true;
-  }
-
-  manager->StorageHas(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvOpen(const RequestId& aRequestId, const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendOpenResponse(aRequestId, mVerifiedStatus, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_OPEN;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendOpenResponse(aRequestId, rv, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return true;
-  }
-
-  manager->StorageOpen(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvDelete(const RequestId& aRequestId,
-                               const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendDeleteResponse(aRequestId, mVerifiedStatus, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Delete response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_DELETE;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendDeleteResponse(aRequestId, rv, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Delete response.");
-    }
-    return true;
-  }
-
-  manager->StorageDelete(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvKeys(const RequestId& aRequestId)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendKeysResponse(aRequestId, mVerifiedStatus, nsTArray<nsString>())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Keys response.");
-    }
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_DELETE;
-    entry->mRequestId = aRequestId;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendKeysResponse(aRequestId, rv, nsTArray<nsString>())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Keys response.");
-    }
-    return true;
-  }
-
-  manager->StorageKeys(this, aRequestId, mNamespace);
-
-  return true;
-}
-
 void
 CacheStorageParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
 {
   MOZ_ASSERT(mVerifier);
   MOZ_ASSERT(!mManagerId);
-  MOZ_ASSERT(!mManager);
   MOZ_ASSERT(NS_SUCCEEDED(mVerifiedStatus));
 
   if (NS_WARN_IF(NS_FAILED(aRv))) {
     mVerifiedStatus = aRv;
   }
 
   mManagerId = aManagerId;
-  mVerifier->ClearListener();
+  mVerifier->RemoveListener(this);
   mVerifier = nullptr;
-
-  RetryPendingRequests();
-}
-
-void
-CacheStorageParent::OnStorageMatch(RequestId aRequestId, nsresult aRv,
-                                   const SavedResponse* aSavedResponse,
-                                   StreamList* aStreamList)
-{
-  PCacheResponseOrVoid responseOrVoid;
-
-  ReleaseManager(aRequestId);
-
-  AutoParentResponseOrVoid response(Manager());
-
-  // no match
-  if (NS_FAILED(aRv) || !aSavedResponse) {
-    if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return;
-  }
-
-  if (aSavedResponse) {
-    response.Add(*aSavedResponse, aStreamList);
-  }
-
-  if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Match response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageHas(RequestId aRequestId, nsresult aRv,
-                                 bool aCacheFound)
-{
-  ReleaseManager(aRequestId);
-  if (!SendHasResponse(aRequestId, aRv, aCacheFound)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Has response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageOpen(RequestId aRequestId, nsresult aRv,
-                                  CacheId aCacheId)
-{
-  if (NS_FAILED(aRv)) {
-    ReleaseManager(aRequestId);
-    if (!SendOpenResponse(aRequestId, aRv, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return;
-  }
-
-  MOZ_ASSERT(mManager);
-  CacheParent* actor = new CacheParent(mManager, aCacheId);
-
-  ReleaseManager(aRequestId);
-
-  PCacheParent* base = Manager()->SendPCacheConstructor(actor);
-  actor = static_cast<CacheParent*>(base);
-  if (!SendOpenResponse(aRequestId, aRv, actor)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Open response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageDelete(RequestId aRequestId, nsresult aRv,
-                                    bool aCacheDeleted)
-{
-  ReleaseManager(aRequestId);
-  if (!SendDeleteResponse(aRequestId, aRv, aCacheDeleted)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Delete response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageKeys(RequestId aRequestId, nsresult aRv,
-                                  const nsTArray<nsString>& aKeys)
-{
-  ReleaseManager(aRequestId);
-  if (!SendKeysResponse(aRequestId, aRv, aKeys)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Keys response.");
-  }
-}
-
-void
-CacheStorageParent::RetryPendingRequests()
-{
-  MOZ_ASSERT(mManagerId || NS_FAILED(mVerifiedStatus));
-  for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
-    const Entry& entry = mPendingRequests[i];
-    switch(entry.mOp) {
-      case OP_MATCH:
-        RecvMatch(entry.mRequestId, entry.mRequest, entry.mParams);
-        break;
-      case OP_HAS:
-        RecvHas(entry.mRequestId, entry.mKey);
-        break;
-      case OP_OPEN:
-        RecvOpen(entry.mRequestId, entry.mKey);
-        break;
-      case OP_DELETE:
-        RecvDelete(entry.mRequestId, entry.mKey);
-        break;
-      case OP_KEYS:
-        RecvKeys(entry.mRequestId);
-        break;
-      default:
-        MOZ_ASSERT_UNREACHABLE("Pending request within unknown op");
-    }
-  }
-  mPendingRequests.Clear();
-  mPendingRequests.Compact();
-}
-
-nsresult
-CacheStorageParent::RequestManager(RequestId aRequestId,
-                                   cache::Manager** aManagerOut)
-{
-  MOZ_ASSERT(!mActiveRequests.Contains(aRequestId));
-  nsRefPtr<cache::Manager> ref = mManager;
-  if (!ref) {
-    MOZ_ASSERT(mActiveRequests.IsEmpty());
-    nsresult rv = cache::Manager::GetOrCreate(mManagerId, getter_AddRefs(ref));
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    mManager = ref;
-  }
-  mActiveRequests.AppendElement(aRequestId);
-  ref.forget(aManagerOut);
-  return NS_OK;
-}
-
-void
-CacheStorageParent::ReleaseManager(RequestId aRequestId)
-{
-  // Note that if the child process dies we also clean up the mManager in
-  // ActorDestroy().  There is no race with this method, however, because
-  // ActorDestroy removes this object from the Manager's listener list.
-  // Therefore ReleaseManager() should never be called after ActorDestroy()
-  // runs.
-  MOZ_ASSERT(mManager);
-  MOZ_ASSERT(!mActiveRequests.IsEmpty());
-
-  MOZ_ALWAYS_TRUE(mActiveRequests.RemoveElement(aRequestId));
-
-  if (mActiveRequests.IsEmpty()) {
-    mManager->RemoveListener(this);
-    mManager = nullptr;
-  }
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorageParent.h
+++ b/dom/cache/CacheStorageParent.h
@@ -2,107 +2,59 @@
 /* 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_CacheStorageParent_h
 #define mozilla_dom_cache_CacheStorageParent_h
 
-#include "mozilla/dom/cache/CacheInitData.h"
 #include "mozilla/dom/cache/PCacheStorageParent.h"
-#include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/PrincipalVerifier.h"
 #include "mozilla/dom/cache/Types.h"
 
-template <class T> class nsRefPtr;
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-class CacheStreamControlParent;
 class ManagerId;
 
 class CacheStorageParent final : public PCacheStorageParent
                                , public PrincipalVerifier::Listener
-                               , public Manager::Listener
 {
 public:
   CacheStorageParent(PBackgroundParent* aManagingActor, Namespace aNamespace,
                      const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
   virtual ~CacheStorageParent();
 
 private:
   // PCacheStorageParent methods
-  virtual void ActorDestroy(ActorDestroyReason aReason) override;
-  virtual bool RecvTeardown() override;
-  virtual bool RecvMatch(const RequestId& aRequestId,
-                         const PCacheRequest& aRequest,
-                         const PCacheQueryParams& aParams) override;
-  virtual bool RecvHas(const RequestId& aRequestId,
-                       const nsString& aKey) override;
-  virtual bool RecvOpen(const RequestId& aRequestId,
-                        const nsString& aKey) override;
-  virtual bool RecvDelete(const RequestId& aRequestId,
-                          const nsString& aKey) override;
-  virtual bool RecvKeys(const RequestId& aRequestId) override;
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  virtual PCacheOpParent*
+  AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  DeallocPCacheOpParent(PCacheOpParent* aActor) override;
+
+  virtual bool
+  RecvPCacheOpConstructor(PCacheOpParent* actor,
+                          const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  RecvTeardown() override;
 
   // PrincipalVerifier::Listener methods
   virtual void OnPrincipalVerified(nsresult aRv,
                                    ManagerId* aManagerId) override;
 
-  // Manager::Listener methods
-  virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
-                              const SavedResponse* aResponse,
-                              StreamList* aStreamList) override;
-  virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
-                            bool aCacheFound) override;
-  virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
-                             CacheId aCacheId) override;
-  virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
-                               bool aCacheDeleted) override;
-  virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
-                             const nsTArray<nsString>& aKeys) override;
-
-  CacheStreamControlParent*
-  SerializeReadStream(CacheStreamControlParent *aStreamControl, const nsID& aId,
-                      StreamList* aStreamList,
-                      PCacheReadStream* aReadStreamOut);
-
-  void RetryPendingRequests();
-
-  nsresult RequestManager(RequestId aRequestId, cache::Manager** aManagerOut);
-  void ReleaseManager(RequestId aRequestId);
-
   const Namespace mNamespace;
   nsRefPtr<PrincipalVerifier> mVerifier;
   nsresult mVerifiedStatus;
   nsRefPtr<ManagerId> mManagerId;
-  nsRefPtr<cache::Manager> mManager;
-
-  enum Op
-  {
-    OP_MATCH,
-    OP_HAS,
-    OP_OPEN,
-    OP_DELETE,
-    OP_KEYS
-  };
-
-  struct Entry
-  {
-    Op mOp;
-    RequestId mRequestId;
-    nsString mKey;
-    PCacheRequest mRequest;
-    PCacheQueryParams mParams;
-  };
-
-  nsTArray<Entry> mPendingRequests;
-  nsTArray<RequestId> mActiveRequests;
 };
 
 } // namesapce cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_CacheStorageParent_h
--- a/dom/cache/DBAction.cpp
+++ b/dom/cache/DBAction.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/net/nsFileProtocolHandler.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageService.h"
 #include "mozStorageCID.h"
 #include "nsIFile.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
 #include "DBSchema.h"
 #include "FileUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
--- a/dom/cache/DBAction.h
+++ b/dom/cache/DBAction.h
@@ -3,17 +3,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/. */
 
 #ifndef mozilla_dom_cache_DBAction_h
 #define mozilla_dom_cache_DBAction_h
 
 #include "mozilla/dom/cache/Action.h"
-#include "mozilla/dom/cache/CacheInitData.h"
 #include "nsRefPtr.h"
 #include "nsString.h"
 
 class mozIStorageConnection;
 class nsIFile;
 
 namespace mozilla {
 namespace dom {
--- a/dom/cache/FetchPut.cpp
+++ b/dom/cache/FetchPut.cpp
@@ -93,34 +93,33 @@ protected:
 
 private:
   nsRefPtr<FetchPut> mFetchPut;
   nsRefPtr<InternalResponse> mInternalResponse;
 };
 
 // static
 nsresult
-FetchPut::Create(Listener* aListener, Manager* aManager,
-                 RequestId aRequestId, CacheId aCacheId,
+FetchPut::Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
                  const nsTArray<PCacheRequest>& aRequests,
                  const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
                  FetchPut** aFetchPutOut)
 {
   MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length());
 
   // The FetchDriver requires that all requests have a referrer already set.
 #ifdef DEBUG
   for (uint32_t i = 0; i < aRequests.Length(); ++i) {
     if (aRequests[i].referrer() == EmptyString()) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 #endif
 
-  nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aRequestId, aCacheId,
+  nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aCacheId,
                                         aRequests, aRequestStreams);
 
   nsresult rv = ref->DispatchToMainThread();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   ref.forget(aFetchPutOut);
 
   return NS_OK;
@@ -128,23 +127,21 @@ FetchPut::Create(Listener* aListener, Ma
 
 void
 FetchPut::ClearListener()
 {
   MOZ_ASSERT(mListener);
   mListener = nullptr;
 }
 
-FetchPut::FetchPut(Listener* aListener, Manager* aManager,
-                   RequestId aRequestId, CacheId aCacheId,
+FetchPut::FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
                    const nsTArray<PCacheRequest>& aRequests,
                    const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams)
   : mListener(aListener)
   , mManager(aManager)
-  , mRequestId(aRequestId)
   , mCacheId(aCacheId)
   , mInitiatingThread(NS_GetCurrentThread())
   , mStateList(aRequests.Length())
   , mPendingCount(0)
 {
   MOZ_ASSERT(mListener);
   MOZ_ASSERT(mManager);
   MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length());
@@ -206,17 +203,17 @@ FetchPut::DoFetchOnMainThread()
 
   nsRefPtr<ManagerId> managerId = mManager->GetManagerId();
   nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
   mPendingCount = mStateList.Length();
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    MaybeSetError(rv);
+    MaybeSetError(ErrorResult(rv));
     MaybeCompleteOnMainThread();
     return;
   }
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     nsRefPtr<InternalRequest> internalRequest =
       ToInternalRequest(mStateList[i].mPCacheRequest);
 
@@ -235,17 +232,17 @@ FetchPut::DoFetchOnMainThread()
 
     nsRefPtr<FetchDriver> fetchDriver = new FetchDriver(internalRequest,
                                                         principal,
                                                         loadGroup);
 
     mStateList[i].mFetchObserver = new FetchObserver(this);
     rv = fetchDriver->Fetch(mStateList[i].mFetchObserver);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      MaybeSetError(rv);
+      MaybeSetError(ErrorResult(rv));
       mStateList[i].mFetchObserver = nullptr;
       mPendingCount -= 1;
       continue;
     }
   }
 
   // If they all failed, then we might need to complete main thread immediately
   MaybeCompleteOnMainThread();
@@ -253,17 +250,17 @@ FetchPut::DoFetchOnMainThread()
 
 void
 FetchPut::FetchComplete(FetchObserver* aObserver,
                         InternalResponse* aInternalResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aInternalResponse->IsError() && !mResult.Failed()) {
-    MaybeSetError(NS_ERROR_FAILURE);
+    MaybeSetError(ErrorResult(NS_ERROR_FAILURE));
   }
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     if (mStateList[i].mFetchObserver == aObserver) {
       ErrorResult rv;
       ToPCacheResponseWithoutBody(mStateList[i].mPCacheResponse,
                                   *aInternalResponse, rv);
       if (rv.Failed()) {
@@ -312,31 +309,31 @@ FetchPut::DoPutOnWorkerThread()
   putList.SetCapacity(mStateList.Length());
   requestStreamList.SetCapacity(mStateList.Length());
   responseStreamList.SetCapacity(mStateList.Length());
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     // The spec requires us to catch if content tries to insert a set of
     // requests that would overwrite each other.
     if (MatchInPutList(mStateList[i].mPCacheRequest, putList)) {
-      MaybeSetError(NS_ERROR_DOM_INVALID_STATE_ERR);
+      MaybeSetError(ErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
       MaybeNotifyListener();
       return;
     }
 
     CacheRequestResponse* entry = putList.AppendElement();
     entry->request() = mStateList[i].mPCacheRequest;
     entry->response() = mStateList[i].mPCacheResponse;
     requestStreamList.AppendElement(mStateList[i].mRequestStream.forget());
     responseStreamList.AppendElement(mStateList[i].mResponseStream.forget());
   }
   mStateList.Clear();
 
-  mManager->CachePutAll(this, mRequestId, mCacheId, putList, requestStreamList,
-                        responseStreamList);
+  mManager->ExecutePutAll(this, mCacheId, putList, requestStreamList,
+                          responseStreamList);
 }
 
 // static
 bool
 FetchPut::MatchInPutList(const PCacheRequest& aRequest,
                          const nsTArray<CacheRequestResponse>& aPutList)
 {
   // This method implements the SW spec QueryCache algorithm against an
@@ -421,45 +418,49 @@ FetchPut::MatchInPutList(const PCacheReq
       return true;
     }
   }
 
   return false;
 }
 
 void
-FetchPut::OnCachePutAll(RequestId aRequestId, nsresult aRv)
+FetchPut::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                       CacheId aOpenedCacheId,
+                       const nsTArray<SavedResponse>& aSavedResponseList,
+                       const nsTArray<SavedRequest>& aSavedRequestList,
+                       StreamList* aStreamList)
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
-  MaybeSetError(aRv);
+  MOZ_ASSERT(aResult.type() == CacheOpResult::TCachePutAllResult);
+  MaybeSetError(Move(aRv));
   MaybeNotifyListener();
 }
 
 void
-FetchPut::MaybeSetError(nsresult aRv)
+FetchPut::MaybeSetError(ErrorResult&& aRv)
 {
-  if (mResult.Failed() || NS_SUCCEEDED(aRv)) {
+  if (mResult.Failed() || !aRv.Failed()) {
     return;
   }
-  mResult.Throw(aRv);
+  mResult = Move(aRv);
 }
 
 void
 FetchPut::MaybeNotifyListener()
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
   if (!mListener) {
     return;
   }
   // CacheParent::OnFetchPut can lead to the destruction of |this| when the
   // object is removed from CacheParent::mFetchPutList, so make sure that
   // doesn't happen until this method returns.
   nsRefPtr<FetchPut> kungFuDeathGrip(this);
-  mListener->OnFetchPut(this, mRequestId, mResult);
-  mResult.ClearMessage(); // This may contain a TypeError.
+  mListener->OnFetchPut(this, Move(mResult));
 }
 
 nsIGlobalObject*
 FetchPut::GetGlobalObject() const
 {
   MOZ_CRASH("No global object in parent-size FetchPut operation!");
 }
 
--- a/dom/cache/FetchPut.h
+++ b/dom/cache/FetchPut.h
@@ -35,22 +35,21 @@ class FetchPut final : public Manager::L
 {
 public:
   typedef std::pair<nsRefPtr<Request>, nsRefPtr<Response>> PutPair;
 
   class Listener
   {
   public:
     virtual void
-    OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, const ErrorResult& aRv) = 0;
+    OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) = 0;
   };
 
   static nsresult
-  Create(Listener* aListener, Manager* aManager,
-         RequestId aRequestId, CacheId aCacheId,
+  Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
          const nsTArray<PCacheRequest>& aRequests,
          const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
          FetchPut** aFetchPutOut);
 
   void ClearListener();
 
 private:
   class Runnable;
@@ -63,50 +62,54 @@ private:
     nsRefPtr<FetchObserver> mFetchObserver;
     PCacheResponse mPCacheResponse;
     nsCOMPtr<nsIInputStream> mResponseStream;
 
     nsRefPtr<Request> mRequest;
     nsRefPtr<Response> mResponse;
   };
 
-  FetchPut(Listener* aListener, Manager* aManager,
-           RequestId aRequestId, CacheId aCacheId,
+  FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
            const nsTArray<PCacheRequest>& aRequests,
            const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams);
   ~FetchPut();
 
   nsresult DispatchToMainThread();
   void DispatchToInitiatingThread();
 
   void DoFetchOnMainThread();
   void FetchComplete(FetchObserver* aObserver,
                      InternalResponse* aInternalResponse);
   void MaybeCompleteOnMainThread();
 
   void DoPutOnWorkerThread();
   static bool MatchInPutList(const PCacheRequest& aRequest,
                              const nsTArray<CacheRequestResponse>& aPutList);
-  virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
 
-  void MaybeSetError(nsresult aRv);
+  virtual void
+  OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+               CacheId aOpenedCacheId,
+               const nsTArray<SavedResponse>& aSavedResponseList,
+               const nsTArray<SavedRequest>& aSavedRequestList,
+               StreamList* aStreamList) override;
+
+  void MaybeSetError(ErrorResult&& aRv);
   void MaybeNotifyListener();
 
   // TypeUtils methods
   virtual nsIGlobalObject* GetGlobalObject() const override;
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual CachePushStreamChild*
   CreatePushStream(nsIAsyncInputStream* aStream) override;
 
   Listener* mListener;
   nsRefPtr<Manager> mManager;
-  const RequestId mRequestId;
   const CacheId mCacheId;
   nsCOMPtr<nsIThread> mInitiatingThread;
   nsTArray<State> mStateList;
   uint32_t mPendingCount;
   ErrorResult mResult;
   nsCOMPtr<nsIRunnable> mRunnable;
 
 public:
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -404,43 +404,41 @@ StaticRefPtr<nsIThread> Manager::Factory
 // ----------------------------------------------------------------------------
 
 // Abstract class to help implement the various Actions.  The vast majority
 // of Actions are synchronous and need to report back to a Listener on the
 // Manager.
 class Manager::BaseAction : public SyncDBAction
 {
 protected:
-  BaseAction(Manager* aManager, ListenerId aListenerId, RequestId aRequestId)
+  BaseAction(Manager* aManager, ListenerId aListenerId)
     : SyncDBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
-    , mRequestId (aRequestId)
   {
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) = 0;
+  Complete(Listener* aListener, ErrorResult&& aRv) = 0;
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
   {
     NS_ASSERT_OWNINGTHREAD(Manager::BaseAction);
     Listener* listener = mManager->GetListener(mListenerId);
     if (listener) {
-      Complete(listener, aRv);
+      Complete(listener, ErrorResult(aRv));
     }
 
     // ensure we release the manager on the initiating thread
     mManager = nullptr;
   }
 
   nsRefPtr<Manager> mManager;
   const ListenerId mListenerId;
-  const RequestId mRequestId;
 };
 
 // ----------------------------------------------------------------------------
 
 // Action that is executed when we determine that content has stopped using
 // a Cache object that has been orphaned.
 class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
 {
@@ -483,34 +481,32 @@ private:
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheMatchAction final : public Manager::BaseAction
 {
 public:
   CacheMatchAction(Manager* aManager, ListenerId aListenerId,
-                   RequestId aRequestId, CacheId aCacheId,
-                   const PCacheRequest& aRequest,
-                   const PCacheQueryParams& aParams,
+                   CacheId aCacheId, const CacheMatchArgs& aArgs,
                    StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequest(aRequest)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
     , mFoundResponse(false)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::CacheMatch(aConn, mCacheId, mRequest, mParams,
-                                       &mFoundResponse, &mResponse);
+    nsresult rv = DBSchema::CacheMatch(aConn, mCacheId, mArgs.request(),
+                                       mArgs.params(), &mFoundResponse,
+                                       &mResponse);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     if (!mFoundResponse || !mResponse.mHasBodyId) {
       return rv;
     }
 
     nsCOMPtr<nsIInputStream> stream;
     rv = FileUtils::BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId,
@@ -519,64 +515,61 @@ public:
     if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
 
     mStreamList->Add(mResponse.mBodyId, stream);
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (!mFoundResponse) {
-      aListener->OnCacheMatch(mRequestId, aRv, nullptr, nullptr);
+      aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()));
     } else {
       mStreamList->Activate(mCacheId);
-      aListener->OnCacheMatch(mRequestId, aRv, &mResponse, mStreamList);
+      aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()), mResponse,
+                              mStreamList);
     }
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequest mRequest;
-  const PCacheQueryParams mParams;
+  const CacheMatchArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   bool mFoundResponse;
   SavedResponse mResponse;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheMatchAllAction final : public Manager::BaseAction
 {
 public:
   CacheMatchAllAction(Manager* aManager, ListenerId aListenerId,
-                      RequestId aRequestId, CacheId aCacheId,
-                      const PCacheRequestOrVoid& aRequestOrVoid,
-                      const PCacheQueryParams& aParams,
+                      CacheId aCacheId, const CacheMatchAllArgs& aArgs,
                       StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequestOrVoid(aRequestOrVoid)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::CacheMatchAll(aConn, mCacheId, mRequestOrVoid,
-                                          mParams, mSavedResponses);
+    nsresult rv = DBSchema::CacheMatchAll(aConn, mCacheId, mArgs.requestOrVoid(),
+                                          mArgs.params(), mSavedResponses);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     for (uint32_t i = 0; i < mSavedResponses.Length(); ++i) {
       if (!mSavedResponses[i].mHasBodyId) {
         continue;
       }
 
       nsCOMPtr<nsIInputStream> stream;
@@ -588,53 +581,52 @@ public:
 
       mStreamList->Add(mSavedResponses[i].mBodyId, stream);
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheMatchAll(mRequestId, aRv, mSavedResponses, mStreamList);
+    aListener->OnOpComplete(Move(aRv), CacheMatchAllResult(), mSavedResponses,
+                            mStreamList);
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequestOrVoid mRequestOrVoid;
-  const PCacheQueryParams mParams;
+  const CacheMatchAllArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   nsTArray<SavedResponse> mSavedResponses;
 };
 
 // ----------------------------------------------------------------------------
 
 // This is the most complex Action.  It puts a request/response pair into the
 // Cache.  It does not complete until all of the body data has been saved to
 // disk.  This means its an asynchronous Action.
 class Manager::CachePutAllAction final : public DBAction
 {
 public:
   CachePutAllAction(Manager* aManager, ListenerId aListenerId,
-                    RequestId aRequestId, CacheId aCacheId,
+                    CacheId aCacheId,
                     const nsTArray<CacheRequestResponse>& aPutList,
                     const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
                     const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
     : DBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
-    , mRequestId(aRequestId)
     , mCacheId(aCacheId)
     , mList(aPutList.Length())
     , mExpectedAsyncCopyCompletions(1)
     , mAsyncResult(NS_OK)
     , mMutex("cache::Manager::CachePutAllAction")
   {
     MOZ_ASSERT(!aPutList.IsEmpty());
     MOZ_ASSERT(aPutList.Length() == aRequestStreamList.Length());
@@ -809,17 +801,17 @@ private:
       mList[i].mResponseStream = nullptr;
     }
 
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
 
     Listener* listener = mManager->GetListener(mListenerId);
     mManager = nullptr;
     if (listener) {
-      listener->OnCachePutAll(mRequestId, aRv);
+      listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
     }
   }
 
   virtual void
   CancelOnInitiatingThread() override
   {
     NS_ASSERT_OWNINGTHREAD(Action);
     Action::CancelOnInitiatingThread();
@@ -962,17 +954,16 @@ private:
     nsRefPtr<Action::Resolver> resolver;
     mResolver.swap(resolver);
     resolver->Resolve(aRv);
   }
 
   // initiating thread only
   nsRefPtr<Manager> mManager;
   const ListenerId mListenerId;
-  const RequestId mRequestId;
 
   // Set on initiating thread, read on target thread.  State machine guarantees
   // these are not modified while being read by the target thread.
   const CacheId mCacheId;
   nsTArray<Entry> mList;
   uint32_t mExpectedAsyncCopyCompletions;
 
   // target thread only
@@ -993,89 +984,83 @@ private:
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheDeleteAction final : public Manager::BaseAction
 {
 public:
   CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
-                    RequestId aRequestId, CacheId aCacheId,
-                    const PCacheRequest& aRequest,
-                    const PCacheQueryParams& aParams)
-    : BaseAction(aManager, aListenerId, aRequestId)
+                    CacheId aCacheId, const CacheDeleteArgs& aArgs)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequest(aRequest)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mSuccess(false)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    nsresult rv = DBSchema::CacheDelete(aConn, mCacheId, mRequest, mParams,
-                                        mDeletedBodyIdList, &mSuccess);
+    nsresult rv = DBSchema::CacheDelete(aConn, mCacheId, mArgs.request(),
+                                        mArgs.params(), mDeletedBodyIdList,
+                                        &mSuccess);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mSuccess = false;
       return rv;
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
-    aListener->OnCacheDelete(mRequestId, aRv, mSuccess);
+    aListener->OnOpComplete(Move(aRv), CacheDeleteResult(mSuccess));
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequest mRequest;
-  const PCacheQueryParams mParams;
+  const CacheDeleteArgs mArgs;
   bool mSuccess;
   nsTArray<nsID> mDeletedBodyIdList;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheKeysAction final : public Manager::BaseAction
 {
 public:
   CacheKeysAction(Manager* aManager, ListenerId aListenerId,
-                  RequestId aRequestId, CacheId aCacheId,
-                  const PCacheRequestOrVoid& aRequestOrVoid,
-                  const PCacheQueryParams& aParams,
+                  CacheId aCacheId, const CacheKeysArgs& aArgs,
                   StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequestOrVoid(aRequestOrVoid)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::CacheKeys(aConn, mCacheId, mRequestOrVoid, mParams,
-                                      mSavedRequests);
+    nsresult rv = DBSchema::CacheKeys(aConn, mCacheId, mArgs.requestOrVoid(),
+                                      mArgs.params(), mSavedRequests);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     for (uint32_t i = 0; i < mSavedRequests.Length(); ++i) {
       if (!mSavedRequests[i].mHasBodyId) {
         continue;
       }
 
       nsCOMPtr<nsIInputStream> stream;
@@ -1087,60 +1072,59 @@ public:
 
       mStreamList->Add(mSavedRequests[i].mBodyId, stream);
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheKeys(mRequestId, aRv, mSavedRequests, mStreamList);
+    aListener->OnOpComplete(Move(aRv), CacheKeysResult(), mSavedRequests,
+                            mStreamList);
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequestOrVoid mRequestOrVoid;
-  const PCacheQueryParams mParams;
+  const CacheKeysArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   nsTArray<SavedRequest> mSavedRequests;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageMatchAction final : public Manager::BaseAction
 {
 public:
   StorageMatchAction(Manager* aManager, ListenerId aListenerId,
-                     RequestId aRequestId, Namespace aNamespace,
-                     const PCacheRequest& aRequest,
-                     const PCacheQueryParams& aParams,
+                     Namespace aNamespace,
+                     const StorageMatchArgs& aArgs,
                      StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
-    , mRequest(aRequest)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
     , mFoundResponse(false)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::StorageMatch(aConn, mNamespace, mRequest, mParams,
-                                         &mFoundResponse, &mSavedResponse);
+    nsresult rv = DBSchema::StorageMatch(aConn, mNamespace, mArgs.request(),
+                                         mArgs.params(), &mFoundResponse,
+                                         &mSavedResponse);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     if (!mFoundResponse || !mSavedResponse.mHasBodyId) {
       return rv;
     }
 
     nsCOMPtr<nsIInputStream> stream;
     rv = FileUtils::BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
@@ -1149,232 +1133,273 @@ public:
     if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
 
     mStreamList->Add(mSavedResponse.mBodyId, stream);
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (!mFoundResponse) {
-      aListener->OnStorageMatch(mRequestId, aRv, nullptr, nullptr);
+      aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()));
     } else {
       mStreamList->Activate(mSavedResponse.mCacheId);
-      aListener->OnStorageMatch(mRequestId, aRv, &mSavedResponse, mStreamList);
+      aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()), mSavedResponse,
+                              mStreamList);
     }
     mStreamList = nullptr;
   }
 
 private:
   const Namespace mNamespace;
-  const PCacheRequest mRequest;
-  const PCacheQueryParams mParams;
+  const StorageMatchArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   bool mFoundResponse;
   SavedResponse mSavedResponse;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageHasAction final : public Manager::BaseAction
 {
 public:
   StorageHasAction(Manager* aManager, ListenerId aListenerId,
-                   RequestId aRequestId, Namespace aNamespace,
-                   const nsAString& aKey)
-    : BaseAction(aManager, aListenerId, aRequestId)
+                   Namespace aNamespace, const StorageHasArgs& aArgs)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
-    , mKey(aKey)
+    , mArgs(aArgs)
     , mCacheFound(false)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     CacheId cacheId;
-    return DBSchema::StorageGetCacheId(aConn, mNamespace, mKey,
+    return DBSchema::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
                                        &mCacheFound, &cacheId);
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
-    aListener->OnStorageHas(mRequestId, aRv, mCacheFound);
+    aListener->OnOpComplete(Move(aRv), StorageHasResult(mCacheFound));
   }
 
 private:
   const Namespace mNamespace;
-  const nsString mKey;
+  const StorageHasArgs mArgs;
   bool mCacheFound;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageOpenAction final : public Manager::BaseAction
 {
 public:
   StorageOpenAction(Manager* aManager, ListenerId aListenerId,
-                    RequestId aRequestId, Namespace aNamespace,
-                    const nsAString& aKey)
-    : BaseAction(aManager, aListenerId, aRequestId)
+                    Namespace aNamespace, const StorageOpenArgs& aArgs)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
-    , mKey(aKey)
+    , mArgs(aArgs)
     , mCacheId(INVALID_CACHE_ID)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     // Cache does not exist, create it instead
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     // Look for existing cache
     bool cacheFound;
-    nsresult rv = DBSchema::StorageGetCacheId(aConn, mNamespace, mKey,
+    nsresult rv = DBSchema::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
                                               &cacheFound, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     if (cacheFound) {
       return rv;
     }
 
     rv = DBSchema::CreateCache(aConn, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-    rv = DBSchema::StoragePutCache(aConn, mNamespace, mKey, mCacheId);
+    rv = DBSchema::StoragePutCache(aConn, mNamespace, mArgs.key(), mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
-    aListener->OnStorageOpen(mRequestId, aRv, mCacheId);
+    aListener->OnOpComplete(Move(aRv), StorageOpenResult(), mCacheId);
   }
 
 private:
   const Namespace mNamespace;
-  const nsString mKey;
+  const StorageOpenArgs mArgs;
   CacheId mCacheId;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageDeleteAction final : public Manager::BaseAction
 {
 public:
   StorageDeleteAction(Manager* aManager, ListenerId aListenerId,
-                      RequestId aRequestId, Namespace aNamespace,
-                      const nsAString& aKey)
-    : BaseAction(aManager, aListenerId, aRequestId)
+                      Namespace aNamespace, const StorageDeleteArgs& aArgs)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
-    , mKey(aKey)
+    , mArgs(aArgs)
     , mCacheDeleted(false)
     , mCacheId(INVALID_CACHE_ID)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     bool exists;
-    nsresult rv = DBSchema::StorageGetCacheId(aConn, mNamespace, mKey, &exists,
-                                              &mCacheId);
+    nsresult rv = DBSchema::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
+                                              &exists, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     if (!exists) {
       mCacheDeleted = false;
       return NS_OK;
     }
 
-    rv = DBSchema::StorageForgetCache(aConn, mNamespace, mKey);
+    rv = DBSchema::StorageForgetCache(aConn, mNamespace, mArgs.key());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     mCacheDeleted = true;
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (mCacheDeleted) {
       // If content is referencing this cache, mark it orphaned to be
       // deleted later.
       if (!mManager->SetCacheIdOrphanedIfRefed(mCacheId)) {
 
         // no outstanding references, delete immediately
         nsRefPtr<Context> context = mManager->CurrentContext();
         context->CancelForCacheId(mCacheId);
         nsRefPtr<Action> action =
           new DeleteOrphanedCacheAction(mManager, mCacheId);
         context->Dispatch(mManager->mIOThread, action);
       }
     }
 
-    aListener->OnStorageDelete(mRequestId, aRv, mCacheDeleted);
+    aListener->OnOpComplete(Move(aRv), StorageDeleteResult(mCacheDeleted));
   }
 
 private:
   const Namespace mNamespace;
-  const nsString mKey;
+  const StorageDeleteArgs mArgs;
   bool mCacheDeleted;
   CacheId mCacheId;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageKeysAction final : public Manager::BaseAction
 {
 public:
   StorageKeysAction(Manager* aManager, ListenerId aListenerId,
-                    RequestId aRequestId, Namespace aNamespace)
-    : BaseAction(aManager, aListenerId, aRequestId)
+                    Namespace aNamespace)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     return DBSchema::StorageGetKeys(aConn, mNamespace, mKeys);
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
-    if (NS_FAILED(aRv)) {
+    if (aRv.Failed()) {
       mKeys.Clear();
     }
-    aListener->OnStorageKeys(mRequestId, aRv, mKeys);
+    aListener->OnOpComplete(Move(aRv), StorageKeysResult(mKeys));
   }
 
 private:
   const Namespace mNamespace;
   nsTArray<nsString> mKeys;
 };
 
 // ----------------------------------------------------------------------------
 
 //static
 Manager::ListenerId Manager::sNextListenerId = 0;
 
+void
+Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult)
+{
+  OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
+               nsTArray<SavedRequest>(), nullptr);
+}
+
+void
+Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                                CacheId aOpenedCacheId)
+{
+  OnOpComplete(Move(aRv), aResult, aOpenedCacheId, nsTArray<SavedResponse>(),
+               nsTArray<SavedRequest>(), nullptr);
+}
+
+void
+Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                                const SavedResponse& aSavedResponse,
+                                StreamList* aStreamList)
+{
+  nsAutoTArray<SavedResponse, 1> responseList;
+  responseList.AppendElement(aSavedResponse);
+  OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, responseList,
+               nsTArray<SavedRequest>(), aStreamList);
+}
+
+void
+Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                                const nsTArray<SavedResponse>& aSavedResponseList,
+                                StreamList* aStreamList)
+{
+  OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, aSavedResponseList,
+               nsTArray<SavedRequest>(), aStreamList);
+}
+
+void
+Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                                const nsTArray<SavedRequest>& aSavedRequestList,
+                                StreamList* aStreamList)
+{
+  OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
+               aSavedRequestList, aStreamList);
+}
+
 // static
 nsresult
 Manager::GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
 {
   mozilla::ipc::AssertIsOnBackgroundThread();
   return Factory::GetOrCreate(aManagerId, aManagerOut);
 }
 
@@ -1555,205 +1580,125 @@ void
 Manager::RemoveStreamList(StreamList* aStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   MOZ_ASSERT(aStreamList);
   mStreamLists.RemoveElement(aStreamList);
 }
 
 void
-Manager::CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
-                    const PCacheRequest& aRequest,
-                    const PCacheQueryParams& aParams)
+Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
+                        const CacheOpArgs& aOpArgs)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   MOZ_ASSERT(aListener);
+  MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs);
+  MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
+
   if (mShuttingDown || !mValid) {
-    aListener->OnCacheMatch(aRequestId, NS_ERROR_FAILURE, nullptr, nullptr);
+    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
     return;
   }
-  nsRefPtr<Context> context = CurrentContext();
-  nsRefPtr<StreamList> streamList = new StreamList(this, context);
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new CacheMatchAction(this, listenerId, aRequestId,
-                                                 aCacheId, aRequest, aParams,
-                                                 streamList);
-  context->Dispatch(mIOThread, action);
-}
 
-void
-Manager::CacheMatchAll(Listener* aListener, RequestId aRequestId,
-                       CacheId aCacheId, const PCacheRequestOrVoid& aRequest,
-                       const PCacheQueryParams& aParams)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnCacheMatchAll(aRequestId, NS_ERROR_FAILURE,
-                               nsTArray<SavedResponse>(), nullptr);
-    return;
-  }
   nsRefPtr<Context> context = CurrentContext();
   nsRefPtr<StreamList> streamList = new StreamList(this, context);
   ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new CacheMatchAllAction(this, listenerId, aRequestId,
-                                                    aCacheId, aRequest, aParams,
-                                                    streamList);
-  context->Dispatch(mIOThread, action);
-}
 
-void
-Manager::CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
-                     const nsTArray<CacheRequestResponse>& aPutList,
-                     const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
-                     const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnCachePutAll(aRequestId, NS_ERROR_FAILURE);
-    return;
+  nsRefPtr<Action> action;
+  switch(aOpArgs.type()) {
+    case CacheOpArgs::TCacheMatchArgs:
+      action = new CacheMatchAction(this, listenerId, aCacheId,
+                                    aOpArgs.get_CacheMatchArgs(), streamList);
+      break;
+    case CacheOpArgs::TCacheMatchAllArgs:
+      action = new CacheMatchAllAction(this, listenerId, aCacheId,
+                                       aOpArgs.get_CacheMatchAllArgs(),
+                                       streamList);
+      break;
+    case CacheOpArgs::TCacheDeleteArgs:
+      action = new CacheDeleteAction(this, listenerId, aCacheId,
+                                     aOpArgs.get_CacheDeleteArgs());
+      break;
+    case CacheOpArgs::TCacheKeysArgs:
+      action = new CacheKeysAction(this, listenerId, aCacheId,
+                                   aOpArgs.get_CacheKeysArgs(), streamList);
+      break;
+    default:
+      MOZ_CRASH("Unknown Cache operation!");
   }
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new CachePutAllAction(this, listenerId, aRequestId,
-                                                  aCacheId, aPutList,
-                                                  aRequestStreamList,
-                                                  aResponseStreamList);
-  nsRefPtr<Context> context = CurrentContext();
-  context->Dispatch(mIOThread, action);
-}
 
-void
-Manager::CacheDelete(Listener* aListener, RequestId aRequestId,
-                     CacheId aCacheId, const PCacheRequest& aRequest,
-                     const PCacheQueryParams& aParams)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnCacheDelete(aRequestId, NS_ERROR_FAILURE, false);
-    return;
-  }
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new CacheDeleteAction(this, listenerId, aRequestId,
-                                                  aCacheId, aRequest, aParams);
-  nsRefPtr<Context> context = CurrentContext();
   context->Dispatch(mIOThread, action);
 }
 
 void
-Manager::CacheKeys(Listener* aListener, RequestId aRequestId,
-                   CacheId aCacheId, const PCacheRequestOrVoid& aRequestOrVoid,
-                   const PCacheQueryParams& aParams)
+Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
+                          const CacheOpArgs& aOpArgs)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   MOZ_ASSERT(aListener);
+
   if (mShuttingDown || !mValid) {
-    aListener->OnCacheKeys(aRequestId, NS_ERROR_FAILURE,
-                           nsTArray<SavedRequest>(), nullptr);
+    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
     return;
   }
+
   nsRefPtr<Context> context = CurrentContext();
   nsRefPtr<StreamList> streamList = new StreamList(this, context);
   ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new CacheKeysAction(this, listenerId, aRequestId,
-                                                aCacheId, aRequestOrVoid,
-                                                aParams, streamList);
-  context->Dispatch(mIOThread, action);
-}
 
-void
-Manager::StorageMatch(Listener* aListener, RequestId aRequestId,
-                      Namespace aNamespace, const PCacheRequest& aRequest,
-                      const PCacheQueryParams& aParams)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnStorageMatch(aRequestId, NS_ERROR_FAILURE,
-                              nullptr, nullptr);
-    return;
+  nsRefPtr<Action> action;
+  switch(aOpArgs.type()) {
+    case CacheOpArgs::TStorageMatchArgs:
+      action = new StorageMatchAction(this, listenerId, aNamespace,
+                                      aOpArgs.get_StorageMatchArgs(),
+                                      streamList);
+      break;
+    case CacheOpArgs::TStorageHasArgs:
+      action = new StorageHasAction(this, listenerId, aNamespace,
+                                    aOpArgs.get_StorageHasArgs());
+      break;
+    case CacheOpArgs::TStorageOpenArgs:
+      action = new StorageOpenAction(this, listenerId, aNamespace,
+                                     aOpArgs.get_StorageOpenArgs());
+      break;
+    case CacheOpArgs::TStorageDeleteArgs:
+      action = new StorageDeleteAction(this, listenerId, aNamespace,
+                                       aOpArgs.get_StorageDeleteArgs());
+      break;
+    case CacheOpArgs::TStorageKeysArgs:
+      action = new StorageKeysAction(this, listenerId, aNamespace);
+      break;
+    default:
+      MOZ_CRASH("Unknown CacheStorage operation!");
   }
-  nsRefPtr<Context> context = CurrentContext();
-  nsRefPtr<StreamList> streamList = new StreamList(this, context);
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new StorageMatchAction(this, listenerId, aRequestId,
-                                                   aNamespace, aRequest,
-                                                   aParams, streamList);
+
   context->Dispatch(mIOThread, action);
 }
 
 void
-Manager::StorageHas(Listener* aListener, RequestId aRequestId,
-                    Namespace aNamespace, const nsAString& aKey)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnStorageHas(aRequestId, NS_ERROR_FAILURE,
-                            false);
-    return;
-  }
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new StorageHasAction(this, listenerId, aRequestId,
-                                                 aNamespace, aKey);
-  nsRefPtr<Context> context = CurrentContext();
-  context->Dispatch(mIOThread, action);
-}
-
-void
-Manager::StorageOpen(Listener* aListener, RequestId aRequestId,
-                     Namespace aNamespace, const nsAString& aKey)
+Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
+                       const nsTArray<CacheRequestResponse>& aPutList,
+                       const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
+                       const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   MOZ_ASSERT(aListener);
+
   if (mShuttingDown || !mValid) {
-    aListener->OnStorageOpen(aRequestId, NS_ERROR_FAILURE, 0);
-    return;
-  }
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new StorageOpenAction(this, listenerId, aRequestId,
-                                                  aNamespace, aKey);
-  nsRefPtr<Context> context = CurrentContext();
-  context->Dispatch(mIOThread, action);
-}
-
-void
-Manager::StorageDelete(Listener* aListener, RequestId aRequestId,
-                       Namespace aNamespace, const nsAString& aKey)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnStorageDelete(aRequestId, NS_ERROR_FAILURE,
-                               false);
+    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
     return;
   }
+
+  nsRefPtr<Context> context = CurrentContext();
   ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new StorageDeleteAction(this, listenerId, aRequestId,
-                                                    aNamespace, aKey);
-  nsRefPtr<Context> context = CurrentContext();
-  context->Dispatch(mIOThread, action);
-}
 
-void
-Manager::StorageKeys(Listener* aListener, RequestId aRequestId,
-                     Namespace aNamespace)
-{
-  NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  if (mShuttingDown || !mValid) {
-    aListener->OnStorageKeys(aRequestId, NS_ERROR_FAILURE,
-                             nsTArray<nsString>());
-    return;
-  }
-  ListenerId listenerId = SaveListener(aListener);
-  nsRefPtr<Action> action = new StorageKeysAction(this, listenerId, aRequestId,
-                                                  aNamespace);
-  nsRefPtr<Context> context = CurrentContext();
+  nsRefPtr<Action> action = new CachePutAllAction(this, listenerId, aCacheId,
+                                                  aPutList, aRequestStreamList,
+                                                  aResponseStreamList);
+
   context->Dispatch(mIOThread, action);
 }
 
 Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
   : mManagerId(aManagerId)
   , mIOThread(aIOThread)
   , mContext(nullptr)
   , mShuttingDown(false)
--- a/dom/cache/Manager.h
+++ b/dom/cache/Manager.h
@@ -2,31 +2,32 @@
 /* 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_Manager_h
 #define mozilla_dom_cache_Manager_h
 
-#include "mozilla/dom/cache/CacheInitData.h"
 #include "mozilla/dom/cache/PCacheStreamControlParent.h"
 #include "mozilla/dom/cache/Types.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsIInputStream;
 class nsIThread;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+class CacheOpArgs;
+class CacheOpResult;
 class CacheRequestResponse;
 class Context;
 class ManagerId;
 class PCacheQueryParams;
 class PCacheRequest;
 class PCacheRequestOrVoid;
 struct SavedRequest;
 struct SavedResponse;
@@ -81,40 +82,46 @@ public:
   // Listeners must call Manager::RemoveListener() before they are destroyed
   // to clear these weak references.
   //
   // All public methods should be invoked on the same thread used to create
   // the Manager.
   class Listener
   {
   public:
-    virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
-                              const SavedResponse* aResponse,
-                              StreamList* aStreamList) { }
-    virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
-                                 const nsTArray<SavedResponse>& aSavedResponses,
-                                 StreamList* aStreamList) { }
-    virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) { }
-    virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
-                               bool aSuccess) { }
-    virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
-                             const nsTArray<SavedRequest>& aSavedRequests,
-                             StreamList* aStreamList) { }
+    // convenience routines
+    void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult);
+
+    void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                 CacheId aOpenedCacheId);
+
+    void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                 const SavedResponse& aSavedResponse,
+                 StreamList* aStreamList);
 
-    virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
-                                const SavedResponse* aResponse,
-                                StreamList* aStreamList) { }
-    virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
-                              bool aCacheFound) { }
-    virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
-                               CacheId aCacheId) { }
-    virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
-                                 bool aCacheDeleted) { }
-    virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
-                               const nsTArray<nsString>& aKeys) { }
+    void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                 const nsTArray<SavedResponse>& aSavedResponseList,
+                 StreamList* aStreamList);
+
+    void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                 const nsTArray<SavedRequest>& aSavedRequestList,
+                 StreamList* aStreamList);
+
+    // interface to be implemented
+    virtual void
+    OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                 CacheId aOpenedCacheId,
+                 const nsTArray<SavedResponse>& aSavedResponseList,
+                 const nsTArray<SavedRequest>& aSavedRequestList,
+                 StreamList* aStreamList) { }
 
   protected:
     ~Listener() { }
   };
 
   static nsresult GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut);
   static already_AddRefed<Manager> Get(ManagerId* aManagerId);
 
@@ -145,45 +152,25 @@ public:
 
   already_AddRefed<ManagerId> GetManagerId() const;
 
   // Methods to allow a StreamList to register themselves with the Manager.
   // StreamList objects must call RemoveStreamList() before they are destroyed.
   void AddStreamList(StreamList* aStreamList);
   void RemoveStreamList(StreamList* aStreamList);
 
-  // TODO: consider moving CacheId up in the argument lists below (bug 1110485)
-  void CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
-                  const PCacheRequest& aRequest,
-                  const PCacheQueryParams& aParams);
-  void CacheMatchAll(Listener* aListener, RequestId aRequestId,
-                     CacheId aCacheId, const PCacheRequestOrVoid& aRequestOrVoid,
-                     const PCacheQueryParams& aParams);
-  void CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
-                   const nsTArray<CacheRequestResponse>& aPutList,
-                   const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
-                   const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
-  void CacheDelete(Listener* aListener, RequestId aRequestId,
-                   CacheId aCacheId, const PCacheRequest& aRequest,
-                   const PCacheQueryParams& aParams);
-  void CacheKeys(Listener* aListener, RequestId aRequestId,
-                 CacheId aCacheId, const PCacheRequestOrVoid& aRequestOrVoid,
-                 const PCacheQueryParams& aParams);
+  void ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
+                      const CacheOpArgs& aOpArgs);
+  void ExecutePutAll(Listener* aListener, CacheId aCacheId,
+                     const nsTArray<CacheRequestResponse>& aPutList,
+                     const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
+                     const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
 
-  void StorageMatch(Listener* aListener, RequestId aRequestId,
-                    Namespace aNamespace, const PCacheRequest& aRequest,
-                    const PCacheQueryParams& aParams);
-  void StorageHas(Listener* aListener, RequestId aRequestId,
-                  Namespace aNamespace, const nsAString& aKey);
-  void StorageOpen(Listener* aListener, RequestId aRequestId,
-                   Namespace aNamespace, const nsAString& aKey);
-  void StorageDelete(Listener* aListener, RequestId aRequestId,
-                     Namespace aNamespace, const nsAString& aKey);
-  void StorageKeys(Listener* aListener, RequestId aRequestId,
-                   Namespace aNamespace);
+  void ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
+                        const CacheOpArgs& aOpArgs);
 
 private:
   class Factory;
   class BaseAction;
   class DeleteOrphanedCacheAction;
 
   class CacheMatchAction;
   class CacheMatchAllAction;
--- a/dom/cache/PCache.ipdl
+++ b/dom/cache/PCache.ipdl
@@ -1,50 +1,35 @@
 /* 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 PCacheOp;
 include protocol PCachePushStream;
 include PCacheTypes;
 include protocol PFileDescriptorSet;
 
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCacheStreamControl;
 
-using mozilla::dom::cache::RequestId from "mozilla/dom/cache/Types.h";
-using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
-include "mozilla/dom/cache/IPCUtils.h";
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCache
 {
   manager PBackground;
+  manages PCacheOp;
   manages PCachePushStream;
 
 parent:
+  PCacheOp(CacheOpArgs aOpArgs);
   PCachePushStream();
   Teardown();
-  Match(RequestId requestId, PCacheRequest request, PCacheQueryParams params);
-  MatchAll(RequestId requestId, PCacheRequestOrVoid request, PCacheQueryParams params);
-  AddAll(RequestId requestId, PCacheRequest[] requests);
-  Put(RequestId requestId, CacheRequestResponse aPut);
-  Delete(RequestId requestId, PCacheRequest request, PCacheQueryParams params);
-  Keys(RequestId requestId, PCacheRequestOrVoid request, PCacheQueryParams params);
 
 child:
-  MatchResponse(RequestId requestId, nsresult aRv, PCacheResponseOrVoid aResponse);
-  MatchAllResponse(RequestId requestId, nsresult aRv, PCacheResponse[] responses);
-  AddAllResponse(RequestId requestId, ErrorResult aRv);
-  PutResponse(RequestId requestId, nsresult aRv);
-  DeleteResponse(RequestId requestId, nsresult aRv, bool success);
-  KeysResponse(RequestId requestId, nsresult aRv, PCacheRequest[] requests);
-
-both:
   __delete__();
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/cache/PCacheOp.ipdl
@@ -0,0 +1,31 @@
+/* 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 PCacheStorage;
+
+// these are needed indirectly through CacheOpResult
+include protocol PCachePushStream;
+include protocol PCacheStreamControl;
+include protocol PFileDescriptorSet;
+
+include PCacheTypes;
+
+using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+protocol PCacheOp
+{
+  manager PCache or PCacheStorage;
+
+child:
+  __delete__(ErrorResult aRv, CacheOpResult aResult);
+};
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
--- a/dom/cache/PCacheStorage.ipdl
+++ b/dom/cache/PCacheStorage.ipdl
@@ -1,44 +1,33 @@
 /* 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 PCache;
+include protocol PCacheOp;
 include PCacheTypes;
 include protocol PFileDescriptorSet;
 
 include protocol PBlob; // FIXME: bug 792908
 include protocol PCacheStreamControl;
 
-using mozilla::dom::cache::RequestId from "mozilla/dom/cache/IPCUtils.h";
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCacheStorage
 {
   manager PBackground;
+  manages PCacheOp;
 
 parent:
+  PCacheOp(CacheOpArgs aOpArgs);
   Teardown();
-  Match(RequestId aRequestId, PCacheRequest aRequest,
-        PCacheQueryParams aParams);
-  Has(RequestId aRequestId, nsString aKey);
-  Open(RequestId aRequestId, nsString aKey);
-  Delete(RequestId aRequestId, nsString aKey);
-  Keys(RequestId aRequestId);
 
 child:
-  MatchResponse(RequestId aRequestId, nsresult aRv,
-                PCacheResponseOrVoid aResponseOrVoid);
-  HasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
-  OpenResponse(RequestId aRequestId, nsresult aRv, nullable PCache aActor);
-  DeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
-  KeysResponse(RequestId aRequestId, nsresult aRv, nsString[] aKeys);
   __delete__();
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/PCacheTypes.ipdlh
+++ b/dom/cache/PCacheTypes.ipdlh
@@ -1,12 +1,13 @@
 /* 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 PHeaders;
 include InputStreamParams;
 
 using HeadersGuardEnum from "mozilla/dom/FetchIPCUtils.h";
 using RequestCredentials from "mozilla/dom/FetchIPCUtils.h";
 using RequestMode from "mozilla/dom/FetchIPCUtils.h";
@@ -85,11 +86,154 @@ union PCacheResponseOrVoid
 };
 
 struct CacheRequestResponse
 {
   PCacheRequest request;
   PCacheResponse response;
 };
 
+struct CacheMatchArgs
+{
+  PCacheRequest request;
+  PCacheQueryParams params;
+};
+
+struct CacheMatchAllArgs
+{
+  PCacheRequestOrVoid requestOrVoid;
+  PCacheQueryParams params;
+};
+
+struct CacheAddAllArgs
+{
+  PCacheRequest[] requestList;
+};
+
+struct CachePutAllArgs
+{
+  CacheRequestResponse[] requestResponseList;
+};
+
+struct CacheDeleteArgs
+{
+  PCacheRequest request;
+  PCacheQueryParams params;
+};
+
+struct CacheKeysArgs
+{
+  PCacheRequestOrVoid requestOrVoid;
+  PCacheQueryParams params;
+};
+
+struct StorageMatchArgs
+{
+  PCacheRequest request;
+  PCacheQueryParams params;
+};
+
+struct StorageHasArgs
+{
+  nsString key;
+};
+
+struct StorageOpenArgs
+{
+  nsString key;
+};
+
+struct StorageDeleteArgs
+{
+  nsString key;
+};
+
+struct StorageKeysArgs
+{
+};
+
+union CacheOpArgs
+{
+  CacheMatchArgs;
+  CacheMatchAllArgs;
+  CacheAddAllArgs;
+  CachePutAllArgs;
+  CacheDeleteArgs;
+  CacheKeysArgs;
+  StorageMatchArgs;
+  StorageHasArgs;
+  StorageOpenArgs;
+  StorageDeleteArgs;
+  StorageKeysArgs;
+};
+
+struct CacheMatchResult
+{
+  PCacheResponseOrVoid responseOrVoid;
+};
+
+struct CacheMatchAllResult
+{
+  PCacheResponse[] responseList;
+};
+
+struct CacheAddAllResult
+{
+};
+
+struct CachePutAllResult
+{
+};
+
+struct CacheDeleteResult
+{
+  bool success;
+};
+
+struct CacheKeysResult
+{
+  PCacheRequest[] requestList;
+};
+
+struct StorageMatchResult
+{
+  PCacheResponseOrVoid responseOrVoid;
+};
+
+struct StorageHasResult
+{
+  bool success;
+};
+
+struct StorageOpenResult
+{
+  nullable PCache actor;
+};
+
+struct StorageDeleteResult
+{
+  bool success;
+};
+
+struct StorageKeysResult
+{
+  nsString[] keyList;
+};
+
+union CacheOpResult
+{
+  void_t;
+  CacheMatchResult;
+  CacheMatchAllResult;
+  CacheAddAllResult;
+  CachePutAllResult;
+  CacheDeleteResult;
+  CacheKeysResult;
+  StorageMatchResult;
+  StorageHasResult;
+  StorageOpenResult;
+  StorageDeleteResult;
+  StorageKeysResult;
+};
+
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/PrincipalVerifier.cpp
+++ b/dom/cache/PrincipalVerifier.cpp
@@ -41,44 +41,54 @@ PrincipalVerifier::CreateAndDispatch(Lis
                                                                aPrincipalInfo);
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(verifier)));
 
   return verifier.forget();
 }
 
 void
-PrincipalVerifier::ClearListener()
+PrincipalVerifier::AddListener(Listener* aListener)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mListener);
-  mListener = nullptr;
+  MOZ_ASSERT(aListener);
+  MOZ_ASSERT(!mListenerList.Contains(aListener));
+  mListenerList.AppendElement(aListener);
+}
+
+void
+PrincipalVerifier::RemoveListener(Listener* aListener)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aListener);
+  MOZ_ALWAYS_TRUE(mListenerList.RemoveElement(aListener));
 }
 
 PrincipalVerifier::PrincipalVerifier(Listener* aListener,
                                      PBackgroundParent* aActor,
                                      const PrincipalInfo& aPrincipalInfo)
-  : mListener(aListener)
-  , mActor(BackgroundParent::GetContentParent(aActor))
+  : mActor(BackgroundParent::GetContentParent(aActor))
   , mPrincipalInfo(aPrincipalInfo)
   , mInitiatingThread(NS_GetCurrentThread())
   , mResult(NS_OK)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mListener);
   MOZ_ASSERT(mInitiatingThread);
+  MOZ_ASSERT(aListener);
+
+  mListenerList.AppendElement(aListener);
 }
 
 PrincipalVerifier::~PrincipalVerifier()
 {
   // Since the PrincipalVerifier is a Runnable that executes on multiple
   // threads, its a race to see which thread de-refs us last.  Therefore
   // we cannot guarantee which thread we destruct on.
 
-  MOZ_ASSERT(!mListener);
+  MOZ_ASSERT(mListenerList.IsEmpty());
 
   // We should always be able to explicitly release the actor on the main
   // thread.
   MOZ_ASSERT(!mActor);
 }
 
 NS_IMETHODIMP
 PrincipalVerifier::Run()
@@ -167,27 +177,23 @@ PrincipalVerifier::VerifyOnMainThread()
 
   DispatchToInitiatingThread(NS_OK);
 }
 
 void
 PrincipalVerifier::CompleteOnInitiatingThread()
 {
   AssertIsOnBackgroundThread();
-
-  // This can happen if the listener is destroyed before we finish.  For
-  // example, if the child process OOMs and the actor is destroyed.
-  if (!mListener) {
-    return;
+  ListenerList::ForwardIterator iter(mListenerList);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnPrincipalVerified(mResult, mManagerId);
   }
 
-  mListener->OnPrincipalVerified(mResult, mManagerId);
-
-  // The listener must clear their reference in OnPrincipalVerified()
-  MOZ_ASSERT(!mListener);
+  // The listener must clear its reference in OnPrincipalVerified()
+  MOZ_ASSERT(mListenerList.IsEmpty());
 }
 
 void
 PrincipalVerifier::DispatchToInitiatingThread(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mResult = aRv;
--- a/dom/cache/PrincipalVerifier.h
+++ b/dom/cache/PrincipalVerifier.h
@@ -4,16 +4,17 @@
  * 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_PrincipalVerifier_h
 #define mozilla_dom_cache_PrincipalVerifier_h
 
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsThreadUtils.h"
+#include "nsTObserverArray.h"
 
 namespace mozilla {
 
 namespace ipc {
   class PBackgroundParent;
 }
 
 namespace dom {
@@ -21,44 +22,47 @@ namespace cache {
 
 class ManagerId;
 
 class PrincipalVerifier final : public nsRunnable
 {
 public:
   // An interface to be implemented by code wishing to use the
   // PrincipalVerifier.  Note, the Listener implementation is responsible
-  // for calling ClearListener() on the PrincipalVerifier to clear the
+  // for calling RemoveListener() on the PrincipalVerifier to clear the
   // weak reference.
   class Listener
   {
   public:
     virtual void OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId) = 0;
   };
 
   static already_AddRefed<PrincipalVerifier>
   CreateAndDispatch(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
                     const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
 
-  // The Listener must call ClearListener() when OnPrincipalVerified() is
+  void AddListener(Listener* aListener);
+
+  // The Listener must call RemoveListener() when OnPrincipalVerified() is
   // called or when the Listener is destroyed.
-  void ClearListener();
+  void RemoveListener(Listener* aListener);
 
 private:
   PrincipalVerifier(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
                     const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
   virtual ~PrincipalVerifier();
 
   void VerifyOnMainThread();
   void CompleteOnInitiatingThread();
 
   void DispatchToInitiatingThread(nsresult aRv);
 
-  // Weak reference cleared by ClearListener()
-  Listener* mListener;
+  // Weak reference cleared by RemoveListener()
+  typedef nsTObserverArray<Listener*> ListenerList;
+  ListenerList mListenerList;
 
   // set in originating thread at construction, but must be accessed and
   // released on main thread
   nsRefPtr<ContentParent> mActor;
 
   const mozilla::ipc::PrincipalInfo mPrincipalInfo;
   nsCOMPtr<nsIThread> mInitiatingThread;
   nsresult mResult;
--- a/dom/cache/Types.h
+++ b/dom/cache/Types.h
@@ -17,19 +17,17 @@ namespace dom {
 namespace cache {
 
 enum Namespace
 {
   DEFAULT_NAMESPACE,
   CHROME_ONLY_NAMESPACE,
   NUMBER_OF_NAMESPACES
 };
-
-typedef uintptr_t RequestId;
-static const RequestId INVALID_REQUEST_ID = 0;
+static const Namespace INVALID_NAMESPACE = NUMBER_OF_NAMESPACES;
 
 typedef int64_t CacheId;
 static const CacheId INVALID_CACHE_ID = -1;
 
 struct QuotaInfo
 {
   QuotaInfo() : mIsApp(false) { }
   nsCOMPtr<nsIFile> mDir;
--- a/dom/cache/moz.build
+++ b/dom/cache/moz.build
@@ -6,16 +6,18 @@
 
 EXPORTS.mozilla.dom.cache += [
     'Action.h',
     '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',
@@ -41,16 +43,18 @@ EXPORTS.mozilla.dom.cache += [
 ]
 
 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',
@@ -68,18 +72,18 @@ UNIFIED_SOURCES += [
     'ReadStream.cpp',
     'StreamControl.cpp',
     'StreamList.cpp',
     'StreamUtils.cpp',
     'TypeUtils.cpp',
 ]
 
 IPDL_SOURCES += [
-    'CacheInitData.ipdlh',
     'PCache.ipdl',
+    'PCacheOp.ipdl',
     'PCachePushStream.ipdl',
     'PCacheStorage.ipdl',
     'PCacheStreamControl.ipdl',
     'PCacheTypes.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')