Bug 1110485 P1 Refactor Cache IPC requests to use a separate actor. r=baku
☠☠ backed out by fb25d83dcaa0 ☠ ☠
authorBen Kelly <ben@wanderview.com>
Mon, 13 Apr 2015 14:05:57 -0700
changeset 238933 12deb121d126d59bec12953eecdf5bfe0d1af7bf
parent 238932 4d0b61eb4ae900b5c0d60d6721ae2e424a02cdeb
child 238934 fa29dbb49a668c61b13e7808176bf73afc35ae70
push id28577
push userryanvm@gmail.com
push dateTue, 14 Apr 2015 14:06:33 +0000
treeherdermozilla-central@388f5861dc7d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1110485
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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,129 +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, nsresult aRv)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    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
@@ -535,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,60 +68,35 @@ 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, nsresult aRv);
-  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,113 +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 nsresult& aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvAddAllResponse(requestId, aRv);
-  }
-  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 nsresult& aRv) 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,201 @@
+/* -*- 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 nsresult& aStatus,
+                             const CacheOpResult& aResult)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  if (NS_FAILED(aStatus)) {
+    mPromise->MaybeReject(aStatus);
+    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 nsresult& aStatus, 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,284 @@
+/* -*- 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, 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(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, aRv, void_t());
+    return;
+  }
+
+  Execute(aManagerId);
+}
+
+void
+CacheOpParent::OnOpComplete(nsresult 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 (NS_FAILED(aRv)) {
+    unused << Send__delete__(this, aRv, result.SendAsOpResult());
+    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);
+  }
+
+  unused << Send__delete__(this, aRv, result.SendAsOpResult());
+}
+
+void
+CacheOpParent::OnFetchPut(FetchPut* aFetchPut, nsresult aRv)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(aFetchPut);
+
+  aFetchPut->ClearListener();
+  MOZ_ALWAYS_TRUE(mFetchPutList.RemoveElement(aFetchPut));
+
+  OnOpComplete(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(nsresult 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, nsresult 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,35 +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::ipc::FileDescriptorSetParent;
-using mozilla::ipc::PFileDescriptorSetParent;
 
 // Declared in ActorUtils.h
 void
 DeallocPCacheParent(PCacheParent* aActor)
 {
   delete aActor;
 }
 
@@ -41,32 +30,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)
@@ -80,240 +95,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))) {
-    if (!SendAddAllResponse(aRequestId, rv)) {
-      // 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, nsresult 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,
-                          nsresult 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,92 @@ 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, 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
@@ -85,34 +85,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;
@@ -120,23 +119,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)
   , mResult(NS_OK)
 {
   MOZ_ASSERT(mListener);
   MOZ_ASSERT(mManager);
@@ -317,18 +314,18 @@ FetchPut::DoPutOnWorkerThread()
     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
@@ -413,19 +410,24 @@ FetchPut::MatchInPutList(const PCacheReq
       return true;
     }
   }
 
   return false;
 }
 
 void
-FetchPut::OnCachePutAll(RequestId aRequestId, nsresult aRv)
+FetchPut::OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                       CacheId aOpenedCacheId,
+                       const nsTArray<SavedResponse>& aSavedResponseList,
+                       const nsTArray<SavedRequest>& aSavedRequestList,
+                       StreamList* aStreamList)
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
+  MOZ_ASSERT(aResult.type() == CacheOpResult::TCachePutAllResult);
   MaybeSetError(aRv);
   MaybeNotifyListener();
 }
 
 void
 FetchPut::MaybeSetError(nsresult aRv)
 {
   if (NS_FAILED(mResult) || NS_SUCCEEDED(aRv)) {
@@ -436,17 +438,17 @@ FetchPut::MaybeSetError(nsresult aRv)
 
 void
 FetchPut::MaybeNotifyListener()
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
   if (!mListener) {
     return;
   }
-  mListener->OnFetchPut(this, mRequestId, mResult);
+  mListener->OnFetchPut(this, 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
@@ -34,22 +34,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, nsresult aRv) = 0;
+    OnFetchPut(FetchPut* aFetchPut, nsresult 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;
@@ -61,50 +60,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;
+
+  virtual void
+  OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+               CacheId aOpenedCacheId,
+               const nsTArray<SavedResponse>& aSavedResponseList,
+               const nsTArray<SavedRequest>& aSavedRequestList,
+               StreamList* aStreamList) override;
 
   void MaybeSetError(nsresult 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;
   nsresult mResult;
   nsCOMPtr<nsIRunnable> mRunnable;
 
 public:
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -404,21 +404,20 @@ 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;
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
@@ -430,17 +429,16 @@ protected:
     }
 
     // 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,
@@ -522,61 +518,58 @@ public:
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, nsresult aRv) override
   {
     if (!mFoundResponse) {
-      aListener->OnCacheMatch(mRequestId, aRv, nullptr, nullptr);
+      aListener->OnOpComplete(aRv, CacheMatchResult(void_t()));
     } else {
       mStreamList->Activate(mCacheId);
-      aListener->OnCacheMatch(mRequestId, aRv, &mResponse, mStreamList);
+      aListener->OnOpComplete(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;
@@ -591,50 +584,49 @@ public:
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, nsresult aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheMatchAll(mRequestId, aRv, mSavedResponses, mStreamList);
+    aListener->OnOpComplete(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(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
   {
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
-    aListener->OnCacheDelete(mRequestId, aRv, mSuccess);
+    aListener->OnOpComplete(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;
@@ -1090,57 +1075,56 @@ public:
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, nsresult aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheKeys(mRequestId, aRv, mSavedRequests, mStreamList);
+    aListener->OnOpComplete(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,
@@ -1152,156 +1136,153 @@ public:
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, nsresult aRv) override
   {
     if (!mFoundResponse) {
-      aListener->OnStorageMatch(mRequestId, aRv, nullptr, nullptr);
+      aListener->OnOpComplete(aRv, StorageMatchResult(void_t()));
     } else {
       mStreamList->Activate(mSavedResponse.mCacheId);
-      aListener->OnStorageMatch(mRequestId, aRv, &mSavedResponse, mStreamList);
+      aListener->OnOpComplete(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
   {
-    aListener->OnStorageHas(mRequestId, aRv, mCacheFound);
+    aListener->OnOpComplete(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
   {
-    aListener->OnStorageOpen(mRequestId, aRv, mCacheId);
+    aListener->OnOpComplete(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;
   }
@@ -1318,63 +1299,107 @@ public:
         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(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
   {
     if (NS_FAILED(aRv)) {
       mKeys.Clear();
     }
-    aListener->OnStorageKeys(mRequestId, aRv, mKeys);
+    aListener->OnOpComplete(aRv, StorageKeysResult(mKeys));
   }
 
 private:
   const Namespace mNamespace;
   nsTArray<nsString> mKeys;
 };
 
 // ----------------------------------------------------------------------------
 
 //static
 Manager::ListenerId Manager::sNextListenerId = 0;
 
+void
+Manager::Listener::OnOpComplete(nsresult aRv, const CacheOpResult& aResult)
+{
+  OnOpComplete(aRv, aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
+               nsTArray<SavedRequest>(), nullptr);
+}
+
+void
+Manager::Listener::OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                                CacheId aOpenedCacheId)
+{
+  OnOpComplete(aRv, aResult, aOpenedCacheId, nsTArray<SavedResponse>(),
+               nsTArray<SavedRequest>(), nullptr);
+}
+
+void
+Manager::Listener::OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                                const SavedResponse& aSavedResponse,
+                                StreamList* aStreamList)
+{
+  nsAutoTArray<SavedResponse, 1> responseList;
+  responseList.AppendElement(aSavedResponse);
+  OnOpComplete(aRv, aResult, INVALID_CACHE_ID, responseList,
+               nsTArray<SavedRequest>(), aStreamList);
+}
+
+void
+Manager::Listener::OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                                const nsTArray<SavedResponse>& aSavedResponseList,
+                                StreamList* aStreamList)
+{
+  OnOpComplete(aRv, aResult, INVALID_CACHE_ID, aSavedResponseList,
+               nsTArray<SavedRequest>(), aStreamList);
+}
+
+void
+Manager::Listener::OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                                const nsTArray<SavedRequest>& aSavedRequestList,
+                                StreamList* aStreamList)
+{
+  OnOpComplete(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(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(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(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(nsresult aRv, const CacheOpResult& aResult);
+
+    void
+    OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                 CacheId aOpenedCacheId);
+
+    void
+    OnOpComplete(nsresult 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(nsresult aRv, const CacheOpResult& aResult,
+                 const nsTArray<SavedResponse>& aSavedResponseList,
+                 StreamList* aStreamList);
+
+    void
+    OnOpComplete(nsresult aRv, const CacheOpResult& aResult,
+                 const nsTArray<SavedRequest>& aSavedRequestList,
+                 StreamList* aStreamList);
+
+    // interface to be implemented
+    virtual void
+    OnOpComplete(nsresult 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,49 +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";
-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, nsresult 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,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PCache;
+include protocol PCacheStorage;
+
+// these are needed indirectly through CacheOpResult
+include protocol PCachePushStream;
+include protocol PCacheStreamControl;
+include protocol PFileDescriptorSet;
+
+include PCacheTypes;
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+protocol PCacheOp
+{
+  manager PCache or PCacheStorage;
+
+child:
+  __delete__(nsresult aStatus, 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')