Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 17 Apr 2015 15:44:37 -0400
changeset 239736 a55f9bdb2bf4f8f6bf4119037386a4409345bc83
parent 239610 1997fbfa8a8ff3f1ecec6a58cd55c6bb6c29b61a (current diff)
parent 239735 10b09c1932142eba1049396720d3a6d36a9418ea (diff)
child 239737 fff4d44e1f41b0674819f9f06cc8727a9ed6ec23
push id12444
push userryanvm@gmail.com
push dateFri, 17 Apr 2015 20:04:42 +0000
treeherderfx-team@560a202db924 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
Merge inbound to m-c. a=merge
CLOBBER
dom/base/nsGkAtomList.h
dom/cache/CacheInitData.ipdlh
dom/cache/PCacheTypes.ipdlh
dom/cache/StreamUtils.cpp
dom/cache/StreamUtils.h
dom/fetch/FetchIPCUtils.h
dom/fetch/PHeaders.ipdlh
dom/ipc/ContentChild.cpp
media/libvpx/vp8/common/arm/neon/copymem_neon.c
media/libvpx/vp8/common/arm/neon/dc_only_idct_add_neon.c
media/libvpx/vp8/common/arm/neon/dequant_idct_neon.c
media/libvpx/vp8/common/arm/neon/dequantizeb_neon.c
media/libvpx/vp8/common/arm/neon/idct_dequant_0_2x_neon.c
media/libvpx/vp8/common/arm/neon/idct_dequant_full_2x_neon.c
media/libvpx/vp8/common/arm/neon/iwalsh_neon.c
media/libvpx/vp8/common/arm/neon/loopfilter_neon.c
media/libvpx/vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c
media/libvpx/vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c
media/libvpx/vp8/common/arm/neon/mbloopfilter_neon.c
media/libvpx/vp8/common/arm/neon/reconintra_neon.c
media/libvpx/vp8/common/arm/neon/sad_neon.c
media/libvpx/vp8/common/arm/neon/shortidct4x4llm_neon.c
media/libvpx/vp8/common/arm/neon/sixtappredict_neon.c
media/libvpx/vp8/common/arm/neon/variance_neon.c
media/libvpx/vp8/common/arm/neon/vp8_subpixelvariance_neon.c
media/libvpx/vp8/common/x86/loopfilter_block_sse2_x86_64.asm
media/libvpx/vp8/encoder/arm/neon/shortfdct_neon.c
media/libvpx/vp8/encoder/arm/neon/subtract_neon.c
media/libvpx/vp8/encoder/arm/neon/vp8_mse16x16_neon.c
media/libvpx/vp8/encoder/arm/neon/vp8_shortwalsh4x4_neon.c
media/libvpx/vp8/encoder/x86/quantize_sse4.c
media/libvpx/vp8/encoder/x86/quantize_ssse3.c
media/libvpx/vp8/encoder/x86/ssim_opt_x86_64.asm
media/libvpx/vp9/common/vp9_thread.c
media/libvpx/vp9/common/vp9_thread.h
media/libvpx/vp9/common/x86/vp9_high_intrapred_sse2.asm
media/libvpx/vp9/common/x86/vp9_high_loopfilter_intrin_sse2.c
media/libvpx/vp9/common/x86/vp9_high_subpixel_8t_sse2.asm
media/libvpx/vp9/common/x86/vp9_high_subpixel_bilinear_sse2.asm
media/libvpx/vp9/common/x86/vp9_idct_intrin_sse2.h
media/libvpx/vp9/common/x86/vp9_idct_intrin_ssse3.c
media/libvpx/vp9/common/x86/vp9_idct_ssse3_x86_64.asm
media/libvpx/vp9/common/x86/vp9_subpixel_8t_intrin_ssse3.c
media/libvpx/vp9/decoder/vp9_decoder.c
media/libvpx/vp9/decoder/vp9_decoder.h
media/libvpx/vp9/decoder/vp9_read_bit_buffer.c
media/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c
media/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c
media/libvpx/vp9/encoder/arm/neon/vp9_sad_neon.c
media/libvpx/vp9/encoder/arm/neon/vp9_subtract_neon.c
media/libvpx/vp9/encoder/arm/neon/vp9_variance_neon.c
media/libvpx/vp9/encoder/vp9_aq_complexity.c
media/libvpx/vp9/encoder/vp9_aq_complexity.h
media/libvpx/vp9/encoder/vp9_aq_cyclicrefresh.c
media/libvpx/vp9/encoder/vp9_aq_cyclicrefresh.h
media/libvpx/vp9/encoder/vp9_aq_variance.c
media/libvpx/vp9/encoder/vp9_aq_variance.h
media/libvpx/vp9/encoder/vp9_context_tree.c
media/libvpx/vp9/encoder/vp9_context_tree.h
media/libvpx/vp9/encoder/vp9_cost.c
media/libvpx/vp9/encoder/vp9_cost.h
media/libvpx/vp9/encoder/vp9_denoiser.c
media/libvpx/vp9/encoder/vp9_denoiser.h
media/libvpx/vp9/encoder/vp9_encoder.c
media/libvpx/vp9/encoder/vp9_encoder.h
media/libvpx/vp9/encoder/vp9_rd.c
media/libvpx/vp9/encoder/vp9_rd.h
media/libvpx/vp9/encoder/vp9_speed_features.c
media/libvpx/vp9/encoder/vp9_speed_features.h
media/libvpx/vp9/encoder/vp9_ssim.h
media/libvpx/vp9/encoder/vp9_svc_layercontext.c
media/libvpx/vp9/encoder/vp9_svc_layercontext.h
media/libvpx/vp9/encoder/vp9_write_bit_buffer.c
media/libvpx/vp9/encoder/x86/vp9_dct_mmx.asm
media/libvpx/vp9/encoder/x86/vp9_dct_ssse3_x86_64.asm
media/libvpx/vp9/encoder/x86/vp9_error_intrin_avx2.c
media/libvpx/vp9/encoder/x86/vp9_quantize_ssse3_x86_64.asm
media/libvpx/vp9/encoder/x86/vp9_sad4d_intrin_avx2.c
media/libvpx/vp9/encoder/x86/vp9_ssim_opt_x86_64.asm
media/libvpx/vp9/encoder/x86/vp9_subpel_variance_impl_intrin_avx2.c
media/libvpx/vpx/internal/vpx_psnr.h
media/libvpx/vpx/src/vpx_psnr.c
modules/libpref/init/all.js
testing/web-platform/meta/service-workers/cache-storage/window/cache-storage-keys.https.html.ini
toolkit/components/perfmonitoring/tests/browser/browser_AddonWatcher.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,13 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1155082: Merge BluetoothUUID.{cpp,h} variants into single file
-
-This patch set renames and removes source files. This requires updating
-the build system's dependency information from scratch. The issue has
-been reported in bug 1154232.
+Bug 1148639 - libvpx update moved a bunch of files around.
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -282,16 +282,17 @@ skip-if = buildapp == "mulet" || e10s # 
 [browser_bug970746.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug1015721.js]
 skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_bug1070778.js]
 [browser_canonizeURL.js]
 skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
+[browser_clipboard.js]
 [browser_contentAreaClick.js]
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
 [browser_ctrlTab.js]
 [browser_datareporting_notification.js]
 skip-if = !datareporting
 [browser_devedition.js]
 [browser_devices_get_user_media.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_clipboard.js
@@ -0,0 +1,123 @@
+// This test is used to check copy and paste in editable areas to ensure that non-text
+// types (html and images) are copied to and pasted from the clipboard properly.
+
+let testPage = "<div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>";
+
+add_task(function*() {
+  let tab = gBrowser.addTab();
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  gBrowser.selectedTab = tab;
+
+  yield promiseTabLoadEvent(tab, "data:text/html," + escape(testPage));
+  yield SimpleTest.promiseFocus(browser.contentWindowAsCPOW);
+
+  let results = yield ContentTask.spawn(browser, {}, function* () {
+    var doc = content.document;
+    var main = doc.getElementById("main");
+    main.focus();
+
+    const utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                         .getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+    const modifier = (content.navigator.platform.indexOf("Mac") >= 0) ?
+                     Components.interfaces.nsIDOMWindowUtils.MODIFIER_META :
+                     Components.interfaces.nsIDOMWindowUtils.MODIFIER_CONTROL;
+
+    function sendKey(key)
+    {
+     if (utils.sendKeyEvent("keydown", key, 0, modifier)) {
+       utils.sendKeyEvent("keypress", key, key.charCodeAt(0), modifier);
+     }
+     utils.sendKeyEvent("keyup", key, 0, modifier);
+    }
+
+    let results = [];
+    function is(l, r, v) {
+      results.push(((l === r) ? "PASSED" : "FAILED") + " got: " + l + " expected: " + r + " - " + v);
+    }
+
+    // Select an area of the text.
+    let selection = doc.getSelection();
+    selection.modify("move", "left", "line");
+    selection.modify("move", "right", "character");
+    selection.modify("move", "right", "character");
+    selection.modify("move", "right", "character");
+    selection.modify("extend", "right", "word");
+    selection.modify("extend", "right", "word");
+
+    yield new content.Promise((resolve, reject) => {
+      addEventListener("copy", function copyEvent(event) {
+        removeEventListener("copy", copyEvent, true);
+        // The data is empty as the selection is copied during the event default phase.
+        is(event.clipboardData.mozItemCount, 0, "Zero items on clipboard");
+        resolve();
+      }, true)
+
+      sendKey("c");
+    });
+
+    selection.modify("move", "right", "line");
+
+    yield new content.Promise((resolve, reject) => {
+      addEventListener("paste", function copyEvent(event) {
+        removeEventListener("paste", copyEvent, true);
+        let clipboardData = event.clipboardData; 
+        is(clipboardData.mozItemCount, 1, "One item on clipboard");
+        is(clipboardData.types.length, 2, "Two types on clipboard");
+        is(clipboardData.types[0], "text/html", "text/html on clipboard");
+        is(clipboardData.types[1], "text/plain", "text/plain on clipboard");
+        is(clipboardData.getData("text/html"), "t <b>Bold</b>", "text/html value");
+        is(clipboardData.getData("text/plain"), "t Bold", "text/plain value");
+        resolve();
+      }, true)
+      sendKey("v");
+    });
+
+    is(main.innerHTML, "Test <b>Bold</b> After Textt <b>Bold</b>", "Copy and paste html");
+
+    selection.modify("extend", "left", "word");
+    selection.modify("extend", "left", "word");
+    selection.modify("extend", "left", "character");
+
+    yield new content.Promise((resolve, reject) => {
+      addEventListener("cut", function copyEvent(event) {
+        removeEventListener("cut", copyEvent, true);
+        event.clipboardData.setData("text/plain", "Some text");
+        event.clipboardData.setData("text/html", "<i>Italic</i> ");
+        selection.deleteFromDocument();
+        event.preventDefault();
+        resolve();
+      }, true)
+      sendKey("x");
+    });
+
+    selection.modify("move", "left", "line");
+
+    yield new content.Promise((resolve, reject) => {
+      addEventListener("paste", function copyEvent(event) {
+        removeEventListener("paste", copyEvent, true);
+        let clipboardData = event.clipboardData; 
+        is(clipboardData.mozItemCount, 1, "One item on clipboard 2");
+        is(clipboardData.types.length, 2, "Two types on clipboard 2");
+        is(clipboardData.types[0], "text/html", "text/html on clipboard 2");
+        is(clipboardData.types[1], "text/plain", "text/plain on clipboard 2");
+        is(clipboardData.getData("text/html"), "<i>Italic</i> ", "text/html value 2");
+        is(clipboardData.getData("text/plain"), "Some text", "text/plain value 2");
+        resolve();
+      }, true)
+      sendKey("v");
+    });
+
+    is(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>", "Copy and paste html 2");
+    return results;
+  });
+
+  is(results.length, 15, "Correct number of results");
+  for (var t = 0; t < results.length; t++) {
+    ok(results[t].startsWith("PASSED"), results[t]);
+  }
+
+  gBrowser.removeCurrentTab();
+});
+
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -241,17 +241,17 @@ var gAdvancedPane = {
   setTelemetrySectionEnabled: function (aEnabled)
   {
 #ifdef MOZ_TELEMETRY_REPORTING
     // If FHR is disabled, additional data sharing should be disabled as well.
     let disabled = !aEnabled;
     document.getElementById("submitTelemetryBox").disabled = disabled;
     if (disabled) {
       // If we disable FHR, untick the telemetry checkbox.
-      document.getElementById("submitTelemetryBox").checked = false;
+      Services.prefs.setBoolPref("toolkit.telemetry.enabled", false);
     }
     document.getElementById("telemetryDataDesc").disabled = disabled;
 #endif
   },
 
 #ifdef MOZ_SERVICES_HEALTHREPORT
   /**
    * Initialize the health report service reference and checkbox.
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -270,17 +270,17 @@ var gAdvancedPane = {
   setTelemetrySectionEnabled: function (aEnabled)
   {
 #ifdef MOZ_TELEMETRY_REPORTING
     // If FHR is disabled, additional data sharing should be disabled as well.
     let disabled = !aEnabled;
     document.getElementById("submitTelemetryBox").disabled = disabled;
     if (disabled) {
       // If we disable FHR, untick the telemetry checkbox.
-      document.getElementById("submitTelemetryBox").checked = false;
+      Services.prefs.setBoolPref("toolkit.telemetry.enabled", false);
     }
     document.getElementById("telemetryDataDesc").disabled = disabled;
 #endif
   },
 
 #ifdef MOZ_SERVICES_HEALTHREPORT
   /**
    * Initialize the health report service reference and checkbox.
--- a/browser/components/preferences/in-content/tests/browser_telemetry.js
+++ b/browser/components/preferences/in-content/tests/browser_telemetry.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
+
 function runPaneTest(fn) {
   open_preferences((win) => {
     let doc = win.document;
     win.gotoPref("paneAdvanced");
     let advancedPrefs = doc.getElementById("advancedPrefs");
     let tab = doc.getElementById("dataChoicesTab");
     advancedPrefs.selectedTab = tab;
     fn(win, doc);
@@ -23,23 +25,28 @@ function test() {
 
 function testTelemetryState(win, doc) {
   let fhrCheckbox = doc.getElementById("submitHealthReportBox");
   Assert.ok(fhrCheckbox.checked, "Health Report checkbox is checked on app first run.");
 
   let telmetryCheckbox = doc.getElementById("submitTelemetryBox");
   Assert.ok(!telmetryCheckbox.disabled,
             "Telemetry checkbox must be enabled if FHR is checked.");
+  Assert.ok(Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED),
+            "Telemetry must be enabled if the checkbox is ticked.");
 
   // Uncheck the FHR checkbox and make sure that Telemetry checkbox gets disabled.
   fhrCheckbox.click();
 
   Assert.ok(telmetryCheckbox.disabled,
             "Telemetry checkbox must be disabled if FHR is unchecked.");
+  Assert.ok(!Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED),
+            "Telemetry must be disabled if the checkbox is unticked.");
 
   win.close();
   finish();
 }
 
 function resetPreferences() {
   Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
+  Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED);
 }
 
--- a/browser/components/preferences/tests/browser_telemetry.js
+++ b/browser/components/preferences/tests/browser_telemetry.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
+
 function runPaneTest(fn) {
   function observer(win, topic, data) {
     Services.obs.removeObserver(observer, "advanced-pane-loaded");
 
     let policy = Components.classes["@mozilla.org/datareporting/service;1"]
                                    .getService(Components.interfaces.nsISupports)
                                    .wrappedJSObject
                                    .policy;
@@ -35,27 +37,32 @@ function testTelemetryState(win) {
   let doc = win.document;
 
   let fhrCheckbox = doc.getElementById("submitHealthReportBox");
   Assert.ok(fhrCheckbox.checked, "Health Report checkbox is checked on app first run.");
 
   let telmetryCheckbox = doc.getElementById("submitTelemetryBox");
   Assert.ok(!telmetryCheckbox.disabled,
             "Telemetry checkbox must be enabled if FHR is checked.");
+  Assert.ok(Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED),
+            "Telemetry must be enabled if the checkbox is ticked.");
 
   // Uncheck the FHR checkbox and make sure that Telemetry checkbox gets disabled.
   fhrCheckbox.click();
 
   Assert.ok(telmetryCheckbox.disabled,
             "Telemetry checkbox must be disabled if FHR is unchecked.");
+  Assert.ok(!Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED),
+            "Telemetry must be disabled if the checkbox is unticked.");
 
   win.close();
   finish();
 }
 
 function resetPreferences() {
   let service = Cc["@mozilla.org/datareporting/service;1"]
                   .getService(Ci.nsISupports)
                   .wrappedJSObject;
   service.policy._prefs.resetBranch("datareporting.policy.");
   service.policy.dataSubmissionPolicyBypassNotification = true;
+  Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED);
 }
 
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -238,41 +238,46 @@ def check_style():
     #
     # Examples (filename -> inclname)
     # - "mfbt/Attributes.h"     -> "mozilla/Attributes.h"
     # - "mfbt/decimal/Decimal.h -> "mozilla/Decimal.h"
     # - "js/public/Vector.h"    -> "js/Vector.h"
     # - "js/src/vm/String.h"    -> "vm/String.h"
 
     mfbt_inclnames = set()      # type: set(inclname)
+    mozalloc_inclnames = set()  # type: set(inclname)
     js_names = dict()           # type: dict(filename, inclname)
 
     # Select the appropriate files.
     for filename in get_all_filenames():
         if filename.startswith('mfbt/') and filename.endswith('.h'):
             inclname = 'mozilla/' + filename.split('/')[-1]
             mfbt_inclnames.add(inclname)
 
+        if filename.startswith('memory/mozalloc/') and filename.endswith('.h'):
+            inclname = 'mozilla/' + filename.split('/')[-1]
+            mozalloc_inclnames.add(inclname)
+
         if filename.startswith('js/public/') and filename.endswith('.h'):
             inclname = 'js/' + filename[len('js/public/'):]
             js_names[filename] = inclname
 
         if filename.startswith('js/src/') and \
            not filename.startswith(tuple(ignored_js_src_dirs)) and \
            filename.endswith(('.c', '.cpp', '.h', '.tbl', '.msg')):
             inclname = filename[len('js/src/'):]
             js_names[filename] = inclname
 
-    all_inclnames = mfbt_inclnames | set(js_names.values())
+    all_inclnames = mfbt_inclnames | mozalloc_inclnames | set(js_names.values())
 
     edges = dict()      # type: dict(inclname, set(inclname))
 
-    # We don't care what's inside the MFBT files, but because they are
-    # #included from JS files we have to add them to the inclusion graph.
-    for inclname in mfbt_inclnames:
+    # We don't care what's inside the MFBT and MOZALLOC files, but because they
+    # are #included from JS files we have to add them to the inclusion graph.
+    for inclname in mfbt_inclnames | mozalloc_inclnames:
         edges[inclname] = set()
 
     # Process all the JS files.
     for filename in js_names.keys():
         inclname = js_names[filename]
         file_kind = FileKind.get(filename)
         if file_kind == FileKind.C or file_kind == FileKind.CPP or \
            file_kind == FileKind.H or file_kind == FileKind.INL_H:
--- a/configure.in
+++ b/configure.in
@@ -3431,30 +3431,16 @@ dnl See if compiler supports some gcc-st
 
 AC_CACHE_CHECK(for __attribute__((always_inline)),
                ac_cv_attribute_always_inline,
                [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));],
                                [],
                                ac_cv_attribute_always_inline=yes,
                                ac_cv_attribute_always_inline=no)])
 
-AC_CACHE_CHECK(for __attribute__((malloc)),
-               ac_cv_attribute_malloc,
-               [AC_TRY_COMPILE([void* f(int) __attribute__((malloc));],
-                               [],
-                               ac_cv_attribute_malloc=yes,
-                               ac_cv_attribute_malloc=no)])
-
-AC_CACHE_CHECK(for __attribute__((warn_unused_result)),
-               ac_cv_attribute_warn_unused,
-               [AC_TRY_COMPILE([int f(void) __attribute__((warn_unused_result));],
-                               [],
-                               ac_cv_attribute_warn_unused=yes,
-                               ac_cv_attribute_warn_unused=no)])
-
 dnl End of C++ language/feature checks
 AC_LANG_C
 
 dnl ========================================================
 dnl =  Internationalization checks
 dnl ========================================================
 dnl
 dnl Internationalization and Locale support is different
@@ -3476,45 +3462,31 @@ AC_HAVE_FUNCS(localeconv)
 fi # ! SKIP_COMPILER_CHECKS
 
 TARGET_XPCOM_ABI=
 if test -n "${CPU_ARCH}" -a -n "${TARGET_COMPILER_ABI}"; then
     TARGET_XPCOM_ABI="${CPU_ARCH}-${TARGET_COMPILER_ABI}"
     AC_DEFINE_UNQUOTED(TARGET_XPCOM_ABI, ["${TARGET_XPCOM_ABI}"])
 fi
 
-dnl Mozilla specific options
-dnl ========================================================
-dnl The macros used for command line options
-dnl are defined in build/autoconf/altoptions.m4.
-
-dnl If the compiler supports these attributes, define them as
-dnl convenience macros.
-if test "$ac_cv_attribute_malloc" = yes ; then
-  AC_DEFINE(NS_ATTR_MALLOC, [__attribute__((malloc))])
-else
-  AC_DEFINE(NS_ATTR_MALLOC,)
-fi
-
-if test "$ac_cv_attribute_warn_unused" = yes ; then
-  AC_DEFINE(NS_WARN_UNUSED_RESULT, [__attribute__((warn_unused_result))])
-else
-  AC_DEFINE(NS_WARN_UNUSED_RESULT,)
-fi
-
 dnl We can't run TRY_COMPILE tests on Windows, so hard-code some
 dnl features that Windows actually does support.
 
 if test -n "$SKIP_COMPILER_CHECKS"; then
    dnl Windows has malloc.h
    AC_DEFINE(MALLOC_H, [<malloc.h>])
    AC_DEFINE(HAVE_FORCEINLINE)
    AC_DEFINE(HAVE_LOCALECONV)
 fi # SKIP_COMPILER_CHECKS
 
+dnl Mozilla specific options
+dnl ========================================================
+dnl The macros used for command line options
+dnl are defined in build/autoconf/altoptions.m4.
+
 dnl ========================================================
 dnl =
 dnl = Check for external package dependencies
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(External Packages)
 
 MOZ_ARG_WITH_STRING(libxul-sdk,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5018,46 +5018,59 @@ nsDocShell::DisplayLoadError(nsresult aE
       if (nsserr) {
         nsserr->GetErrorMessage(aError, messageStr);
       }
     }
     if (!messageStr.IsEmpty()) {
       if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
         error.AssignLiteral("nssBadCert");
 
-        // if this is a Strict-Transport-Security host and the cert
-        // is bad, don't allow overrides (STS Spec section 7.3).
-        uint32_t type = nsISiteSecurityService::HEADER_HSTS;
+        // If this is an HTTP Strict Transport Security host or a pinned host
+        // and the certificate is bad, don't allow overrides (RFC 6797 section
+        // 12.1, HPKP draft spec section 2.6).
         uint32_t flags =
           mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
         bool isStsHost = false;
+        bool isPinnedHost = false;
         if (XRE_GetProcessType() == GeckoProcessType_Default) {
           nsCOMPtr<nsISiteSecurityService> sss =
             do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
           NS_ENSURE_SUCCESS(rv, rv);
-          rv = sss->IsSecureURI(type, aURI, flags, &isStsHost);
+          rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
+                                flags, &isStsHost);
+          NS_ENSURE_SUCCESS(rv, rv);
+          rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
+                                flags, &isPinnedHost);
           NS_ENSURE_SUCCESS(rv, rv);
         } else {
           mozilla::dom::ContentChild* cc =
             mozilla::dom::ContentChild::GetSingleton();
           mozilla::ipc::URIParams uri;
           SerializeURI(aURI, uri);
-          cc->SendIsSecureURI(type, uri, flags, &isStsHost);
+          cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
+                              &isStsHost);
+          cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
+                              &isPinnedHost);
         }
 
         if (Preferences::GetBool(
               "browser.xul.error_pages.expert_bad_cert", false)) {
           cssClass.AssignLiteral("expertBadCert");
         }
 
-        // HSTS takes precedence over the expert bad cert pref. We
-        // never want to show the "Add Exception" button for HSTS sites.
+        // HSTS/pinning takes precedence over the expert bad cert pref. We
+        // never want to show the "Add Exception" button for these sites.
+        // In the future we should differentiate between an HSTS host and a
+        // pinned host and display a more informative message to the user.
+        if (isStsHost || isPinnedHost) {
+          cssClass.AssignLiteral("badStsCert");
+        }
+
         uint32_t bucketId;
         if (isStsHost) {
-          cssClass.AssignLiteral("badStsCert");
           // measuring STS separately allows us to measure click through
           // rates easily
           bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
         } else {
           bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
         }
 
         // See if an alternate cert error page is registered
@@ -11456,17 +11469,17 @@ nsDocShell::AddState(JS::Handle<JS::Valu
   {
     nsCOMPtr<nsIDocument> origDocument = GetDocument();
     if (!origDocument) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
     nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
 
     scContainer = new nsStructuredCloneContainer();
-    rv = scContainer->InitFromJSVal(aData);
+    rv = scContainer->InitFromJSVal(aData, aCx);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocument> newDocument = GetDocument();
     if (!newDocument) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
     nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
 
--- a/dom/alarm/test/mochitest.ini
+++ b/dom/alarm/test/mochitest.ini
@@ -1,11 +1,9 @@
 [DEFAULT]
-skip-if = e10s
-
 support-files =
   file_empty.html
 
 [test_alarm_add_data.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_add_date.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_add_respectTimezone.html]
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -451,17 +451,17 @@ nsChildContentList::IndexOf(nsIContent* 
 
 nsIHTMLCollection*
 FragmentOrElement::Children()
 {
   FragmentOrElement::nsDOMSlots *slots = DOMSlots();
 
   if (!slots->mChildrenList) {
     slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard, 
-                                             nsGkAtoms::_asterix, nsGkAtoms::_asterix,
+                                             nsGkAtoms::_asterisk, nsGkAtoms::_asterisk,
                                              false);
   }
 
   return slots->mChildrenList;
 }
 
 
 //----------------------------------------------------------------------
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -12,17 +12,16 @@
 #ifndef FragmentOrElement_h___
 #define FragmentOrElement_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsAttrAndChildArray.h"          // member
 #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
 #include "nsIContent.h"                   // base class
-#include "nsINodeList.h"                  // base class
 #include "nsIWeakReference.h"             // base class
 #include "nsNodeUtils.h"                  // class member nsNodeUtils::CloneNodeImpl
 #include "nsIHTMLCollection.h"
 
 class ContentUnbinder;
 class nsContentList;
 class nsDOMAttributeMap;
 class nsDOMTokenList;
@@ -34,60 +33,16 @@ class nsIURI;
 
 namespace mozilla {
 namespace dom {
 class Element;
 }
 }
 
 /**
- * Class that implements the nsIDOMNodeList interface (a list of children of
- * the content), by holding a reference to the content and delegating GetLength
- * and Item to its existing child list.
- * @see nsIDOMNodeList
- */
-class nsChildContentList final : public nsINodeList
-{
-public:
-  explicit nsChildContentList(nsINode* aNode)
-    : mNode(aNode)
-  {
-  }
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsChildContentList)
-
-  // nsWrapperCache
-  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
-
-  // nsIDOMNodeList interface
-  NS_DECL_NSIDOMNODELIST
-
-  // nsINodeList interface
-  virtual int32_t IndexOf(nsIContent* aContent) override;
-  virtual nsIContent* Item(uint32_t aIndex) override;
-
-  void DropReference()
-  {
-    mNode = nullptr;
-  }
-
-  virtual nsINode* GetParentObject() override
-  {
-    return mNode;
-  }
-
-private:
-  ~nsChildContentList() {}
-
-  // The node whose children make up the list (weak reference)
-  nsINode* mNode;
-};
-
-/**
  * A class that implements nsIWeakReference
  */
 
 class nsNodeWeakReference final : public nsIWeakReference
 {
 public:
   explicit nsNodeWeakReference(nsINode* aNode)
     : mNode(aNode)
@@ -181,18 +136,18 @@ public:
   {
     return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
   }
   virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
                               bool aNotify) override;
   virtual bool TextIsOnlyWhitespace() override;
   virtual bool HasTextForTranslation() override;
   virtual void AppendTextTo(nsAString& aResult) override;
-  virtual bool AppendTextTo(nsAString& aResult,
-                            const mozilla::fallible_t&) override NS_WARN_UNUSED_RESULT; 
+  MOZ_WARN_UNUSED_RESULT
+  virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) override;
   virtual nsIContent *GetBindingParent() const override;
   virtual nsXBLBinding *GetXBLBinding() const override;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) override;
   virtual ShadowRoot *GetShadowRoot() const override;
   virtual ShadowRoot *GetContainingShadow() const override;
   virtual nsTArray<nsIContent*> &DestInsertionPoints() override;
   virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const override;
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1154598.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body><body>
+<script>
+s = document.createElement('script');
+s.src="";
+document.body.appendChild(s);
+</script>
+</body>
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -195,8 +195,10 @@ load 973401.html
 load 978646.html
 pref(dom.webcomponents.enabled,true) load 1027461-1.html
 pref(dom.webcomponents.enabled,true) load 1024428-1.html
 load 1026714.html
 pref(dom.webcomponents.enabled,true) load 1029710.html
 HTTP(..) load xhr_abortinprogress.html
 load xhr_empty_datauri.html
 load xhr_html_nullresponse.html
+load structured_clone_container_throws.html
+load 1154598.xhtml
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/structured_clone_container_throws.html
@@ -0,0 +1,9 @@
+<iframe></iframe>
+<script>
+try { frames[0].history.pushState({ dummy: function() {} }, ''); }
+catch (e) {}
+var doc = frames[0].document;
+var s = doc.createElement("script");
+s.textContent = "1";
+doc.body.appendChild(s);
+</script>
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -412,16 +412,17 @@ LOCAL_INCLUDES += [
     '/dom/ipc',
     '/dom/storage',
     '/dom/svg',
     '/dom/workers',
     '/dom/xbl',
     '/dom/xml',
     '/dom/xslt/xpath',
     '/dom/xul',
+    '/gfx/2d',
     '/image/src',
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
     '/layout/base',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
     '/layout/xul',
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -399,18 +399,18 @@ nsContentList::nsContentList(nsINode* aR
     mFunc(nullptr),
     mDestroyFunc(nullptr),
     mData(nullptr),
     mState(LIST_DIRTY),
     mDeep(aDeep),
     mFuncMayDependOnAttr(false)
 {
   NS_ASSERTION(mRootNode, "Must have root");
-  if (nsGkAtoms::_asterix == mHTMLMatchAtom) {
-    NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterix, "HTML atom and XML atom are not both asterix?");
+  if (nsGkAtoms::_asterisk == mHTMLMatchAtom) {
+    NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterisk, "HTML atom and XML atom are not both asterisk?");
     mMatchAll = true;
   }
   else {
     mMatchAll = false;
   }
   mRootNode->AddMutationObserver(this);
 
   // We only need to flush if we're in an non-HTML document, since the
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -166,16 +166,17 @@
 #include "nsParserCIID.h"
 #include "nsParserConstants.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsReferencedElement.h"
 #include "nsSandboxFlags.h"
 #include "nsScriptSecurityManager.h"
+#include "nsStreamUtils.h"
 #include "nsSVGFeatures.h"
 #include "nsTextEditorState.h"
 #include "nsTextFragment.h"
 #include "nsTextNode.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsViewManager.h"
@@ -7214,92 +7215,179 @@ nsContentUtils::CallOnAllRemoteChildren(
 
 void
 nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
                                                 nsTArray<IPCDataTransfer>& aIPC,
                                                 mozilla::dom::nsIContentChild* aChild,
                                                 mozilla::dom::nsIContentParent* aParent)
 {
   aIPC.Clear();
-  MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
   if (aTransferables) {
     uint32_t transferableCount = 0;
     aTransferables->Count(&transferableCount);
     for (uint32_t i = 0; i < transferableCount; ++i) {
       IPCDataTransfer* dt = aIPC.AppendElement();
       nsCOMPtr<nsISupports> genericItem;
       aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
-      nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
-      if (item) {
-        nsCOMPtr<nsISupportsArray> flavorList;
-        item->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
-        if (flavorList) {
-          uint32_t flavorCount = 0;
-          flavorList->Count(&flavorCount);
-          for (uint32_t j = 0; j < flavorCount; ++j) {
-            nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
-            if (!flavor) {
-              continue;
+      nsCOMPtr<nsITransferable> transferable(do_QueryInterface(genericItem));
+      TransferableToIPCTransferable(transferable, dt, aChild, aParent);
+    }
+  }
+}
+
+void
+nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
+                                              IPCDataTransfer* aIPCDataTransfer,
+                                              mozilla::dom::nsIContentChild* aChild,
+                                              mozilla::dom::nsIContentParent* aParent)
+{
+  MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
+
+  if (aTransferable) {
+    nsCOMPtr<nsISupportsArray> flavorList;
+    aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
+    if (flavorList) {
+      uint32_t flavorCount = 0;
+      flavorList->Count(&flavorCount);
+      for (uint32_t j = 0; j < flavorCount; ++j) {
+        nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
+        if (!flavor) {
+          continue;
+        }
+
+        nsAutoCString flavorStr;
+        flavor->GetData(flavorStr);
+        if (!flavorStr.Length()) {
+          continue;
+        }
+
+        nsCOMPtr<nsISupports> data;
+        uint32_t dataLen = 0;
+        aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
+
+        nsCOMPtr<nsISupportsString> text = do_QueryInterface(data);
+        nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data);
+        if (text) {
+          nsAutoString dataAsString;
+          text->GetData(dataAsString);
+          IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+          item->flavor() = nsCString(flavorStr);
+          item->data() = nsString(dataAsString);
+        } else if (ctext) {
+          nsAutoCString dataAsString;
+          ctext->GetData(dataAsString);
+          IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+          item->flavor() = nsCString(flavorStr);
+          item->data() = nsCString(dataAsString);
+        } else {
+          nsCOMPtr<nsISupportsInterfacePointer> sip =
+            do_QueryInterface(data);
+          if (sip) {
+            sip->GetData(getter_AddRefs(data));
+          }
+
+          // Images to be pasted on the clipboard are nsIInputStreams
+          nsCOMPtr<nsIInputStream> stream(do_QueryInterface(data));
+          if (stream) {
+            IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+            item->flavor() = nsCString(flavorStr);
+
+            nsCString imageData;
+            NS_ConsumeStream(stream, UINT32_MAX, imageData);
+            item->data() = imageData;
+            continue;
+          }
+
+          // Images to be placed on the clipboard are imgIContainers.
+          nsCOMPtr<imgIContainer> image(do_QueryInterface(data));
+          if (image) {
+            RefPtr<mozilla::gfx::SourceSurface> surface =
+              image->GetFrame(imgIContainer::FRAME_CURRENT,
+                              imgIContainer::FLAG_SYNC_DECODE);
+
+            mozilla::RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
+              surface->GetDataSurface();
+            size_t length;
+            int32_t stride;
+            mozilla::UniquePtr<char[]> surfaceData =
+              nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
+            
+            IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+            item->flavor() = nsCString(flavorStr);
+            item->data() = nsCString(surfaceData.get(), length);
+
+            IPCDataTransferImage& imageDetails = item->imageDetails();
+            mozilla::gfx::IntSize size = dataSurface->GetSize();
+            imageDetails.width() = size.width;
+            imageDetails.height() = size.height;
+            imageDetails.stride() = stride;
+            imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat());
+
+            continue;
+          }
+
+          // Otherwise, handle this as a file.
+          nsCOMPtr<FileImpl> fileImpl;
+          nsCOMPtr<nsIFile> file = do_QueryInterface(data);
+          if (file) {
+            fileImpl = new FileImplFile(file, false);
+            ErrorResult rv;
+            fileImpl->GetSize(rv);
+            fileImpl->GetLastModified(rv);
+          } else {
+            fileImpl = do_QueryInterface(data);
+          }
+          if (fileImpl) {
+            IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+            item->flavor() = nsCString(flavorStr);
+            if (aChild) {
+              item->data() =
+                mozilla::dom::BlobChild::GetOrCreate(aChild,
+                  static_cast<FileImpl*>(fileImpl.get()));
+            } else if (aParent) {
+              item->data() =
+                mozilla::dom::BlobParent::GetOrCreate(aParent,
+                  static_cast<FileImpl*>(fileImpl.get()));
             }
-
-            nsAutoCString flavorStr;
-            flavor->GetData(flavorStr);
-            if (!flavorStr.Length()) {
-              continue;
-            }
-
-            nsCOMPtr<nsISupports> data;
-            uint32_t dataLen = 0;
-            item->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
-
-            nsCOMPtr<nsISupportsString> text = do_QueryInterface(data);
-            if (text) {
-              nsAutoString dataAsString;
-              text->GetData(dataAsString);
-              IPCDataTransferItem* item = dt->items().AppendElement();
+          } else {
+            // This is a hack to support kFilePromiseMime.
+            // On Windows there just needs to be an entry for it, 
+            // and for OSX we need to create
+            // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
+            if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
+              IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
               item->flavor() = nsCString(flavorStr);
-              item->data() = nsString(dataAsString);
-            } else {
-              nsCOMPtr<nsISupportsInterfacePointer> sip =
-                do_QueryInterface(data);
-              if (sip) {
-                sip->GetData(getter_AddRefs(data));
-              }
-              nsCOMPtr<FileImpl> fileImpl;
-              nsCOMPtr<nsIFile> file = do_QueryInterface(data);
-              if (file) {
-                fileImpl = new FileImplFile(file, false);
-                ErrorResult rv;
-                fileImpl->GetSize(rv);
-                fileImpl->GetLastModified(rv);
-              } else {
-                fileImpl = do_QueryInterface(data);
-              }
-              if (fileImpl) {
-                IPCDataTransferItem* item = dt->items().AppendElement();
-                item->flavor() = nsCString(flavorStr);
-                if (aChild) {
-                  item->data() =
-                    mozilla::dom::BlobChild::GetOrCreate(aChild,
-                      static_cast<FileImpl*>(fileImpl.get()));
-                } else if (aParent) {
-                  item->data() =
-                    mozilla::dom::BlobParent::GetOrCreate(aParent,
-                      static_cast<FileImpl*>(fileImpl.get()));
-                }
-              } else {
-                // This is a hack to support kFilePromiseMime.
-                // On Windows there just needs to be an entry for it, 
-                // and for OSX we need to create
-                // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
-                if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
-                  IPCDataTransferItem* item = dt->items().AppendElement();
-                  item->flavor() = nsCString(flavorStr);
-                  item->data() = NS_ConvertUTF8toUTF16(flavorStr);
-                }
-              }
+              item->data() = NS_ConvertUTF8toUTF16(flavorStr);
             }
           }
         }
       }
     }
   }
 }
+
+mozilla::UniquePtr<char[]>
+nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+                               size_t* aLength, int32_t* aStride)
+{
+  mozilla::gfx::DataSourceSurface::MappedSurface map;
+  aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map);
+  mozilla::gfx::IntSize size = aSurface->GetSize();
+  mozilla::CheckedInt32 requiredBytes =
+    mozilla::CheckedInt32(map.mStride) * mozilla::CheckedInt32(size.height);
+  size_t maxBufLen = requiredBytes.isValid() ? requiredBytes.value() : 0;
+  mozilla::gfx::SurfaceFormat format = aSurface->GetFormat();
+
+  // Surface data handling is totally nuts. This is the magic one needs to
+  // know to access the data.
+  size_t bufLen = maxBufLen - map.mStride + (size.width * BytesPerPixel(format));
+
+  // nsDependentCString wants null-terminated string.
+  mozilla::UniquePtr<char[]> surfaceData(new char[maxBufLen + 1]);
+  memcpy(surfaceData.get(), reinterpret_cast<char*>(map.mData), bufLen);
+  memset(surfaceData.get() + bufLen, 0, maxBufLen - bufLen + 1);
+
+  *aLength = maxBufLen;
+  *aStride = map.mStride;
+
+  aSurface->Unmap();
+  return surfaceData;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -98,16 +98,17 @@ class nsPIDOMWindow;
 class nsPresContext;
 class nsScriptObjectTracer;
 class nsStringBuffer;
 class nsStringHashKey;
 class nsTextFragment;
 class nsViewportInfo;
 class nsWrapperCache;
 class nsAttrValue;
+class nsITransferable;
 
 struct JSPropertyDescriptor;
 struct JSRuntime;
 struct nsIntMargin;
 
 template<class E> class nsCOMArray;
 template<class K, class V> class nsDataHashtable;
 template<class K, class V> class nsRefPtrHashtable;
@@ -124,16 +125,20 @@ class EventTarget;
 class IPCDataTransfer;
 class NodeInfo;
 class nsIContentChild;
 class nsIContentParent;
 class Selection;
 class TabParent;
 } // namespace dom
 
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
 namespace layers {
 class LayerManager;
 } // namespace layers
 
 } // namespace mozilla
 
 class nsIBidiKeyboard;
 
@@ -1272,18 +1277,19 @@ public:
    * added to the result.
    *
    * @param aNode Node to get textual contents of.
    * @param aDeep If true child elements of aNode are recursivly descended
    *              into to find text children.
    * @param aResult the result. Out param.
    * @return false on out of memory errors, true otherwise.
    */
+  MOZ_WARN_UNUSED_RESULT
   static bool GetNodeTextContent(nsINode* aNode, bool aDeep,
-                                 nsAString& aResult) NS_WARN_UNUSED_RESULT;
+                                 nsAString& aResult);
 
   /**
    * Same as GetNodeTextContents but appends the result rather than sets it.
    */
   static bool AppendNodeTextContent(nsINode* aNode, bool aDeep,
                                     nsAString& aResult, const mozilla::fallible_t&);
 
   /**
@@ -1748,18 +1754,19 @@ public:
    */
   static void RemoveNewlines(nsString &aString);
 
   /**
    * Convert Windows and Mac platform linebreaks to \n.
    * @param aString the string to convert the newlines inside [in/out]
    */
   static void PlatformToDOMLineBreaks(nsString &aString);
-  static NS_WARN_UNUSED_RESULT bool PlatformToDOMLineBreaks(nsString &aString,
-                                                            const mozilla::fallible_t&);
+  MOZ_WARN_UNUSED_RESULT
+  static bool PlatformToDOMLineBreaks(nsString &aString,
+                                      const mozilla::fallible_t&);
 
   /**
    * Populates aResultString with the contents of the string-buffer aBuf, up
    * to aBuf's null-terminator.  aBuf must not be null. Ownership of the string
    * is not transferred.
    */
   static void PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
                                              nsAString& aResultString);
@@ -2291,16 +2298,29 @@ public:
   static void CallOnAllRemoteChildren(nsIDOMWindow* aWindow,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
   static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
                                               nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
                                               mozilla::dom::nsIContentChild* aChild,
                                               mozilla::dom::nsIContentParent* aParent);
+
+  static void TransferableToIPCTransferable(nsITransferable* aTransferable,
+                                            mozilla::dom::IPCDataTransfer* aIPCDataTransfer,
+                                            mozilla::dom::nsIContentChild* aChild,
+                                            mozilla::dom::nsIContentParent* aParent);
+
+  /*
+   * Get the pixel data from the given source surface and return it as a buffer.
+   * The length and stride will be assigned from the surface.
+   */
+  static mozilla::UniquePtr<char[]> GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+                                                   size_t* aLength, int32_t* aStride);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -29,20 +29,20 @@ class nsAttrKey
 {
 public:
   /**
    * The namespace of the attribute
    */
   int32_t  mNamespaceID;
 
   /**
-   * The atom for attribute, weak ref. is fine as we only use it for the
-   * hashcode, we never dereference it.
+   * The atom for attribute, stored as void*, to make sure that we only use it
+   * for the hashcode, and we can never dereference it.
    */
-  nsIAtom* mLocalName;
+  void* mLocalName;
 
   nsAttrKey(int32_t aNs, nsIAtom* aName)
     : mNamespaceID(aNs), mLocalName(aName) {}
 
   nsAttrKey(const nsAttrKey& aAttr)
     : mNamespaceID(aAttr.mNamespaceID), mLocalName(aAttr.mLocalName) {}
 };
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -10826,18 +10826,18 @@ nsIDocument::ObsoleteSheet(const nsAStri
   }
 }
 
 nsIHTMLCollection*
 nsIDocument::Children()
 {
   if (!mChildrenCollection) {
     mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
-                                            nsGkAtoms::_asterix,
-                                            nsGkAtoms::_asterix,
+                                            nsGkAtoms::_asterisk,
+                                            nsGkAtoms::_asterisk,
                                             false);
   }
 
   return mChildrenCollection;
 }
 
 uint32_t
 nsIDocument::ChildElementCount()
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -137,18 +137,19 @@ public:
   {
     return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
   }
   virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
                               bool aNotify) override;
   virtual bool TextIsOnlyWhitespace() override;
   virtual bool HasTextForTranslation() override;
   virtual void AppendTextTo(nsAString& aResult) override;
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendTextTo(nsAString& aResult,
-                            const mozilla::fallible_t&) override NS_WARN_UNUSED_RESULT;
+                            const mozilla::fallible_t&) override;
   virtual void DestroyContent() override;
   virtual void SaveSubtreeState() override;
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const override;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
 #endif
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -45,17 +45,17 @@ GK_ATOM(mozgeneratedcontentafter, "_moz_
 GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image")
 GK_ATOM(mozquote, "_moz_quote")
 GK_ATOM(mozsignature, "moz-signature")
 GK_ATOM(_moz_is_glyph, "-moz-is-glyph")
 GK_ATOM(_moz_original_size, "_moz_original_size")
 GK_ATOM(_moz_target, "_moz_target")
 GK_ATOM(menuactive, "_moz-menuactive")
 GK_ATOM(_poundDefault, "#default")
-GK_ATOM(_asterix, "*")
+GK_ATOM(_asterisk, "*")
 GK_ATOM(a, "a")
 GK_ATOM(abbr, "abbr")
 GK_ATOM(abort, "abort")
 GK_ATOM(above, "above")
 GK_ATOM(acceltext, "acceltext")
 GK_ATOM(accept, "accept")
 GK_ATOM(acceptcharset, "accept-charset")
 GK_ATOM(accesskey, "accesskey")
--- a/dom/base/nsHTMLContentSerializer.h
+++ b/dom/base/nsHTMLContentSerializer.h
@@ -31,26 +31,26 @@ class nsHTMLContentSerializer final : pu
 
   NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr) override;
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr) override;
  protected:
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool SerializeHTMLAttributes(nsIContent* aContent,
                                        nsIContent *aOriginalElement,
                                        nsAString& aTagPrefix,
                                        const nsAString& aTagNamespaceURI,
                                        nsIAtom* aTagName,
                                        int32_t aNamespace,
                                        nsAString& aStr);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr) override;
 
 };
 
 nsresult
 NS_NewHTMLContentSerializer(nsIContentSerializer** aSerializer);
 
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -540,18 +540,18 @@ public:
    * NOTE: This asserts and returns for elements
    */
   virtual void AppendTextTo(nsAString& aResult) = 0;
 
   /**
    * Append the text content to aResult.
    * NOTE: This asserts and returns for elements
    */
-  virtual bool AppendTextTo(nsAString& aResult,
-                            const mozilla::fallible_t&) NS_WARN_UNUSED_RESULT = 0;
+  MOZ_WARN_UNUSED_RESULT
+  virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) = 0;
 
   /**
    * Check if this content is focusable and in the current tab order.
    * Note: most callers should use nsIFrame::IsFocusable() instead as it 
    *       checks visibility and other layout factors as well.
    * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
    * For example, only the selected radio button in a group is in the 
    * tab order, unless the radio group has no selection in which case
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -108,17 +108,16 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsINode::nsSlots::~nsSlots()
 {
   if (mChildNodes) {
     mChildNodes->DropReference();
-    NS_RELEASE(mChildNodes);
   }
 
   if (mWeakReference) {
     mWeakReference->NoticeNodeDestruction();
   }
 }
 
 void
@@ -128,17 +127,16 @@ nsINode::nsSlots::Traverse(nsCycleCollec
   cb.NoteXPCOMChild(mChildNodes);
 }
 
 void
 nsINode::nsSlots::Unlink()
 {
   if (mChildNodes) {
     mChildNodes->DropReference();
-    NS_RELEASE(mChildNodes);
   }
 }
 
 //----------------------------------------------------------------------
 
 nsINode::~nsINode()
 {
   MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
@@ -366,19 +364,16 @@ nsINode::GetSelectionRootContent(nsIPres
 }
 
 nsINodeList*
 nsINode::ChildNodes()
 {
   nsSlots* slots = Slots();
   if (!slots->mChildNodes) {
     slots->mChildNodes = new nsChildContentList(this);
-    if (slots->mChildNodes) {
-      NS_ADDREF(slots->mChildNodes);
-    }
   }
 
   return slots->mChildNodes;
 }
 
 void
 nsINode::GetTextContentInternal(nsAString& aTextContent, ErrorResult& aError)
 {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -6,16 +6,17 @@
 #ifndef nsINode_h___
 #define nsINode_h___
 
 #include "mozilla/Likely.h"
 #include "nsCOMPtr.h"               // for member, local
 #include "nsGkAtoms.h"              // for nsGkAtoms::baseURIProperty
 #include "nsIDOMNode.h"
 #include "mozilla/dom/NodeInfo.h"            // member (in nsCOMPtr)
+#include "nsINodeList.h"            // base class
 #include "nsIVariant.h"             // for use in GetUserData()
 #include "nsNodeInfoManager.h"      // for use in NodePrincipal()
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
 #include "mozilla/ErrorResult.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
@@ -36,17 +37,17 @@ class nsDOMAttributeMap;
 class nsIAnimationObserver;
 class nsIContent;
 class nsIDocument;
 class nsIDOMElement;
 class nsIDOMNodeList;
 class nsIEditor;
 class nsIFrame;
 class nsIMutationObserver;
-class nsINodeList;
+class nsINode;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
 class nsNodeWeakReference;
 class nsXPCClassInfo;
 class nsDOMMutationObserver;
 
@@ -229,16 +230,60 @@ public:
 private:
   // This is the value sGeneration had when the guard was constructed.
   uint64_t mStartingGeneration;
 
   // This value is incremented on every mutation, for the life of the process.
   static uint64_t sGeneration;
 };
 
+/**
+ * Class that implements the nsIDOMNodeList interface (a list of children of
+ * the content), by holding a reference to the content and delegating GetLength
+ * and Item to its existing child list.
+ * @see nsIDOMNodeList
+ */
+class nsChildContentList final : public nsINodeList
+{
+public:
+  explicit nsChildContentList(nsINode* aNode)
+    : mNode(aNode)
+  {
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsChildContentList)
+
+  // nsWrapperCache
+  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
+
+  // nsIDOMNodeList interface
+  NS_DECL_NSIDOMNODELIST
+
+  // nsINodeList interface
+  virtual int32_t IndexOf(nsIContent* aContent) override;
+  virtual nsIContent* Item(uint32_t aIndex) override;
+
+  void DropReference()
+  {
+    mNode = nullptr;
+  }
+
+  virtual nsINode* GetParentObject() override
+  {
+    return mNode;
+  }
+
+private:
+  ~nsChildContentList() {}
+
+  // The node whose children make up the list (weak reference)
+  nsINode* mNode;
+};
+
 // This should be used for any nsINode sub-class that has fields of its own
 // that it needs to measure;  any sub-class that doesn't use it will inherit
 // SizeOfExcludingThis from its super-class.  SizeOfIncludingThis() need not be
 // defined, it is inherited from nsINode.
 // This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
 #define NS_DECL_SIZEOF_EXCLUDING_THIS \
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
@@ -1016,18 +1061,17 @@ public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const = 0;
 
   // This class can be extended by subclasses that wish to store more
   // information in the slots.
   class nsSlots
   {
   public:
     nsSlots()
-      : mChildNodes(nullptr),
-        mWeakReference(nullptr)
+      : mWeakReference(nullptr)
     {
     }
 
     // If needed we could remove the vtable pointer this dtor causes by
     // putting a DestroySlots function on nsINode
     virtual ~nsSlots();
 
     void Traverse(nsCycleCollectionTraversalCallback &cb);
@@ -1037,25 +1081,24 @@ public:
      * A list of mutation observers
      */
     nsTObserverArray<nsIMutationObserver*> mMutationObservers;
 
     /**
      * An object implementing nsIDOMNodeList for this content (childNodes)
      * @see nsIDOMNodeList
      * @see nsGenericHTMLElement::GetChildNodes
-     *
-     * MSVC 7 doesn't like this as an nsRefPtr
      */
-    nsChildContentList* mChildNodes;
+    nsRefPtr<nsChildContentList> mChildNodes;
 
     /**
-     * Weak reference to this node
+     * Weak reference to this node.  This is cleared by the destructor of
+     * nsNodeWeakReference.
      */
-    nsNodeWeakReference* mWeakReference;
+    nsNodeWeakReference* MOZ_NON_OWNING_REF mWeakReference;
   };
 
   /**
    * Functions for managing flags and slots
    */
 #ifdef DEBUG
   nsSlots* DebugGetSlots()
   {
--- a/dom/base/nsINodeList.h
+++ b/dom/base/nsINodeList.h
@@ -3,23 +3,25 @@
  * 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 nsINodeList_h___
 #define nsINodeList_h___
 
 #include "nsIDOMNodeList.h"
 #include "nsWrapperCache.h"
-#include "nsIContent.h"
 
 // IID for the nsINodeList interface
 #define NS_INODELIST_IID \
 { 0xadb5e54c, 0x6e96, 0x4102, \
  { 0x8d, 0x40, 0xe0, 0x12, 0x3d, 0xcf, 0x48, 0x7a } }
 
+class nsIContent;
+class nsINode;
+
 /**
  * An internal interface for a reasonably fast indexOf.
  */
 class nsINodeList : public nsIDOMNodeList,
                     public nsWrapperCache
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODELIST_IID)
--- a/dom/base/nsNodeInfoManager.h
+++ b/dom/base/nsNodeInfoManager.h
@@ -124,19 +124,19 @@ protected:
 
 private:
   static int NodeInfoInnerKeyCompare(const void *key1, const void *key2);
   static PLHashNumber GetNodeInfoInnerHashValue(const void *key);
   static int DropNodeInfoDocument(PLHashEntry *he, int hashIndex,
                                      void *arg);
 
   PLHashTable *mNodeInfoHash;
-  nsIDocument *mDocument; // WEAK
+  nsIDocument * MOZ_NON_OWNING_REF mDocument; // WEAK
   uint32_t mNonDocumentNodeInfos;
   nsCOMPtr<nsIPrincipal> mPrincipal; // Never null after Init() succeeds.
   nsCOMPtr<nsIPrincipal> mDefaultPrincipal; // Never null after Init() succeeds
-  mozilla::dom::NodeInfo *mTextNodeInfo; // WEAK to avoid circular ownership
-  mozilla::dom::NodeInfo *mCommentNodeInfo; // WEAK to avoid circular ownership
-  mozilla::dom::NodeInfo *mDocumentNodeInfo; // WEAK to avoid circular ownership
+  mozilla::dom::NodeInfo * MOZ_NON_OWNING_REF mTextNodeInfo; // WEAK to avoid circular ownership
+  mozilla::dom::NodeInfo * MOZ_NON_OWNING_REF mCommentNodeInfo; // WEAK to avoid circular ownership
+  mozilla::dom::NodeInfo * MOZ_NON_OWNING_REF mDocumentNodeInfo; // WEAK to avoid circular ownership
   nsRefPtr<nsBindingManager> mBindingManager;
 };
 
 #endif /* nsNodeInfoManager_h___ */
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -71,16 +71,17 @@ nsScriptLoadRequestList::~nsScriptLoadRe
   Clear();
 }
 
 void
 nsScriptLoadRequestList::Clear()
 {
   while (!isEmpty()) {
     nsRefPtr<nsScriptLoadRequest> first = StealFirst();
+    first->Cancel();
     // And just let it go out of scope and die.
   }
 }
 
 #ifdef DEBUG
 bool
 nsScriptLoadRequestList::Contains(nsScriptLoadRequest* aElem)
 {
@@ -1481,16 +1482,20 @@ nsScriptLoader::PrepareLoadedRequest(nsS
                                      nsresult aStatus,
                                      uint32_t aStringLen,
                                      const uint8_t* aString)
 {
   if (NS_FAILED(aStatus)) {
     return aStatus;
   }
 
+  if (aRequest->IsCanceled()) {
+    return NS_BINDING_ABORTED;
+  }
+
   // If we don't have a document, then we need to abort further
   // evaluation.
   if (!mDocument) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // If the load returned an error page, then we need to abort
   nsCOMPtr<nsIRequest> req;
@@ -1551,20 +1556,25 @@ nsScriptLoader::PrepareLoadedRequest(nsS
                mXSLTRequests.Contains(aRequest)  ||
                mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
                mParserBlockingRequest,
                "aRequest should be pending!");
 
   // Mark this as loaded
   aRequest->mLoading = false;
 
-  // And if it's async, move it to the loaded list.
+  // And if it's async, move it to the loaded list.  aRequest->mIsAsync really
+  // _should_ be in a list, but the consequences if it's not are bad enough we
+  // want to avoid trying to move it if it's not.
   if (aRequest->mIsAsync) {
-    nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
-    mLoadedAsyncRequests.AppendElement(req);
+    MOZ_ASSERT(aRequest->isInList());
+    if (aRequest->isInList()) {
+      nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
+      mLoadedAsyncRequests.AppendElement(req);
+    }
   }
 
   return NS_OK;
 }
 
 void
 nsScriptLoader::ParsingComplete(bool aTerminated)
 {
@@ -1575,17 +1585,20 @@ nsScriptLoader::ParsingComplete(bool aTe
   }
   mDeferEnabled = false;
   if (aTerminated) {
     mDeferRequests.Clear();
     mLoadingAsyncRequests.Clear();
     mLoadedAsyncRequests.Clear();
     mNonAsyncExternalScriptInsertedRequests.Clear();
     mXSLTRequests.Clear();
-    mParserBlockingRequest = nullptr;
+    if (mParserBlockingRequest) {
+      mParserBlockingRequest->Cancel();
+      mParserBlockingRequest = nullptr;
+    }
   }
 
   // Have to call this even if aTerminated so we'll correctly unblock
   // onload and all.
   ProcessPendingRequests();
 }
 
 void
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -59,16 +59,17 @@ public:
     : mElement(aElement),
       mLoading(true),
       mIsInline(true),
       mHasSourceMapURL(false),
       mIsDefer(false),
       mIsAsync(false),
       mIsNonAsyncScriptInserted(false),
       mIsXSLT(false),
+      mIsCanceled(false),
       mScriptTextBuf(nullptr),
       mScriptTextLength(0),
       mJSVersion(aVersion),
       mLineNo(1),
       mCORSMode(aCORSMode),
       mReferrerPolicy(mozilla::net::RP_Default)
   {
   }
@@ -84,27 +85,38 @@ public:
     mElement->ScriptEvaluated(aResult, mElement, mIsInline);
   }
 
   bool IsPreload()
   {
     return mElement == nullptr;
   }
 
+  void Cancel()
+  {
+    mIsCanceled = true;
+  }
+
+  bool IsCanceled() const
+  {
+    return mIsCanceled;
+  }
+
   using super::getNext;
   using super::isInList;
 
   nsCOMPtr<nsIScriptElement> mElement;
   bool mLoading;          // Are we still waiting for a load to complete?
   bool mIsInline;         // Is the script inline or loaded?
   bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
   bool mIsDefer;          // True if we live in mDeferRequests.
   bool mIsAsync;          // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
   bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests
   bool mIsXSLT;           // True if we live in mXSLTRequests.
+  bool mIsCanceled;       // True if we have been explicitly canceled.
   nsString mSourceMapURL; // Holds source map url for loaded scripts
   char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
   size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
   uint32_t mJSVersion;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIPrincipal> mOriginPrincipal;
   nsAutoCString mURL;   // Keep the URI's filename alive during off thread parsing.
   int32_t mLineNo;
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -37,38 +37,29 @@ nsStructuredCloneContainer::nsStructured
 }
 
 nsStructuredCloneContainer::~nsStructuredCloneContainer()
 {
   free(mData);
 }
 
 nsresult
-nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData)
+nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
+                                          JSContext* aCx)
 {
   NS_ENSURE_STATE(!mData);
 
   uint64_t* jsBytes = nullptr;
   bool success = false;
   if (aData.isPrimitive()) {
-    // |aData| is a primitive, so the structured clone algorithm won't run
-    // script and we can just use AutoJSAPI.
-    dom::AutoJSAPI jsapi;
-    jsapi.Init();
-    success = JS_WriteStructuredClone(jsapi.cx(), aData, &jsBytes, &mSize,
+    success = JS_WriteStructuredClone(aCx, aData, &jsBytes, &mSize,
                                       nullptr, nullptr,
                                       JS::UndefinedHandleValue);
   } else {
-    // |aData| is an object and the structured clone algorithm can run script as
-    // part of the "own" "deep clone" sub-steps, so we need an AutoEntryScript.
-    // http://www.whatwg.org/specs/web-apps/current-work/#internal-structured-cloning-algorithm
-    nsIGlobalObject* nativeGlobal =
-      xpc::NativeGlobal(js::GetGlobalForObjectCrossCompartment(&aData.toObject()));
-    dom::AutoEntryScript aes(nativeGlobal);
-    success = JS_WriteStructuredClone(aes.cx(), aData, &jsBytes, &mSize,
+    success = JS_WriteStructuredClone(aCx, aData, &jsBytes, &mSize,
                                       nullptr, nullptr,
                                       JS::UndefinedHandleValue);
   }
   NS_ENSURE_STATE(success);
   NS_ENSURE_STATE(jsBytes);
 
   // Copy jsBytes into our own buffer.
   mData = (uint64_t*) malloc(mSize);
--- a/dom/base/nsTextFragment.h
+++ b/dom/base/nsTextFragment.h
@@ -129,18 +129,19 @@ public:
       aString.AllocFailed(aString.Length() + GetLength());
     }
   }
 
   /**
    * Append the contents of this string fragment to aString
    * @return false if an out of memory condition is detected, true otherwise
    */
+  MOZ_WARN_UNUSED_RESULT
   bool AppendTo(nsAString& aString,
-                const mozilla::fallible_t& aFallible) const NS_WARN_UNUSED_RESULT {
+                const mozilla::fallible_t& aFallible) const {
     if (mState.mIs2b) {
       bool ok = aString.Append(m2b, mState.mLength, aFallible);
       if (!ok) {
         return false;
       }
 
       return true;
     } else {
@@ -162,18 +163,19 @@ public:
 
   /**
    * Append a substring of the contents of this string fragment to aString.
    * @param aString the string in which to append
    * @param aOffset where to start the substring in this text fragment
    * @param aLength the length of the substring
    * @return false if an out of memory condition is detected, true otherwise
    */
+  MOZ_WARN_UNUSED_RESULT
   bool AppendTo(nsAString& aString, int32_t aOffset, int32_t aLength,
-                const mozilla::fallible_t& aFallible) const NS_WARN_UNUSED_RESULT
+                const mozilla::fallible_t& aFallible) const
   {
     if (mState.mIs2b) {
       bool ok = aString.Append(m2b + aOffset, aLength, aFallible);
       if (!ok) {
         return false;
       }
 
       return true;
--- a/dom/base/nsXHTMLContentSerializer.h
+++ b/dom/base/nsXHTMLContentSerializer.h
@@ -41,23 +41,23 @@ class nsXHTMLContentSerializer : public 
  protected:
 
 
   virtual bool CheckElementStart(nsIContent * aContent,
                           bool & aForceFormat,
                           nsAString& aStr,
                           nsresult& aResult) override;
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement,
                                nsIAtom * aName,
                                int32_t aNamespaceID,
                                nsAString& aStr) override;
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AfterElementStart(nsIContent* aContent,
                                  nsIContent* aOriginalElement,
                                  nsAString& aStr) override;
 
   virtual bool CheckElementEnd(nsIContent * aContent,
                           bool & aForceFormat,
                           nsAString& aStr) override;
 
@@ -70,35 +70,35 @@ class nsXHTMLContentSerializer : public 
   virtual bool LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName) override;
 
   bool HasLongLines(const nsString& text, int32_t& aLastNewlineOffset);
 
   // functions to check if we enter in or leave from a preformated content
   virtual void MaybeEnterInPreContent(nsIContent* aNode) override;
   virtual void MaybeLeaveFromPreContent(nsIContent* aNode) override;
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool SerializeAttributes(nsIContent* aContent,
                            nsIContent *aOriginalElement,
                            nsAString& aTagPrefix,
                            const nsAString& aTagNamespaceURI,
                            nsIAtom* aTagName,
                            nsAString& aStr,
                            uint32_t aSkipAttr,
                            bool aAddNSAttr) override;
 
   bool IsFirstChildOfOL(nsIContent* aElement);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool SerializeLIValueAttribute(nsIContent* aElement,
                                  nsAString& aStr);
   bool IsShorthandAttr(const nsIAtom* aAttrName,
                          const nsIAtom* aElementName);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr) override;
 
   nsresult EscapeURI(nsIContent* aContent,
                      const nsAString& aURI,
                      nsAString& aEscapedURI);
 
 private:
--- a/dom/base/nsXMLContentSerializer.h
+++ b/dom/base/nsXMLContentSerializer.h
@@ -67,92 +67,92 @@ class nsXMLContentSerializer : public ns
                                  nsAString& aStr) override;
 
  protected:
   virtual ~nsXMLContentSerializer();
 
   /**
    * Appends a char16_t character and increments the column position
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendToString(const char16_t aChar,
                       nsAString& aOutputStr);
 
   /**
    * Appends a nsAString string and increments the column position
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendToString(const nsAString& aStr,
                       nsAString& aOutputStr);
 
   /**
    * Appends a string by replacing all line-endings
    * by mLineBreak, except in the case of raw output.
    * It increments the column position.
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendToStringConvertLF(const nsAString& aStr,
                                nsAString& aOutputStr);
 
   /**
    * Appends a string by wrapping it when necessary.
    * It updates the column position.
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendToStringWrapped(const nsASingleFragmentString& aStr,
                              nsAString& aOutputStr);
 
   /**
    * Appends a string by formating and wrapping it when necessary
    * It updates the column position.
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendToStringFormatedWrapped(const nsASingleFragmentString& aStr,
                                      nsAString& aOutputStr);
 
   // used by AppendToStringWrapped
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendWrapped_WhitespaceSequence(
           nsASingleFragmentString::const_char_iterator &aPos,
           const nsASingleFragmentString::const_char_iterator aEnd,
           const nsASingleFragmentString::const_char_iterator aSequenceStart,
           nsAString &aOutputStr);
 
   // used by AppendToStringFormatedWrapped
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendFormatedWrapped_WhitespaceSequence(
           nsASingleFragmentString::const_char_iterator &aPos,
           const nsASingleFragmentString::const_char_iterator aEnd,
           const nsASingleFragmentString::const_char_iterator aSequenceStart,
           bool &aMayIgnoreStartOfLineWhitespaceSequence,
           nsAString &aOutputStr);
 
   // used by AppendToStringWrapped and AppendToStringFormatedWrapped
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendWrapped_NonWhitespaceSequence(
           nsASingleFragmentString::const_char_iterator &aPos,
           const nsASingleFragmentString::const_char_iterator aEnd,
           const nsASingleFragmentString::const_char_iterator aSequenceStart,
           bool &aMayIgnoreStartOfLineWhitespaceSequence,
           bool &aSequenceStartAfterAWhiteSpace,
           nsAString &aOutputStr);
 
   /**
    * add mLineBreak to the string
    * It updates the column position and other flags.
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendNewLineToString(nsAString& aOutputStr);
 
 
   /**
    * Appends a string by translating entities
    * It doesn't increment the column position
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr);
 
   /**
    * retrieve the text content of the node and append it to the given string
    * It doesn't increment the column position
    */
   nsresult AppendTextData(nsIContent* aNode,
@@ -192,27 +192,27 @@ class nsXMLContentSerializer : public ns
    * GenerateNewPrefix generates a new prefix and writes it to aPrefix
    */
   void GenerateNewPrefix(nsAString& aPrefix);
 
   uint32_t ScanNamespaceDeclarations(nsIContent* aContent,
                                      nsIContent *aOriginalElement,
                                      const nsAString& aTagNamespaceURI);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool SerializeAttributes(nsIContent* aContent,
                                    nsIContent *aOriginalElement,
                                    nsAString& aTagPrefix,
                                    const nsAString& aTagNamespaceURI,
                                    nsIAtom* aTagName,
                                    nsAString& aStr,
                                    uint32_t aSkipAttr,
                                    bool aAddNSAttr);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool SerializeAttr(const nsAString& aPrefix,
                      const nsAString& aName,
                      const nsAString& aValue,
                      nsAString& aStr,
                      bool aDoEscapeEntities);
 
   bool IsJavaScript(nsIContent * aContent,
                       nsIAtom* aAttrNameAtom,
@@ -231,28 +231,28 @@ class nsXMLContentSerializer : public ns
                                    bool & aForceFormat,
                                    nsAString& aStr,
                                    nsresult& aResult);
 
   /**
    * this method is responsible to finish the start tag,
    * in particulary to append the "greater than" sign
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement,
                                        nsIAtom * aName,
                                        int32_t aNamespaceID,
                                        nsAString& aStr);
 
   /**
    * This method can be redefine to serialize additional things just after
    * after the serialization ot the start tag.
    * (called at the end of AppendElementStart)
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   virtual bool AfterElementStart(nsIContent* aContent,
                                  nsIContent* aOriginalElement,
                                  nsAString& aStr) { return true; };
 
   /**
    * This method can be redefined to check if the element can be serialized.
    * It is called when the serialization of the end tag is asked 
    * (AppendElementEnd)
@@ -291,26 +291,26 @@ class nsXMLContentSerializer : public ns
    * Returns true if a line break should be inserted after an element close tag
    */
   virtual bool LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName);
 
   /**
    * add intendation. Call only in the case of formating and if the current
    * position is at 0. It updates the column position.
    */
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool AppendIndentation(nsAString& aStr);
 
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool IncrIndentation(nsIAtom* aName);
   void DecrIndentation(nsIAtom* aName);
 
   // Functions to check for newlines that needs to be added between nodes in
   // the root of a document. See mAddNewlineForRootNode
-  NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
   bool MaybeAddNewlineForRootNode(nsAString& aStr);
   void MaybeFlagNewlineForRootNode(nsINode* aNode);
 
   // Functions to check if we enter in or leave from a preformated content
   virtual void MaybeEnterInPreContent(nsIContent* aNode);
   virtual void MaybeLeaveFromPreContent(nsIContent* aNode);
 
   bool ShouldMaintainPreLevel() const;
--- a/dom/base/test/websocket_hybi/mochitest.ini
+++ b/dom/base/test/websocket_hybi/mochitest.ini
@@ -1,14 +1,13 @@
 [DEFAULT]
-skip-if = e10s
 support-files =
   file_binary-frames_wsh.py
   file_check-binary-messages_wsh.py
 
 [test_receive-arraybuffer.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_receive-blob.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_send-arraybuffer.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_send-blob.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android'
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
--- a/dom/battery/test/mochitest.ini
+++ b/dom/battery/test/mochitest.ini
@@ -1,5 +1,2 @@
-[DEFAULT]
-skip-if = e10s
-
 [test_battery_basics.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10441,17 +10441,17 @@ class CGDOMJSProxyHandler_delete(ClassMe
                                     "that has unforgeables.  Figure out how "
                                     "that should work!")
                 # See if the deleter method is fallible.
                 t = deleter.signatures()[0][0]
                 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
                     # The deleter method has a boolean out-parameter. When a
                     # property is found, the out-param indicates whether it was
                     # successfully deleted.
-                    decls = "bool result;\n"
+                    decls = ""
                     if foundVar is None:
                         foundVar = "found"
                         decls += "bool found = false;\n"
                     setDS = fill(
                         """
                         if (!${foundVar}) {
                           deleteSucceeded = true;
                         }
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -61,26 +61,25 @@ public:
 #endif
 
   ErrorResult(ErrorResult&& aRHS)
   {
     *this = Move(aRHS);
   }
   ErrorResult& operator=(ErrorResult&& aRHS);
 
+  explicit ErrorResult(nsresult aRv)
+    : ErrorResult()
+  {
+    AssignErrorCode(aRv);
+  }
+
   void Throw(nsresult rv) {
     MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
-    MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
-    MOZ_ASSERT(rv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
-    MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
-    MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
-    MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
-    MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
-    MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
-    mResult = rv;
+    AssignErrorCode(rv);
   }
 
   void ThrowTypeError(const dom::ErrNum errorNumber, ...);
   void ThrowRangeError(const dom::ErrNum errorNumber, ...);
   void ReportErrorWithMessage(JSContext* cx);
   void ClearMessage();
   bool IsErrorWithMessage() const { return ErrorCode() == NS_ERROR_TYPE_ERR || ErrorCode() == NS_ERROR_RANGE_ERR; }
 
@@ -136,66 +135,71 @@ public:
   // In the future, we can add overloads of Throw that take more
   // interesting things, like strings or DOM exception types or
   // something if desired.
 
   // Backwards-compat to make conversion simpler.  We don't call
   // Throw() here because people can easily pass success codes to
   // this.
   void operator=(nsresult rv) {
-    MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
-    MOZ_ASSERT(rv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
-    MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
-    MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
-    MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
-    MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
-    MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
-    mResult = rv;
+    AssignErrorCode(rv);
   }
 
   bool Failed() const {
     return NS_FAILED(mResult);
   }
 
   nsresult ErrorCode() const {
     return mResult;
   }
 
 private:
+  friend struct IPC::ParamTraits<ErrorResult>;
+  void SerializeMessage(IPC::Message* aMsg) const;
+  bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
+
+  void ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
+                             nsresult errorType);
+
+  void AssignErrorCode(nsresult aRv) {
+    MOZ_ASSERT(aRv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
+    MOZ_ASSERT(aRv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
+    MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
+    MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
+    MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
+    MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
+    MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
+    mResult = aRv;
+  }
+
   nsresult mResult;
   struct Message;
   // mMessage is set by ThrowErrorWithMessage and cleared (and deallocated) by
   // ReportErrorWithMessage.
   // mJSException is set (and rooted) by ThrowJSException and unrooted
   // by ReportJSException.
   union {
     Message* mMessage; // valid when IsErrorWithMessage()
     JS::Value mJSException; // valid when IsJSException()
   };
 
-  friend struct IPC::ParamTraits<ErrorResult>;
-  void SerializeMessage(IPC::Message* aMsg) const;
-  bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
-
 #ifdef DEBUG
   // Used to keep track of codepaths that might throw JS exceptions,
   // for assertion purposes.
   bool mMightHaveUnreportedJSException;
   // Used to keep track of whether mMessage has ever been assigned to.
   // We need to check this in order to ensure that not attempting to
   // delete mMessage in DeserializeMessage doesn't leak memory.
   bool mHasMessage;
 #endif
 
   // Not to be implemented, to make sure people always pass this by
   // reference, not by value.
   ErrorResult(const ErrorResult&) = delete;
   void operator=(const ErrorResult&) = delete;
-  void ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
-                             nsresult errorType);
 };
 
 /******************************************************************************
  ** Macros for checking results
  ******************************************************************************/
 
 #define ENSURE_SUCCESS(res, ret)                                          \
   do {                                                                    \
--- a/dom/bindings/MozMap.h
+++ b/dom/bindings/MozMap.h
@@ -12,16 +12,17 @@
 
 #ifndef mozilla_dom_MozMap_h
 #define mozilla_dom_MozMap_h
 
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace binding_detail {
 template<typename DataType>
 class MozMapEntry : public nsStringHashKey
@@ -95,17 +96,18 @@ public:
   // with a dependency on BindingUtils.h here for the SequenceTracer bits.
   typedef PLDHashOperator (* Enumerator)(DataType* aValue, void* aClosure);
   void EnumerateValues(Enumerator aEnumerator, void *aClosure)
   {
     ValueEnumClosure args = { aEnumerator, aClosure };
     this->EnumerateEntries(ValueEnumerator, &args);
   }
 
-  DataType* AddEntry(const nsAString& aKey) NS_WARN_UNUSED_RESULT
+  MOZ_WARN_UNUSED_RESULT
+  DataType* AddEntry(const nsAString& aKey)
   {
     EntryType* ent = this->PutEntry(aKey, fallible);
     if (!ent) {
       return nullptr;
     }
     return &ent->mData;
   }
 
--- a/dom/cache/ActorChild.cpp
+++ b/dom/cache/ActorChild.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/ActorChild.h"
 
 #include "mozilla/dom/cache/Feature.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 void
 ActorChild::SetFeature(Feature* aFeature)
 {
@@ -27,16 +28,17 @@ ActorChild::SetFeature(Feature* aFeature
   if (mFeature) {
     mFeature->AddActor(this);
   }
 }
 
 void
 ActorChild::RemoveFeature()
 {
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mFeature);
   if (mFeature) {
     mFeature->RemoveActor(this);
     mFeature = nullptr;
   }
 }
 
 Feature*
 ActorChild::GetFeature() const
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -2,45 +2,46 @@
 /* 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"
 #include "mozilla/ipc/PBackgroundParent.h"
 
 namespace {
 
 using mozilla::unused;
 using mozilla::dom::cache::CachePushStreamChild;
-using mozilla::dom::cache::PCacheReadStream;
-using mozilla::dom::cache::PCacheReadStreamOrVoid;
+using mozilla::dom::cache::CacheReadStream;
+using mozilla::dom::cache::CacheReadStreamOrVoid;
 using mozilla::ipc::FileDescriptor;
 using mozilla::ipc::FileDescriptorSetChild;
 using mozilla::ipc::FileDescriptorSetParent;
 using mozilla::ipc::OptionalFileDescriptorSet;
 
 enum CleanupAction
 {
   Forget,
   Delete
 };
 
 void
-CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
+CleanupChildFds(CacheReadStream& aReadStream, CleanupAction aAction)
 {
   if (aReadStream.fds().type() !=
       OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
     return;
   }
 
   nsAutoTArray<FileDescriptor, 4> fds;
 
@@ -54,17 +55,17 @@ CleanupChildFds(PCacheReadStream& aReadS
 
   // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
   // unconditionally forget them here.  The fds themselves are auto-closed in
   // ~FileDescriptor since they originated in this process.
   fdSetActor->ForgetFileDescriptors(fds);
 }
 
 void
-CleanupChildPushStream(PCacheReadStream& aReadStream, CleanupAction aAction)
+CleanupChildPushStream(CacheReadStream& aReadStream, CleanupAction aAction)
 {
   if (!aReadStream.pushStreamChild()) {
     return;
   }
 
   auto pushStream =
     static_cast<CachePushStreamChild*>(aReadStream.pushStreamChild());
 
@@ -73,34 +74,34 @@ CleanupChildPushStream(PCacheReadStream&
     return;
   }
 
   // If we send the stream, then we need to start it before forgetting about it.
   pushStream->Start();
 }
 
 void
-CleanupChild(PCacheReadStream& aReadStream, CleanupAction aAction)
+CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction)
 {
   CleanupChildFds(aReadStream, aAction);
   CleanupChildPushStream(aReadStream, aAction);
 }
 
 void
-CleanupChild(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
+CleanupChild(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
 {
-  if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
+  if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
 
-  CleanupChild(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
+  CleanupChild(aReadStreamOrVoid.get_CacheReadStream(), aAction);
 }
 
 void
-CleanupParentFds(PCacheReadStream& aReadStream, CleanupAction aAction)
+CleanupParentFds(CacheReadStream& aReadStream, CleanupAction aAction)
 {
   if (aReadStream.fds().type() !=
       OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
     return;
   }
 
   nsAutoTArray<FileDescriptor, 4> fds;
 
@@ -114,198 +115,408 @@ CleanupParentFds(PCacheReadStream& aRead
 
   // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
   // unconditionally forget them here.  The fds themselves are auto-closed in
   // ~FileDescriptor since they originated in this process.
   fdSetActor->ForgetFileDescriptors(fds);
 }
 
 void
-CleanupParentFds(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
+CleanupParentFds(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
 {
-  if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
+  if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
 
-  CleanupParentFds(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
+  CleanupParentFds(aReadStreamOrVoid.get_CacheReadStream(), aAction);
 }
 
 } // 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() == CacheRequestOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupChild(args.requestOrVoid().get_CacheRequest().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() == CacheRequestOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupChild(args.requestOrVoid().get_CacheRequest().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->ToCacheRequest(args.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TCacheMatchAllArgs:
+    {
+      CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
+      MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
+      args.requestOrVoid() = CacheRequest();
+      mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
+                                  aRequest, aBodyAction, 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.
+      CacheRequest& 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->ToCacheRequest(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->ToCacheRequest(args.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TCacheKeysArgs:
+    {
+      CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
+      MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
+      args.requestOrVoid() = CacheRequest();
+      mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
+                                  aRequest, aBodyAction, aReferrerAction,
+                                  aSchemeAction, aRv);
+      break;
+    }
+    case CacheOpArgs::TStorageMatchArgs:
+    {
+      StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
+      mTypeUtils->ToCacheRequest(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->ToCacheRequest(pair.request(), aRequest, aBodyAction,
+                                  aReferrerAction, aSchemeAction, aRv);
+      if (!aRv.Failed()) {
+        mTypeUtils->ToCacheResponse(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() == CacheResponseOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
+                       action);
+      break;
+    }
+    case CacheOpResult::TCacheMatchAllResult:
+    {
+      CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
+      for (uint32_t i = 0; i < result.responseList().Length(); ++i) {
+        CleanupParentFds(result.responseList()[i].body(), action);
+      }
+      break;
+    }
+    case CacheOpResult::TCacheKeysResult:
+    {
+      CacheKeysResult& result = mOpResult.get_CacheKeysResult();
+      for (uint32_t i = 0; i < result.requestList().Length(); ++i) {
+        CleanupParentFds(result.requestList()[i].body(), action);
+      }
+      break;
+    }
+    case CacheOpResult::TStorageMatchResult:
+    {
+      StorageMatchResult& result = mOpResult.get_StorageMatchResult();
+      if (result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t) {
+        break;
+      }
+      CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
+                       action);
+      break;
+    }
+    case CacheOpResult::TStorageOpenResult:
+    {
+      StorageOpenResult& result = mOpResult.get_StorageOpenResult();
+      if (action == Forget || result.actorParent() == nullptr) {
+        break;
+      }
+      unused << PCacheParent::Send__delete__(result.actorParent());
+    }
+    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() == CacheResponseOrVoid::Tvoid_t);
+      result.responseOrVoid() = aSavedResponse.mValue;
+      SerializeResponseBody(aSavedResponse, aStreamList,
+                            &result.responseOrVoid().get_CacheResponse());
+      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() == CacheResponseOrVoid::Tvoid_t);
+      result.responseOrVoid() = aSavedResponse.mValue;
+      SerializeResponseBody(aSavedResponse, aStreamList,
+                            &result.responseOrVoid().get_CacheResponse());
+      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);
+      CacheRequest& request = result.requestList().LastElement();
+
+      if (!aSavedRequest.mHasBodyId) {
+        request.body() = void_t();
+        break;
+      }
+
+      request.body() = CacheReadStream();
+      SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
+                          &request.body().get_CacheReadStream());
+      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,
+                                          CacheResponse* aResponseOut)
+{
+  MOZ_ASSERT(aResponseOut);
+
+  if (!aSavedResponse.mHasBodyId) {
+    aResponseOut->body() = void_t();
+    return;
+  }
+
+  aResponseOut->body() = CacheReadStream();
+  SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
+                      &aResponseOut->body().get_CacheReadStream());
+}
+
+void
+AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
+                                        CacheReadStream* 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
@@ -3,17 +3,18 @@
 /* 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_AutoUtils_h
 #define mozilla_dom_cache_AutoUtils_h
 
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.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();
-
-  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);
-
-  const nsTArray<PCacheRequest>& SendAsRequestList();
-
-private:
-  // Allocates ~5k inline in the stack-only class
-  nsAutoTArray<PCacheRequest, 32> mRequestList;
-};
-
-class MOZ_STACK_CLASS AutoChildRequestResponse final : public AutoChildBase
-{
-public:
-  explicit AutoChildRequestResponse(TypeUtils* aTypeUtils);
-  ~AutoChildRequestResponse();
+  AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs);
+  ~AutoChildOpArgs();
 
   void Add(InternalRequest* aRequest, BodyAction aBodyAction,
            ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
            ErrorResult& aRv);
-  void Add(Response& aResponse, ErrorResult& aRv);
+  void Add(InternalRequest* aRequest, BodyAction aBodyAction,
+           ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
+           Response& aResponse, ErrorResult& aRv);
 
-  const CacheRequestResponse& SendAsRequestResponse();
+  const CacheOpArgs& SendAsOpArgs();
 
 private:
-  CacheRequestResponse mRequestResponse;
-};
-
-class MOZ_STACK_CLASS AutoParentBase
-{
-protected:
-  explicit AutoParentBase(mozilla::ipc::PBackgroundParent* aManager);
-  virtual ~AutoParentBase() = 0;
-
-  void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
-                           PCacheReadStream* aReadStreamOut);
-
-  mozilla::ipc::PBackgroundParent* mManager;
-  CacheStreamControlParent* mStreamControl;
+  TypeUtils* mTypeUtils;
+  CacheOpArgs mOpArgs;
   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
+class MOZ_STACK_CLASS AutoParentOpResult final
 {
 public:
-  AutoParentResponseList(mozilla::ipc::PBackgroundParent* aManager,
-                         uint32_t aCapacity);
-  ~AutoParentResponseList();
+  AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
+                     const CacheOpResult& aOpResult);
+  ~AutoParentOpResult();
 
+  void Add(CacheId aOpenedCacheId, Manager* aManager);
   void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
+  void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
 
-  const nsTArray<PCacheResponse>& SendAsResponseList();
+  const CacheOpResult& SendAsOpResult();
 
 private:
-  // Allocates ~4k inline in the stack-only class
-  nsAutoTArray<PCacheResponse, 32> mResponseList;
-};
+  void SerializeResponseBody(const SavedResponse& aSavedResponse,
+                             StreamList* aStreamList,
+                             CacheResponse* aResponseOut);
 
-class MOZ_STACK_CLASS AutoParentResponseOrVoid final : public AutoParentBase
-{
-public:
-  explicit AutoParentResponseOrVoid(mozilla::ipc::PBackgroundParent* aManager);
-  ~AutoParentResponseOrVoid();
+  void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
+                           CacheReadStream* aReadStreamOut);
 
-  void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
-
-  const PCacheResponseOrVoid& SendAsResponseOrVoid();
-
-private:
-  PCacheResponseOrVoid mResponseOrVoid;
+  mozilla::ipc::PBackgroundParent* mManager;
+  CacheOpResult mOpResult;
+  CacheStreamControlParent* mStreamControl;
+  bool mSent;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_AutoUtils_h
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -11,17 +11,16 @@
 #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/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 +73,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 +95,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()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  AutoChildRequest request(this);
+  CacheQueryParams params;
+  ToCacheQueryParams(params, aOptions);
 
-  request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
-  if (aRv.Failed()) {
+  AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params));
+
+  args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  PCacheQueryParams params;
-  ToPCacheQueryParams(params, aOptions);
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendMatch(requestId, request.SendAsRequest(), params);
-
-  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;
-  }
+  CacheQueryParams params;
+  ToCacheQueryParams(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);
+  CacheQueryParams params;
+  ToCacheQueryParams(params, aOptions);
+
+  AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params));
+
+  args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  PCacheQueryParams params;
-  ToPCacheQueryParams(params, aOptions);
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendDelete(requestId, request.SendAsRequest(), params);
-
-  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;
-  }
+  CacheQueryParams params;
+  ToCacheQueryParams(params, aOptions);
 
-  AutoChildRequest request(this);
+  AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params));
 
   if (aRequest.WasPassed()) {
     nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
                                                      IgnoreBody, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
-    request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
+    args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  PCacheQueryParams params;
-  ToPCacheQueryParams(params, aOptions);
-
-  RequestId requestId = AddRequestPromise(promise, aRv);
-
-  unused << mActor->SendKeys(requestId, request.SendAsRequestOrVoid(), params);
-
-  return promise.forget();
+  return ExecuteOp(args, aRv);
 }
 
 // static
 bool
 Cache::PrefEnabled(JSContext* aCx, JSObject* aObj)
 {
   using mozilla::dom::workers::WorkerPrivate;
   using mozilla::dom::workers::GetWorkerPrivateFromContext;
@@ -396,133 +324,16 @@ void
 Cache::DestroyInternal(CacheChild* aActor)
 {
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(mActor == aActor);
   mActor->ClearListener();
   mActor = nullptr;
 }
 
-void
-Cache::RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                         const PCacheResponseOrVoid& aResponse)
-{
-  // Convert the response immediately if its present.  This ensures that
-  // any stream actors are cleaned up, even if we error out below.
-  nsRefPtr<Response> response;
-  if (aResponse.type() == PCacheResponseOrVoid::TPCacheResponse) {
-    response = ToResponse(aResponse);
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  if (!response) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
-    return;
-  }
-
-  promise->MaybeResolve(response);
-}
-
-void
-Cache::RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
-                            const nsTArray<PCacheResponse>& aResponses)
-{
-  // Convert responses immediately.  This ensures that any stream actors are
-  // cleaned up, even if we error out below.
-  nsAutoTArray<nsRefPtr<Response>, 256> responses;
-  responses.SetCapacity(aResponses.Length());
-
-  for (uint32_t i = 0; i < aResponses.Length(); ++i) {
-    nsRefPtr<Response> response = ToResponse(aResponses[i]);
-    responses.AppendElement(response.forget());
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(responses);
-}
-
-void
-Cache::RecvAddAllResponse(RequestId aRequestId,
-                          const mozilla::ErrorResult& aError)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (aError.Failed()) {
-    // TODO: Remove this const_cast (bug 1152078).
-    // It is safe for now since this ErrorResult is handed off to us by IPDL
-    // and is thrown into the trash afterwards.
-    promise->MaybeReject(const_cast<ErrorResult&>(aError));
-    return;
-  }
-
-  promise->MaybeResolve(JS::UndefinedHandleValue);
-}
-
-void
-Cache::RecvPutResponse(RequestId aRequestId, nsresult aRv)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(JS::UndefinedHandleValue);
-}
-
-void
-Cache::RecvDeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess)
-{
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(aSuccess);
-}
-
-void
-Cache::RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                        const nsTArray<PCacheRequest>& aRequests)
-{
-  // Convert requests immediately.  This ensures that any stream actors are
-  // cleaned up, even if we error out below.
-  nsAutoTArray<nsRefPtr<Request>, 256> requests;
-  requests.SetCapacity(aRequests.Length());
-
-  for (uint32_t i = 0; i < aRequests.Length(); ++i) {
-    nsRefPtr<Request> request = ToRequest(aRequests[i]);
-    requests.AppendElement(request.forget());
-  }
-
-  nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
-
-  if (NS_FAILED(aRv)) {
-    promise->MaybeReject(aRv);
-    return;
-  }
-
-  promise->MaybeResolve(requests);
-}
-
 nsIGlobalObject*
 Cache::GetGlobalObject() const
 {
   return mGlobal;
 }
 
 #ifdef DEBUG
 void
@@ -533,88 +344,37 @@ Cache::AssertOwningThread() const
 #endif
 
 CachePushStreamChild*
 Cache::CreatePushStream(nsIAsyncInputStream* aStream)
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
   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.
+  return mActor->CreatePushStream(aStream);
 }
 
 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);
-
-  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;
+  mActor->ExecuteOp(mGlobal, promise, aOpArgs.SendAsOpArgs());
+  return promise.forget();
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_Cache_h
 #define mozilla_dom_cache_Cache_h
 
-#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsIGlobalObject;
@@ -28,22 +27,20 @@ class Promise;
 struct CacheQueryOptions;
 class RequestOrUSVString;
 class Response;
 template<typename T> class Optional;
 template<typename T> class Sequence;
 
 namespace cache {
 
+class AutoChildOpArgs;
 class CacheChild;
-class PCacheRequest;
-class PCacheResponse;
-class PCacheResponseOrVoid;
 
-class Cache final : public PromiseNativeHandler
+class Cache final : public nsISupports
                   , public nsWrapperCache
                   , public TypeUtils
 {
 public:
   Cache(nsIGlobalObject* aGlobal, CacheChild* aActor);
 
   // webidl interface methods
   already_AddRefed<Promise>
@@ -71,61 +68,38 @@ public:
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
 
   nsISupports* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto) override;
 
   // Called when CacheChild actor is being destroyed
   void DestroyInternal(CacheChild* aActor);
 
-  // methods forwarded from CacheChild
-  void RecvMatchResponse(RequestId aRequestId, nsresult aRv,
-                         const PCacheResponseOrVoid& aResponse);
-  void RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
-                            const nsTArray<PCacheResponse>& aResponses);
-  void RecvAddAllResponse(RequestId aRequestId,
-                          const mozilla::ErrorResult& aError);
-  void RecvPutResponse(RequestId aRequestId, nsresult aRv);
-
-  void RecvDeleteResponse(RequestId aRequestId, nsresult aRv,
-                          bool aSuccess);
-  void RecvKeysResponse(RequestId aRequestId, nsresult aRv,
-                        const nsTArray<PCacheRequest>& aRequests);
-
   // TypeUtils methods
   virtual nsIGlobalObject*
   GetGlobalObject() const override;
 
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual CachePushStreamChild*
   CreatePushStream(nsIAsyncInputStream* aStream) override;
 
-  // PromiseNativeHandler methods
-  virtual void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
-  virtual void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
-
 private:
   ~Cache();
 
   // Called when we're destroyed or CCed.
   void DisconnectFromActor();
 
-  // TODO: Replace with actor-per-request model during refactor (bug 1110485)
-  RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
-  already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
+  already_AddRefed<Promise>
+  ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv);
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
   CacheChild* mActor;
-  nsTArray<nsRefPtr<Promise>> mRequestPromises;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Cache)
 };
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -4,18 +4,18 @@
  * 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/PCachePushStreamChild.h"
-#include "mozilla/dom/cache/StreamUtils.h"
+#include "mozilla/dom/cache/CacheOpChild.h"
+#include "mozilla/dom/cache/CachePushStreamChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // Declared in ActorUtils.h
 PCacheChild*
 AllocPCacheChild()
@@ -27,25 +27,27 @@ AllocPCacheChild()
 void
 DeallocPCacheChild(PCacheChild* aActor)
 {
   delete aActor;
 }
 
 CacheChild::CacheChild()
   : mListener(nullptr)
+  , mNumChildActors(0)
 {
   MOZ_COUNT_CTOR(cache::CacheChild);
 }
 
 CacheChild::~CacheChild()
 {
   MOZ_COUNT_DTOR(cache::CacheChild);
   NS_ASSERT_OWNINGTHREAD(CacheChild);
   MOZ_ASSERT(!mListener);
+  MOZ_ASSERT(!mNumChildActors);
 }
 
 void
 CacheChild::SetListener(Cache* aListener)
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
   MOZ_ASSERT(!mListener);
   mListener = aListener;
@@ -56,32 +58,59 @@ void
 CacheChild::ClearListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
   MOZ_ASSERT(mListener);
   mListener = nullptr;
 }
 
 void
+CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
+                      const CacheOpArgs& aArgs)
+{
+  mNumChildActors += 1;
+  MOZ_ALWAYS_TRUE(SendPCacheOpConstructor(
+    new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs));
+}
+
+CachePushStreamChild*
+CacheChild::CreatePushStream(nsIAsyncInputStream* aStream)
+{
+  mNumChildActors += 1;
+  auto actor = SendPCachePushStreamConstructor(
+    new CachePushStreamChild(GetFeature(), aStream));
+  MOZ_ASSERT(actor);
+  return static_cast<CachePushStreamChild*>(actor);
+}
+
+void
 CacheChild::StartDestroy()
 {
   nsRefPtr<Cache> listener = mListener;
 
   // 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;
   }
 
   listener->DestroyInternal(this);
 
   // Cache listener should call ClearListener() in DestroyInternal()
   MOZ_ASSERT(!mListener);
 
+  // If we have outstanding child actors, then don't destroy ourself yet.
+  // The child actors should be short lived and we should allow them to complete
+  // if possible.  SendTeardown() will be called when the count drops to zero
+  // in NoteDeletedActor().
+  if (mNumChildActors) {
+    return;
+  }
+
   // Start actor destruction from parent process
   unused << SendTeardown();
 }
 
 void
 CacheChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
@@ -90,114 +119,50 @@ 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;
+  NoteDeletedActor();
+  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);
+  NoteDeletedActor();
   return true;
 }
 
-bool
-CacheChild::RecvAddAllResponse(const RequestId& requestId,
-                               const mozilla::ErrorResult& aError)
+void
+CacheChild::NoteDeletedActor()
 {
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvAddAllResponse(requestId, aError);
-  }
-  return true;
-}
-
-bool
-CacheChild::RecvPutResponse(const RequestId& aRequestId, const nsresult& aRv)
-{
-  NS_ASSERT_OWNINGTHREAD(CacheChild);
-  nsRefPtr<Cache> listener = mListener;
-  if (listener) {
-    listener->RecvPutResponse(aRequestId, aRv);
+  mNumChildActors -= 1;
+  if (!mNumChildActors && !mListener) {
+    unused << SendTeardown();
   }
-  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
@@ -5,76 +5,83 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_CacheChild_h
 #define mozilla_dom_cache_CacheChild_h
 
 #include "mozilla/dom/cache/ActorChild.h"
 #include "mozilla/dom/cache/PCacheChild.h"
 
+class nsIAsyncInputStream;
+class nsIGlobalObject;
+
 namespace mozilla {
 namespace dom {
+
+class Promise;
+
 namespace cache {
 
 class Cache;
+class CacheOpArgs;
+class CachePushStreamChild;
 
 class CacheChild final : public PCacheChild
                        , public ActorChild
 {
 public:
   CacheChild();
   ~CacheChild();
 
   void SetListener(Cache* aListener);
 
   // Must be called by the associated Cache listener in its ActorDestroy()
   // method.  Also, Cache must Send__delete__() the actor in its destructor to
   // trigger ActorDestroy() if it has not been called yet.
   void ClearListener();
 
+  void
+  ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
+            const CacheOpArgs& aArgs);
+
+  CachePushStreamChild*
+  CreatePushStream(nsIAsyncInputStream* aStream);
+
   // ActorChild methods
 
   // Synchronously call ActorDestroy on our Cache listener and then start the
   // actor destruction asynchronously from the parent-side.
   virtual void StartDestroy() override;
 
 private:
   // PCacheChild methods
   virtual void
   ActorDestroy(ActorDestroyReason aReason) override;
 
+  virtual PCacheOpChild*
+  AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  DeallocPCacheOpChild(PCacheOpChild* aActor) override;
+
   virtual PCachePushStreamChild*
   AllocPCachePushStreamChild() override;
 
   virtual bool
   DeallocPCachePushStreamChild(PCachePushStreamChild* aActor) override;
 
-  virtual bool
-  RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
-                    const PCacheResponseOrVoid& aResponse) override;
-  virtual bool
-  RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
-                       nsTArray<PCacheResponse>&& responses) override;
-  virtual bool
-  RecvAddAllResponse(const RequestId& requestId,
-                     const mozilla::ErrorResult& aError) override;
-  virtual bool
-  RecvPutResponse(const RequestId& aRequestId,
-                  const nsresult& aRv) override;
-  virtual bool
-  RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
-                     const bool& result) override;
-  virtual bool
-  RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
-                   nsTArray<PCacheRequest>&& requests) override;
+  // utility methods
+  void
+  NoteDeletedActor();
 
   // 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;
+  uint32_t mNumChildActors;
 
   NS_DECL_OWNINGTHREAD
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
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,248 @@
+/* -*- 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/Promise.h"
+#include "mozilla/dom/Request.h"
+#include "mozilla/dom/Response.h"
+#include "mozilla/dom/cache/Cache.h"
+#include "mozilla/dom/cache/CacheChild.h"
+#include "mozilla/dom/cache/CacheStreamControlChild.h"
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+namespace {
+
+void
+AddFeatureToStreamChild(const CacheReadStream& aReadStream, Feature* aFeature)
+{
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  CacheStreamControlChild* cacheControl =
+    static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
+  if (cacheControl) {
+    cacheControl->SetFeature(aFeature);
+  }
+}
+
+void
+AddFeatureToStreamChild(const CacheResponse& aResponse, Feature* aFeature)
+{
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+
+  if (aResponse.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
+    return;
+  }
+
+  AddFeatureToStreamChild(aResponse.body().get_CacheReadStream(), aFeature);
+}
+
+void
+AddFeatureToStreamChild(const CacheRequest& aRequest, Feature* aFeature)
+{
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+
+  if (aRequest.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
+    return;
+  }
+
+  AddFeatureToStreamChild(aRequest.body().get_CacheReadStream(), aFeature);
+}
+
+} // anonymous namespace
+
+CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
+                           Promise* aPromise)
+  : mGlobal(aGlobal)
+  , mPromise(aPromise)
+{
+  MOZ_ASSERT(mGlobal);
+  MOZ_ASSERT(mPromise);
+
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  SetFeature(aFeature);
+}
+
+CacheOpChild::~CacheOpChild()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+  MOZ_ASSERT(!mPromise);
+}
+
+void
+CacheOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  // If the actor was terminated for some unknown reason, then indicate the
+  // operation is dead.
+  if (mPromise) {
+    mPromise->MaybeReject(NS_ERROR_FAILURE);
+    mPromise = nullptr;
+  }
+
+  RemoveFeature();
+}
+
+bool
+CacheOpChild::Recv__delete__(const ErrorResult& aRv,
+                             const CacheOpResult& aResult)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  if (aRv.Failed()) {
+    MOZ_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
+    // TODO: Remove this const_cast (bug 1152078).
+    // It is safe for now since this ErrorResult is handed off to us by IPDL
+    // and is thrown into the trash afterwards.
+    mPromise->MaybeReject(const_cast<ErrorResult&>(aRv));
+    mPromise = nullptr;
+    return true;
+  }
+
+  switch (aResult.type()) {
+    case CacheOpResult::TCacheMatchResult:
+    {
+      HandleResponse(aResult.get_CacheMatchResult().responseOrVoid());
+      break;
+    }
+    case CacheOpResult::TCacheMatchAllResult:
+    {
+      HandleResponseList(aResult.get_CacheMatchAllResult().responseList());
+      break;
+    }
+    case CacheOpResult::TCacheAddAllResult:
+    case CacheOpResult::TCachePutAllResult:
+    {
+      mPromise->MaybeResolve(JS::UndefinedHandleValue);
+      break;
+    }
+    case CacheOpResult::TCacheDeleteResult:
+    {
+      mPromise->MaybeResolve(aResult.get_CacheDeleteResult().success());
+      break;
+    }
+    case CacheOpResult::TCacheKeysResult:
+    {
+      HandleRequestList(aResult.get_CacheKeysResult().requestList());
+      break;
+    }
+    case CacheOpResult::TStorageMatchResult:
+    {
+      HandleResponse(aResult.get_StorageMatchResult().responseOrVoid());
+      break;
+    }
+    case CacheOpResult::TStorageHasResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
+      break;
+    }
+    case CacheOpResult::TStorageOpenResult:
+    {
+      auto actor = static_cast<CacheChild*>(
+        aResult.get_StorageOpenResult().actorChild());
+      actor->SetFeature(GetFeature());
+      nsRefPtr<Cache> cache = new Cache(mGlobal, actor);
+      mPromise->MaybeResolve(cache);
+      break;
+    }
+    case CacheOpResult::TStorageDeleteResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
+      break;
+    }
+    case CacheOpResult::TStorageKeysResult:
+    {
+      mPromise->MaybeResolve(aResult.get_StorageKeysResult().keyList());
+      break;
+    }
+    default:
+      MOZ_CRASH("Unknown Cache op result type!");
+  }
+
+  mPromise = nullptr;
+
+  return true;
+}
+
+void
+CacheOpChild::StartDestroy()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+
+  // Do not cancel on-going operations when Feature calls this.  Instead, keep
+  // the Worker alive until we are done.
+}
+
+nsIGlobalObject*
+CacheOpChild::GetGlobalObject() const
+{
+  return mGlobal;
+}
+
+#ifdef DEBUG
+void
+CacheOpChild::AssertOwningThread() const
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
+}
+#endif
+
+CachePushStreamChild*
+CacheOpChild::CreatePushStream(nsIAsyncInputStream* aStream)
+{
+  MOZ_CRASH("CacheOpChild should never create a push stream actor!");
+}
+
+void
+CacheOpChild::HandleResponse(const CacheResponseOrVoid& aResponseOrVoid)
+{
+  if (aResponseOrVoid.type() == CacheResponseOrVoid::Tvoid_t) {
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    return;
+  }
+
+  const CacheResponse& cacheResponse = aResponseOrVoid.get_CacheResponse();
+
+  AddFeatureToStreamChild(cacheResponse, GetFeature());
+  nsRefPtr<Response> response = ToResponse(cacheResponse);
+
+  mPromise->MaybeResolve(response);
+}
+
+void
+CacheOpChild::HandleResponseList(const nsTArray<CacheResponse>& aResponseList)
+{
+  nsAutoTArray<nsRefPtr<Response>, 256> responses;
+  responses.SetCapacity(aResponseList.Length());
+
+  for (uint32_t i = 0; i < aResponseList.Length(); ++i) {
+    AddFeatureToStreamChild(aResponseList[i], GetFeature());
+    responses.AppendElement(ToResponse(aResponseList[i]));
+  }
+
+  mPromise->MaybeResolve(responses);
+}
+
+void
+CacheOpChild::HandleRequestList(const nsTArray<CacheRequest>& aRequestList)
+{
+  nsAutoTArray<nsRefPtr<Request>, 256> requests;
+  requests.SetCapacity(aRequestList.Length());
+
+  for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
+    AddFeatureToStreamChild(aRequestList[i], GetFeature());
+    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,80 @@
+/* -*- 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
+{
+  friend class CacheChild;
+  friend class CacheStorageChild;
+
+private:
+  // This class must be constructed by CacheChild or CacheStorageChild using
+  // their ExecuteOp() factory method.
+  CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal, Promise* aPromise);
+  ~CacheOpChild();
+
+  // PCacheOpChild methods
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  virtual bool
+  Recv__delete__(const ErrorResult& aRv, const CacheOpResult& aResult) override;
+
+  // ActorChild methods
+  virtual void
+  StartDestroy() override;
+
+  // TypeUtils methods
+  virtual nsIGlobalObject*
+  GetGlobalObject() const override;
+
+#ifdef DEBUG
+  virtual void
+  AssertOwningThread() const override;
+#endif
+
+  virtual CachePushStreamChild*
+  CreatePushStream(nsIAsyncInputStream* aStream) override;
+
+  // Utility methods
+  void
+  HandleResponse(const CacheResponseOrVoid& aResponseOrVoid);
+
+  void
+  HandleResponseList(const nsTArray<CacheResponse>& aResponseList);
+
+  void
+  HandleRequestList(const nsTArray<CacheRequest>& aRequestList);
+
+  nsCOMPtr<nsIGlobalObject> mGlobal;
+  nsRefPtr<Promise> mPromise;
+
+  NS_DECL_OWNINGTHREAD
+};
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_cache_CacheOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpParent.cpp
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/cache/CacheOpParent.h"
+
+#include "mozilla/unused.h"
+#include "mozilla/dom/cache/AutoUtils.h"
+#include "mozilla/dom/cache/CachePushStreamParent.h"
+#include "mozilla/dom/cache/ReadStream.h"
+#include "mozilla/dom/cache/SavedTypes.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+
+namespace mozilla {
+namespace dom {
+namespace cache {
+
+using mozilla::ipc::FileDescriptorSetParent;
+using mozilla::ipc::PBackgroundParent;
+
+CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
+                             const CacheOpArgs& aOpArgs)
+  : mIpcManager(aIpcManager)
+  , mCacheId(aCacheId)
+  , mNamespace(INVALID_NAMESPACE)
+  , mOpArgs(aOpArgs)
+{
+  MOZ_ASSERT(mIpcManager);
+}
+
+CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager,
+                             Namespace aNamespace, const CacheOpArgs& aOpArgs)
+  : mIpcManager(aIpcManager)
+  , mCacheId(INVALID_CACHE_ID)
+  , mNamespace(aNamespace)
+  , mOpArgs(aOpArgs)
+{
+  MOZ_ASSERT(mIpcManager);
+}
+
+CacheOpParent::~CacheOpParent()
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+}
+
+void
+CacheOpParent::Execute(ManagerId* aManagerId)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  nsRefPtr<Manager> manager;
+  nsresult rv = Manager::GetOrCreate(aManagerId, getter_AddRefs(manager));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    unused << Send__delete__(this, ErrorResult(rv), void_t());
+    return;
+  }
+
+  Execute(manager);
+}
+
+void
+CacheOpParent::Execute(Manager* aManager)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  mManager = aManager;
+
+  // Handle add/addAll op with a FetchPut object
+  if (mOpArgs.type() == CacheOpArgs::TCacheAddAllArgs) {
+    MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
+
+    const CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
+    const nsTArray<CacheRequest>& list = args.requestList();
+
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
+    for (uint32_t i = 0; i < list.Length(); ++i) {
+      requestStreamList.AppendElement(DeserializeCacheStream(list[i].body()));
+    }
+
+    nsRefPtr<FetchPut> fetchPut;
+    nsresult rv = FetchPut::Create(this, mManager, mCacheId, list,
+                                   requestStreamList, getter_AddRefs(fetchPut));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      OnOpComplete(ErrorResult(rv), CacheAddAllResult());
+      return;
+    }
+
+    mFetchPutList.AppendElement(fetchPut.forget());
+    return;
+  }
+
+  // Handle put op
+  if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
+    MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
+
+    const CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
+    const nsTArray<CacheRequestResponse>& list = args.requestResponseList();
+
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
+    nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> responseStreamList;
+
+    for (uint32_t i = 0; i < list.Length(); ++i) {
+      requestStreamList.AppendElement(
+        DeserializeCacheStream(list[i].request().body()));
+      responseStreamList.AppendElement(
+        DeserializeCacheStream(list[i].response().body()));
+    }
+
+    mManager->ExecutePutAll(this, mCacheId, args.requestResponseList(),
+                            requestStreamList, responseStreamList);
+    return;
+  }
+
+  // Handle all other cache ops
+  if (mCacheId != INVALID_CACHE_ID) {
+    MOZ_ASSERT(mNamespace == INVALID_NAMESPACE);
+    mManager->ExecuteCacheOp(this, mCacheId, mOpArgs);
+    return;
+  }
+
+  // Handle all storage ops
+  MOZ_ASSERT(mNamespace != INVALID_NAMESPACE);
+  mManager->ExecuteStorageOp(this, mNamespace, mOpArgs);
+}
+
+void
+CacheOpParent::WaitForVerification(PrincipalVerifier* aVerifier)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(!mManager);
+  MOZ_ASSERT(!mVerifier);
+
+  mVerifier = aVerifier;
+  mVerifier->AddListener(this);
+}
+
+void
+CacheOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+
+  if (mVerifier) {
+    mVerifier->RemoveListener(this);
+    mVerifier = nullptr;
+  }
+
+  for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
+    mFetchPutList[i]->ClearListener();
+  }
+  mFetchPutList.Clear();
+
+  if (mManager) {
+    mManager->RemoveListener(this);
+    mManager = nullptr;
+  }
+
+  mIpcManager = nullptr;
+}
+
+void
+CacheOpParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+
+  mVerifier->RemoveListener(this);
+  mVerifier = nullptr;
+
+  if (NS_WARN_IF(NS_FAILED(aRv))) {
+    unused << Send__delete__(this, ErrorResult(aRv), void_t());
+    return;
+  }
+
+  Execute(aManagerId);
+}
+
+void
+CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                            CacheId aOpenedCacheId,
+                            const nsTArray<SavedResponse>& aSavedResponseList,
+                            const nsTArray<SavedRequest>& aSavedRequestList,
+                            StreamList* aStreamList)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(mIpcManager);
+  MOZ_ASSERT(mManager);
+
+  // Never send an op-specific result if we have an error.  Instead, send
+  // void_t() to ensure that we don't leak actors on the child side.
+  if (aRv.Failed()) {
+    unused << Send__delete__(this, aRv, void_t());
+    aRv.ClearMessage(); // This may contain a TypeError.
+    return;
+  }
+
+  // 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 (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, ErrorResult&& aRv)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheOpParent);
+  MOZ_ASSERT(aFetchPut);
+
+  aFetchPut->ClearListener();
+  MOZ_ALWAYS_TRUE(mFetchPutList.RemoveElement(aFetchPut));
+
+  OnOpComplete(Move(aRv), CacheAddAllResult());
+}
+
+already_AddRefed<nsIInputStream>
+CacheOpParent::DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid)
+{
+  if (aStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  const CacheReadStream& readStream = aStreamOrVoid.get_CacheReadStream();
+
+  // Option 1: A push stream actor was sent for nsPipe data
+  if (readStream.pushStreamParent()) {
+    MOZ_ASSERT(!readStream.controlParent());
+    CachePushStreamParent* pushStream =
+      static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
+    stream = pushStream->TakeReader();
+    MOZ_ASSERT(stream);
+    return stream.forget();
+  }
+
+  // Option 2: One of our own ReadStreams was passed back to us with a stream
+  //           control actor.
+  stream = ReadStream::Create(readStream);
+  if (stream) {
+    return stream.forget();
+  }
+
+  // Option 3: A stream was serialized using normal methods.
+  nsAutoTArray<FileDescriptor, 4> fds;
+  if (readStream.fds().type() ==
+      OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+
+    FileDescriptorSetParent* fdSetActor =
+      static_cast<FileDescriptorSetParent*>(readStream.fds().get_PFileDescriptorSetParent());
+    MOZ_ASSERT(fdSetActor);
+
+    fdSetActor->ForgetFileDescriptors(fds);
+    MOZ_ASSERT(!fds.IsEmpty());
+
+    if (!fdSetActor->Send__delete__(fdSetActor)) {
+      // child process is gone, warn and allow actor to clean up normally
+      NS_WARNING("Cache failed to delete fd set actor.");
+    }
+  }
+
+  return DeserializeInputStream(readStream.params(), fds);
+}
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/cache/CacheOpParent.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_cache_CacheOpParent_h
+#define mozilla_dom_cache_CacheOpParent_h
+
+#include "mozilla/dom/cache/FetchPut.h"
+#include "mozilla/dom/cache/Manager.h"
+#include "mozilla/dom/cache/PCacheOpParent.h"
+#include "mozilla/dom/cache/PrincipalVerifier.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace ipc {
+class PBackgroundParent;
+}
+namespace dom {
+namespace cache {
+
+class CacheOpParent final : public PCacheOpParent
+                          , public PrincipalVerifier::Listener
+                          , public Manager::Listener
+                          , public FetchPut::Listener
+{
+  // to allow use of convenience overrides
+  using Manager::Listener::OnOpComplete;
+
+public:
+  CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager, CacheId aCacheId,
+                const CacheOpArgs& aOpArgs);
+  CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager,
+                Namespace aNamespace, const CacheOpArgs& aOpArgs);
+  ~CacheOpParent();
+
+  void
+  Execute(ManagerId* aManagerId);
+
+  void
+  Execute(Manager* aManager);
+
+  void
+  WaitForVerification(PrincipalVerifier* aVerifier);
+
+private:
+  // PCacheOpParent methods
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  // PrincipalVerifier::Listener methods
+  virtual void
+  OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId) override;
+
+  // Manager::Listener methods
+  virtual void
+  OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+               CacheId aOpenedCacheId,
+               const nsTArray<SavedResponse>& aSavedResponseList,
+               const nsTArray<SavedRequest>& aSavedRequestList,
+               StreamList* aStreamList) override;
+
+  // FetchPut::Listener methods
+  virtual void
+  OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) override;
+
+  // utility methods
+  already_AddRefed<nsIInputStream>
+  DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid);
+
+  mozilla::ipc::PBackgroundParent* mIpcManager;
+  const CacheId mCacheId;
+  const Namespace mNamespace;
+  const CacheOpArgs mOpArgs;
+  nsRefPtr<Manager> mManager;
+  nsRefPtr<PrincipalVerifier> mVerifier;
+  nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
+
+  NS_DECL_OWNINGTHREAD
+};
+
+} // namespace cache
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_cache_CacheOpParent_h
--- a/dom/cache/CacheParent.cpp
+++ b/dom/cache/CacheParent.cpp
@@ -1,37 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheParent.h"
 
-#include "mozilla/DebugOnly.h"
-#include "mozilla/dom/cache/AutoUtils.h"
+#include "mozilla/dom/cache/CacheOpParent.h"
 #include "mozilla/dom/cache/CachePushStreamParent.h"
-#include "mozilla/dom/cache/CacheStreamControlParent.h"
-#include "mozilla/dom/cache/ReadStream.h"
-#include "mozilla/dom/cache/SavedTypes.h"
-#include "mozilla/dom/cache/StreamList.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/PBackgroundParent.h"
-#include "mozilla/ipc/FileDescriptorSetParent.h"
-#include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-using mozilla::dom::ErrNum;
-using mozilla::ipc::FileDescriptorSetParent;
-using mozilla::ipc::PFileDescriptorSetParent;
-
 // Declared in ActorUtils.h
 void
 DeallocPCacheParent(PCacheParent* aActor)
 {
   delete aActor;
 }
 
 CacheParent::CacheParent(cache::Manager* aManager, CacheId aCacheId)
@@ -42,32 +29,58 @@ CacheParent::CacheParent(cache::Manager*
   MOZ_ASSERT(mManager);
   mManager->AddRefCacheId(mCacheId);
 }
 
 CacheParent::~CacheParent()
 {
   MOZ_COUNT_DTOR(cache::CacheParent);
   MOZ_ASSERT(!mManager);
-  MOZ_ASSERT(mFetchPutList.IsEmpty());
 }
 
 void
 CacheParent::ActorDestroy(ActorDestroyReason aReason)
 {
   MOZ_ASSERT(mManager);
-  for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
-    mFetchPutList[i]->ClearListener();
-  }
-  mFetchPutList.Clear();
-  mManager->RemoveListener(this);
   mManager->ReleaseCacheId(mCacheId);
   mManager = nullptr;
 }
 
+PCacheOpParent*
+CacheParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
+{
+  if (aOpArgs.type() != CacheOpArgs::TCacheMatchArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheMatchAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCachePutAllArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheDeleteArgs &&
+      aOpArgs.type() != CacheOpArgs::TCacheKeysArgs)
+  {
+    MOZ_CRASH("Invalid operation sent to Cache actor!");
+  }
+
+  return new CacheOpParent(Manager(), mCacheId, aOpArgs);
+}
+
+bool
+CacheParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+CacheParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
+                                     const CacheOpArgs& aOpArgs)
+{
+  auto actor = static_cast<CacheOpParent*>(aActor);
+  actor->Execute(mManager);
+  return true;
+}
+
 PCachePushStreamParent*
 CacheParent::AllocPCachePushStreamParent()
 {
   return CachePushStreamParent::Create();
 }
 
 bool
 CacheParent::DeallocPCachePushStreamParent(PCachePushStreamParent* aActor)
@@ -81,243 +94,11 @@ CacheParent::RecvTeardown()
 {
   if (!Send__delete__(this)) {
     // child process is gone, warn and allow actor to clean up normally
     NS_WARNING("Cache failed to send delete.");
   }
   return true;
 }
 
-bool
-CacheParent::RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
-                       const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheMatch(this, aRequestId, mCacheId, aRequest,
-                       aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvMatchAll(const RequestId& aRequestId,
-                          const PCacheRequestOrVoid& aRequest,
-                          const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheMatchAll(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvAddAll(const RequestId& aRequestId,
-                        nsTArray<PCacheRequest>&& aRequests)
-{
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreams;
-  requestStreams.SetCapacity(aRequests.Length());
-
-  for (uint32_t i = 0; i < aRequests.Length(); ++i) {
-    requestStreams.AppendElement(DeserializeCacheStream(aRequests[i].body()));
-  }
-
-  nsRefPtr<FetchPut> fetchPut;
-  nsresult rv = FetchPut::Create(this, mManager, aRequestId, mCacheId,
-                                 aRequests, requestStreams,
-                                 getter_AddRefs(fetchPut));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR);
-    ErrorResult error;
-    error.Throw(rv);
-    if (!SendAddAllResponse(aRequestId, error)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to send AddAll response.");
-    }
-    return true;
-  }
-
-  mFetchPutList.AppendElement(fetchPut.forget());
-
-  return true;
-}
-
-bool
-CacheParent::RecvPut(const RequestId& aRequestId,
-                     const CacheRequestResponse& aPut)
-{
-  MOZ_ASSERT(mManager);
-
-  nsAutoTArray<CacheRequestResponse, 1> putList;
-  putList.AppendElement(aPut);
-
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> requestStreamList;
-  nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> responseStreamList;
-
-  requestStreamList.AppendElement(
-    DeserializeCacheStream(aPut.request().body()));
-  responseStreamList.AppendElement(
-    DeserializeCacheStream(aPut.response().body()));
-
-
-  mManager->CachePutAll(this, aRequestId, mCacheId, putList, requestStreamList,
-                        responseStreamList);
-
-  return true;
-}
-
-bool
-CacheParent::RecvDelete(const RequestId& aRequestId,
-                        const PCacheRequest& aRequest,
-                        const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheDelete(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-bool
-CacheParent::RecvKeys(const RequestId& aRequestId,
-                      const PCacheRequestOrVoid& aRequest,
-                      const PCacheQueryParams& aParams)
-{
-  MOZ_ASSERT(mManager);
-  mManager->CacheKeys(this, aRequestId, mCacheId, aRequest, aParams);
-  return true;
-}
-
-void
-CacheParent::OnCacheMatch(RequestId aRequestId, nsresult aRv,
-                          const SavedResponse* aSavedResponse,
-                          StreamList* aStreamList)
-{
-  AutoParentResponseOrVoid response(Manager());
-
-  // no match
-  if (NS_FAILED(aRv) || !aSavedResponse || !aStreamList) {
-    if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to send Match response.");
-    }
-    return;
-  }
-
-  if (aSavedResponse) {
-    response.Add(*aSavedResponse, aStreamList);
-  }
-
-  if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Match response.");
-  }
-}
-
-void
-CacheParent::OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
-                             const nsTArray<SavedResponse>& aSavedResponses,
-                             StreamList* aStreamList)
-{
-  AutoParentResponseList responses(Manager(), aSavedResponses.Length());
-
-  for (uint32_t i = 0; i < aSavedResponses.Length(); ++i) {
-    responses.Add(aSavedResponses[i], aStreamList);
-  }
-
-  if (!SendMatchAllResponse(aRequestId, aRv, responses.SendAsResponseList())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send MatchAll response.");
-  }
-}
-
-void
-CacheParent::OnCachePutAll(RequestId aRequestId, nsresult aRv)
-{
-  if (!SendPutResponse(aRequestId, aRv)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Put response.");
-  }
-}
-
-void
-CacheParent::OnCacheDelete(RequestId aRequestId, nsresult aRv, bool aSuccess)
-{
-  if (!SendDeleteResponse(aRequestId, aRv, aSuccess)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Delete response.");
-  }
-}
-
-void
-CacheParent::OnCacheKeys(RequestId aRequestId, nsresult aRv,
-                         const nsTArray<SavedRequest>& aSavedRequests,
-                         StreamList* aStreamList)
-{
-  AutoParentRequestList requests(Manager(), aSavedRequests.Length());
-
-  for (uint32_t i = 0; i < aSavedRequests.Length(); ++i) {
-    requests.Add(aSavedRequests[i], aStreamList);
-  }
-
-  if (!SendKeysResponse(aRequestId, aRv, requests.SendAsRequestList())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send Keys response.");
-  }
-}
-
-void
-CacheParent::OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, const ErrorResult& aRv)
-{
-  aFetchPut->ClearListener();
-  mFetchPutList.RemoveElement(aFetchPut);
-  if (!SendAddAllResponse(aRequestId, aRv)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("Cache failed to send AddAll response.");
-  }
-}
-
-already_AddRefed<nsIInputStream>
-CacheParent::DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid)
-{
-  if (aStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIInputStream> stream;
-  const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
-
-  // Option 1: A push stream actor was sent for nsPipe data
-  if (readStream.pushStreamParent()) {
-    MOZ_ASSERT(!readStream.controlParent());
-    CachePushStreamParent* pushStream =
-      static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
-    stream = pushStream->TakeReader();
-    MOZ_ASSERT(stream);
-    return stream.forget();
-  }
-
-  // Option 2: One of our own ReadStreams was passed back to us with a stream
-  //           control actor.
-  stream = ReadStream::Create(readStream);
-  if (stream) {
-    return stream.forget();
-  }
-
-  // Option 3: A stream was serialized using normal methods.
-  nsAutoTArray<FileDescriptor, 4> fds;
-  if (readStream.fds().type() ==
-      OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-
-    FileDescriptorSetParent* fdSetActor =
-      static_cast<FileDescriptorSetParent*>(readStream.fds().get_PFileDescriptorSetParent());
-    MOZ_ASSERT(fdSetActor);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    if (!fdSetActor->Send__delete__(fdSetActor)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("Cache failed to delete fd set actor.");
-    }
-  }
-
-  return DeserializeInputStream(readStream.params(), fds);
-}
-
 } // namespace cache
 } // namespace dom
 } // namesapce mozilla
--- a/dom/cache/CacheParent.h
+++ b/dom/cache/CacheParent.h
@@ -2,86 +2,55 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_CacheParent_h
 #define mozilla_dom_cache_CacheParent_h
 
-#include "mozilla/dom/cache/FetchPut.h"
-#include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/PCacheParent.h"
 #include "mozilla/dom/cache/Types.h"
 
-struct nsID;
-template <class T> class nsRefPtr;
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-struct SavedResponse;
+class Manager;
 
 class CacheParent final : public PCacheParent
-                        , public Manager::Listener
-                        , public FetchPut::Listener
 {
 public:
   CacheParent(cache::Manager* aManager, CacheId aCacheId);
   virtual ~CacheParent();
 
 private:
-  // PCacheParent method
+  // PCacheParent methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
-  virtual PCachePushStreamParent* AllocPCachePushStreamParent() override;
-  virtual bool DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
-  virtual bool RecvTeardown() override;
-  virtual bool
-  RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
-            const PCacheQueryParams& aParams) override;
+
+  virtual PCacheOpParent*
+  AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
+
   virtual bool
-  RecvMatchAll(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
-               const PCacheQueryParams& aParams) override;
-  virtual bool
-  RecvAddAll(const RequestId& aRequestId,
-             nsTArray<PCacheRequest>&& aRequests) override;
-  virtual bool
-  RecvPut(const RequestId& aRequestId,
-          const CacheRequestResponse& aPut) override;
-  virtual bool
-  RecvDelete(const RequestId& aRequestId, const PCacheRequest& aRequest,
-             const PCacheQueryParams& aParams) override;
+  DeallocPCacheOpParent(PCacheOpParent* aActor) override;
+
   virtual bool
-  RecvKeys(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
-           const PCacheQueryParams& aParams) override;
+  RecvPCacheOpConstructor(PCacheOpParent* actor,
+                          const CacheOpArgs& aOpArgs) override;
+
+  virtual PCachePushStreamParent*
+  AllocPCachePushStreamParent() override;
 
-  // Manager::Listener methods
-  virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
-                            const SavedResponse* aSavedResponse,
-                            StreamList* aStreamList) override;
-  virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
-                               const nsTArray<SavedResponse>& aSavedResponses,
-                               StreamList* aStreamList) override;
-  virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
-  virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
-                             bool aSuccess) override;
-  virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
-                           const nsTArray<SavedRequest>& aSavedRequests,
-                           StreamList* aStreamList) override;
+  virtual bool
+  DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
 
-  // FetchPut::Listener methods
-  virtual void OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId,
-                          const mozilla::ErrorResult& aRv) override;
-
-  already_AddRefed<nsIInputStream>
-  DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid);
+  virtual bool
+  RecvTeardown() override;
 
   nsRefPtr<cache::Manager> mManager;
   const CacheId mCacheId;
-  nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_CacheParent_h
--- a/dom/cache/CachePushStreamChild.h
+++ b/dom/cache/CachePushStreamChild.h
@@ -15,27 +15,30 @@ class nsIAsyncInputStream;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 class CachePushStreamChild final : public PCachePushStreamChild
                                  , public ActorChild
 {
+  friend class CacheChild;
+
 public:
-  CachePushStreamChild(Feature* aFeature, nsIAsyncInputStream* aStream);
-  ~CachePushStreamChild();
+  void Start();
 
   virtual void StartDestroy() override;
 
-  void Start();
-
 private:
   class Callback;
 
+  // This class must be constructed using CacheChild::CreatePushStream()
+  CachePushStreamChild(Feature* aFeature, nsIAsyncInputStream* aStream);
+  ~CachePushStreamChild();
+
   // PCachePushStreamChild methods
   virtual void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   void DoRead();
 
   void Wait();
 
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -36,34 +36,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 +172,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);
+  CacheQueryParams params;
+  ToCacheQueryParams(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(CacheRequest(), 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 +363,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 +383,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 +404,46 @@ 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;
+    }
+    mActor->ExecuteOp(mGlobal, 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"
@@ -33,25 +32,22 @@ namespace dom {
 class Promise;
 
 namespace workers {
   class WorkerPrivate;
 }
 
 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 +72,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
@@ -3,32 +3,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 "mozilla/dom/cache/CacheStorageChild.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/CacheChild.h"
+#include "mozilla/dom/cache/CacheOpChild.h"
 #include "mozilla/dom/cache/CacheStorage.h"
-#include "mozilla/dom/cache/StreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 // declared in ActorUtils.h
 void
 DeallocPCacheStorageChild(PCacheStorageChild* aActor)
 {
   delete aActor;
 }
 
 CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature)
   : mListener(aListener)
+  , mNumChildActors(0)
 {
   MOZ_COUNT_CTOR(cache::CacheStorageChild);
   MOZ_ASSERT(mListener);
 
   SetFeature(aFeature);
 }
 
 CacheStorageChild::~CacheStorageChild()
@@ -42,16 +43,25 @@ void
 CacheStorageChild::ClearListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
   MOZ_ASSERT(mListener);
   mListener = nullptr;
 }
 
 void
+CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
+                             const CacheOpArgs& aArgs)
+{
+  mNumChildActors += 1;
+  unused << SendPCacheOpConstructor(
+    new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs);
+}
+
+void
 CacheStorageChild::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
 
   nsRefPtr<CacheStorage> listener = mListener;
 
   // StartDestroy() can get called from either CacheStorage or the Feature.
   // Theoretically we can get double called if the right race happens.  Handle
@@ -60,16 +70,24 @@ CacheStorageChild::StartDestroy()
     return;
   }
 
   listener->DestroyInternal(this);
 
   // CacheStorage listener should call ClearListener() in DestroyInternal()
   MOZ_ASSERT(!mListener);
 
+  // If we have outstanding child actors, then don't destroy ourself yet.
+  // The child actors should be short lived and we should allow them to complete
+  // if possible.  SendTeardown() will be called when the count drops to zero
+  // in NoteDeletedActor().
+  if (mNumChildActors) {
+    return;
+  }
+
   // Start actor destruction from parent process
   unused << SendTeardown();
 }
 
 void
 CacheStorageChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
@@ -78,99 +96,36 @@ 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);
+  delete aActor;
+  NoteDeletedActor();
   return true;
 }
 
-bool
-CacheStorageChild::RecvDeleteResponse(const RequestId& aRequestId,
-                                      const nsresult& aRv,
-                                      const bool& aResult)
+void
+CacheStorageChild::NoteDeletedActor()
 {
-  NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  nsRefPtr<CacheStorage> listener = mListener;
-  if (listener) {
-    listener->RecvDeleteResponse(aRequestId, aRv, aResult);
+  MOZ_ASSERT(mNumChildActors);
+  mNumChildActors -= 1;
+  if (!mNumChildActors && !mListener) {
+    unused << SendTeardown();
   }
-  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);
-  }
-  return true;
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorageChild.h
+++ b/dom/cache/CacheStorageChild.h
@@ -6,67 +6,72 @@
 
 #ifndef mozilla_dom_cache_CacheStorageChild_h
 #define mozilla_dom_cache_CacheStorageChild_h
 
 #include "mozilla/dom/cache/ActorChild.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/PCacheStorageChild.h"
 
+class nsIGlobalObject;
+
 namespace mozilla {
 namespace dom {
+
+class Promise;
+
 namespace cache {
 
+class CacheOpArgs;
 class CacheStorage;
 class PCacheChild;
 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();
 
+  void
+  ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
+            const CacheOpArgs& aArgs);
+
   // 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;
+
+  // utility methods
+  void
+  NoteDeletedActor();
 
   // 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;
+  uint32_t mNumChildActors;
 
   NS_DECL_OWNINGTHREAD
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/cache/CacheStorageParent.cpp
+++ b/dom/cache/CacheStorageParent.cpp
@@ -1,38 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheStorageParent.h"
 
+#include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/cache/ActorUtils.h"
-#include "mozilla/dom/cache/AutoUtils.h"
-#include "mozilla/dom/cache/CacheParent.h"
-#include "mozilla/dom/cache/CacheStreamControlParent.h"
-#include "mozilla/dom/cache/Manager.h"
+#include "mozilla/dom/cache/CacheOpParent.h"
 #include "mozilla/dom/cache/ManagerId.h"
-#include "mozilla/dom/cache/ReadStream.h"
-#include "mozilla/dom/cache/SavedTypes.h"
-#include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/ipc/PBackgroundParent.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/PFileDescriptorSetParent.h"
-#include "mozilla/DebugOnly.h"
-#include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::PBackgroundParent;
-using mozilla::ipc::PFileDescriptorSetParent;
 using mozilla::ipc::PrincipalInfo;
 
 // declared in ActorUtils.h
 PCacheStorageParent*
 AllocPCacheStorageParent(PBackgroundParent* aManagingActor,
                          Namespace aNamespace,
                          const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
 {
@@ -60,391 +50,93 @@ CacheStorageParent::CacheStorageParent(P
                                                    aPrincipalInfo);
   MOZ_ASSERT(mVerifier);
 }
 
 CacheStorageParent::~CacheStorageParent()
 {
   MOZ_COUNT_DTOR(cache::CacheStorageParent);
   MOZ_ASSERT(!mVerifier);
-  MOZ_ASSERT(!mManager);
 }
 
 void
 CacheStorageParent::ActorDestroy(ActorDestroyReason aReason)
 {
   if (mVerifier) {
-    mVerifier->ClearListener();
+    mVerifier->RemoveListener(this);
     mVerifier = nullptr;
   }
+}
+
+PCacheOpParent*
+CacheStorageParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
+{
+  if (aOpArgs.type() != CacheOpArgs::TStorageMatchArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageHasArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageOpenArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageDeleteArgs &&
+      aOpArgs.type() != CacheOpArgs::TStorageKeysArgs)
+  {
+    MOZ_CRASH("Invalid operation sent to CacheStorage actor!");
+  }
 
-  if (mManager) {
-    MOZ_ASSERT(!mActiveRequests.IsEmpty());
-    mManager->RemoveListener(this);
-    mManager = nullptr;
+  return new CacheOpParent(Manager(), mNamespace, aOpArgs);
+}
+
+bool
+CacheStorageParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+CacheStorageParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
+                                            const CacheOpArgs& aOpArgs)
+{
+  auto actor = static_cast<CacheOpParent*>(aActor);
+
+  if (mVerifier) {
+    MOZ_ASSERT(!mManagerId);
+    actor->WaitForVerification(mVerifier);
+    return true;
   }
+
+  if (NS_FAILED(mVerifiedStatus)) {
+    unused << CacheOpParent::Send__delete__(actor, ErrorResult(mVerifiedStatus),
+                                            void_t());
+    return true;
+  }
+
+  MOZ_ASSERT(mManagerId);
+  actor->Execute(mManagerId);
+  return true;
 }
 
 bool
 CacheStorageParent::RecvTeardown()
 {
   if (!Send__delete__(this)) {
     // child process is gone, warn and allow actor to clean up normally
     NS_WARNING("CacheStorage failed to delete actor.");
   }
   return true;
 }
 
-bool
-CacheStorageParent::RecvMatch(const RequestId& aRequestId,
-                              const PCacheRequest& aRequest,
-                              const PCacheQueryParams& aParams)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendMatchResponse(aRequestId, mVerifiedStatus, void_t())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_MATCH;
-    entry->mRequestId = aRequestId;
-    entry->mRequest = aRequest;
-    entry->mParams = aParams;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendMatchResponse(aRequestId, rv, void_t())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return true;
-  }
-
-  manager->StorageMatch(this, aRequestId, mNamespace, aRequest,
-                        aParams);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvHas(const RequestId& aRequestId, const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendHasResponse(aRequestId, mVerifiedStatus, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Has response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_HAS;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendHasResponse(aRequestId, rv, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Has response.");
-    }
-    return true;
-  }
-
-  manager->StorageHas(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvOpen(const RequestId& aRequestId, const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendOpenResponse(aRequestId, mVerifiedStatus, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_OPEN;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendOpenResponse(aRequestId, rv, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return true;
-  }
-
-  manager->StorageOpen(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvDelete(const RequestId& aRequestId,
-                               const nsString& aKey)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendDeleteResponse(aRequestId, mVerifiedStatus, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Delete response.");
-    }
-    return true;
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_DELETE;
-    entry->mRequestId = aRequestId;
-    entry->mKey = aKey;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendDeleteResponse(aRequestId, rv, false)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Delete response.");
-    }
-    return true;
-  }
-
-  manager->StorageDelete(this, aRequestId, mNamespace, aKey);
-
-  return true;
-}
-
-bool
-CacheStorageParent::RecvKeys(const RequestId& aRequestId)
-{
-  if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
-    if (!SendKeysResponse(aRequestId, mVerifiedStatus, nsTArray<nsString>())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Keys response.");
-    }
-  }
-
-  // queue requests if we are still waiting for principal verification
-  if (!mManagerId) {
-    Entry* entry = mPendingRequests.AppendElement();
-    entry->mOp = OP_DELETE;
-    entry->mRequestId = aRequestId;
-    return true;
-  }
-
-  nsRefPtr<cache::Manager> manager;
-  nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    if (!SendKeysResponse(aRequestId, rv, nsTArray<nsString>())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Keys response.");
-    }
-    return true;
-  }
-
-  manager->StorageKeys(this, aRequestId, mNamespace);
-
-  return true;
-}
-
 void
 CacheStorageParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
 {
   MOZ_ASSERT(mVerifier);
   MOZ_ASSERT(!mManagerId);
-  MOZ_ASSERT(!mManager);
   MOZ_ASSERT(NS_SUCCEEDED(mVerifiedStatus));
 
   if (NS_WARN_IF(NS_FAILED(aRv))) {
     mVerifiedStatus = aRv;
   }
 
   mManagerId = aManagerId;
-  mVerifier->ClearListener();
+  mVerifier->RemoveListener(this);
   mVerifier = nullptr;
-
-  RetryPendingRequests();
-}
-
-void
-CacheStorageParent::OnStorageMatch(RequestId aRequestId, nsresult aRv,
-                                   const SavedResponse* aSavedResponse,
-                                   StreamList* aStreamList)
-{
-  PCacheResponseOrVoid responseOrVoid;
-
-  ReleaseManager(aRequestId);
-
-  AutoParentResponseOrVoid response(Manager());
-
-  // no match
-  if (NS_FAILED(aRv) || !aSavedResponse) {
-    if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Match response.");
-    }
-    return;
-  }
-
-  if (aSavedResponse) {
-    response.Add(*aSavedResponse, aStreamList);
-  }
-
-  if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Match response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageHas(RequestId aRequestId, nsresult aRv,
-                                 bool aCacheFound)
-{
-  ReleaseManager(aRequestId);
-  if (!SendHasResponse(aRequestId, aRv, aCacheFound)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Has response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageOpen(RequestId aRequestId, nsresult aRv,
-                                  CacheId aCacheId)
-{
-  if (NS_FAILED(aRv)) {
-    ReleaseManager(aRequestId);
-    if (!SendOpenResponse(aRequestId, aRv, nullptr)) {
-      // child process is gone, warn and allow actor to clean up normally
-      NS_WARNING("CacheStorage failed to send Open response.");
-    }
-    return;
-  }
-
-  MOZ_ASSERT(mManager);
-  CacheParent* actor = new CacheParent(mManager, aCacheId);
-
-  ReleaseManager(aRequestId);
-
-  PCacheParent* base = Manager()->SendPCacheConstructor(actor);
-  actor = static_cast<CacheParent*>(base);
-  if (!SendOpenResponse(aRequestId, aRv, actor)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Open response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageDelete(RequestId aRequestId, nsresult aRv,
-                                    bool aCacheDeleted)
-{
-  ReleaseManager(aRequestId);
-  if (!SendDeleteResponse(aRequestId, aRv, aCacheDeleted)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Delete response.");
-  }
-}
-
-void
-CacheStorageParent::OnStorageKeys(RequestId aRequestId, nsresult aRv,
-                                  const nsTArray<nsString>& aKeys)
-{
-  ReleaseManager(aRequestId);
-  if (!SendKeysResponse(aRequestId, aRv, aKeys)) {
-    // child process is gone, warn and allow actor to clean up normally
-    NS_WARNING("CacheStorage failed to send Keys response.");
-  }
-}
-
-void
-CacheStorageParent::RetryPendingRequests()
-{
-  MOZ_ASSERT(mManagerId || NS_FAILED(mVerifiedStatus));
-  for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
-    const Entry& entry = mPendingRequests[i];
-    switch(entry.mOp) {
-      case OP_MATCH:
-        RecvMatch(entry.mRequestId, entry.mRequest, entry.mParams);
-        break;
-      case OP_HAS:
-        RecvHas(entry.mRequestId, entry.mKey);
-        break;
-      case OP_OPEN:
-        RecvOpen(entry.mRequestId, entry.mKey);
-        break;
-      case OP_DELETE:
-        RecvDelete(entry.mRequestId, entry.mKey);
-        break;
-      case OP_KEYS:
-        RecvKeys(entry.mRequestId);
-        break;
-      default:
-        MOZ_ASSERT_UNREACHABLE("Pending request within unknown op");
-    }
-  }
-  mPendingRequests.Clear();
-  mPendingRequests.Compact();
-}
-
-nsresult
-CacheStorageParent::RequestManager(RequestId aRequestId,
-                                   cache::Manager** aManagerOut)
-{
-  MOZ_ASSERT(!mActiveRequests.Contains(aRequestId));
-  nsRefPtr<cache::Manager> ref = mManager;
-  if (!ref) {
-    MOZ_ASSERT(mActiveRequests.IsEmpty());
-    nsresult rv = cache::Manager::GetOrCreate(mManagerId, getter_AddRefs(ref));
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    mManager = ref;
-  }
-  mActiveRequests.AppendElement(aRequestId);
-  ref.forget(aManagerOut);
-  return NS_OK;
-}
-
-void
-CacheStorageParent::ReleaseManager(RequestId aRequestId)
-{
-  // Note that if the child process dies we also clean up the mManager in
-  // ActorDestroy().  There is no race with this method, however, because
-  // ActorDestroy removes this object from the Manager's listener list.
-  // Therefore ReleaseManager() should never be called after ActorDestroy()
-  // runs.
-  MOZ_ASSERT(mManager);
-  MOZ_ASSERT(!mActiveRequests.IsEmpty());
-
-  MOZ_ALWAYS_TRUE(mActiveRequests.RemoveElement(aRequestId));
-
-  if (mActiveRequests.IsEmpty()) {
-    mManager->RemoveListener(this);
-    mManager = nullptr;
-  }
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorageParent.h
+++ b/dom/cache/CacheStorageParent.h
@@ -2,107 +2,59 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_CacheStorageParent_h
 #define mozilla_dom_cache_CacheStorageParent_h
 
-#include "mozilla/dom/cache/CacheInitData.h"
 #include "mozilla/dom/cache/PCacheStorageParent.h"
-#include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/PrincipalVerifier.h"
 #include "mozilla/dom/cache/Types.h"
 
-template <class T> class nsRefPtr;
-
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-class CacheStreamControlParent;
 class ManagerId;
 
 class CacheStorageParent final : public PCacheStorageParent
                                , public PrincipalVerifier::Listener
-                               , public Manager::Listener
 {
 public:
   CacheStorageParent(PBackgroundParent* aManagingActor, Namespace aNamespace,
                      const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
   virtual ~CacheStorageParent();
 
 private:
   // PCacheStorageParent methods
-  virtual void ActorDestroy(ActorDestroyReason aReason) override;
-  virtual bool RecvTeardown() override;
-  virtual bool RecvMatch(const RequestId& aRequestId,
-                         const PCacheRequest& aRequest,
-                         const PCacheQueryParams& aParams) override;
-  virtual bool RecvHas(const RequestId& aRequestId,
-                       const nsString& aKey) override;
-  virtual bool RecvOpen(const RequestId& aRequestId,
-                        const nsString& aKey) override;
-  virtual bool RecvDelete(const RequestId& aRequestId,
-                          const nsString& aKey) override;
-  virtual bool RecvKeys(const RequestId& aRequestId) override;
+  virtual void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  virtual PCacheOpParent*
+  AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  DeallocPCacheOpParent(PCacheOpParent* aActor) override;
+
+  virtual bool
+  RecvPCacheOpConstructor(PCacheOpParent* actor,
+                          const CacheOpArgs& aOpArgs) override;
+
+  virtual bool
+  RecvTeardown() override;
 
   // PrincipalVerifier::Listener methods
   virtual void OnPrincipalVerified(nsresult aRv,
                                    ManagerId* aManagerId) override;
 
-  // Manager::Listener methods
-  virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
-                              const SavedResponse* aResponse,
-                              StreamList* aStreamList) override;
-  virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
-                            bool aCacheFound) override;
-  virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
-                             CacheId aCacheId) override;
-  virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
-                               bool aCacheDeleted) override;
-  virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
-                             const nsTArray<nsString>& aKeys) override;
-
-  CacheStreamControlParent*
-  SerializeReadStream(CacheStreamControlParent *aStreamControl, const nsID& aId,
-                      StreamList* aStreamList,
-                      PCacheReadStream* aReadStreamOut);
-
-  void RetryPendingRequests();
-
-  nsresult RequestManager(RequestId aRequestId, cache::Manager** aManagerOut);
-  void ReleaseManager(RequestId aRequestId);
-
   const Namespace mNamespace;
   nsRefPtr<PrincipalVerifier> mVerifier;
   nsresult mVerifiedStatus;
   nsRefPtr<ManagerId> mManagerId;
-  nsRefPtr<cache::Manager> mManager;
-
-  enum Op
-  {
-    OP_MATCH,
-    OP_HAS,
-    OP_OPEN,
-    OP_DELETE,
-    OP_KEYS
-  };
-
-  struct Entry
-  {
-    Op mOp;
-    RequestId mRequestId;
-    nsString mKey;
-    PCacheRequest mRequest;
-    PCacheQueryParams mParams;
-  };
-
-  nsTArray<Entry> mPendingRequests;
-  nsTArray<RequestId> mActiveRequests;
 };
 
 } // namesapce cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_CacheStorageParent_h
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -4,17 +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/CacheStreamControlChild.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace dom {
@@ -36,16 +36,17 @@ AllocPCacheStreamControlChild()
 void
 DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
 {
   delete aActor;
 }
 
 CacheStreamControlChild::CacheStreamControlChild()
   : mDestroyStarted(false)
+  , mDestroyDelayed(false)
 {
   MOZ_COUNT_CTOR(cache::CacheStreamControlChild);
 }
 
 CacheStreamControlChild::~CacheStreamControlChild()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   MOZ_COUNT_DTOR(cache::CacheStreamControlChild);
@@ -58,31 +59,46 @@ CacheStreamControlChild::StartDestroy()
   // This can get called twice under some circumstances.  For example, if the
   // actor is added to a Feature that has already been notified and the Cache
   // actor has no mListener.
   if (mDestroyStarted) {
     return;
   }
   mDestroyStarted = true;
 
+  // If any of the streams have started to be read, then wait for them to close
+  // naturally.
+  if (HasEverBeenRead()) {
+    // Note that we are delaying so that we can re-check for active streams
+    // in NoteClosedAfterForget().
+    mDestroyDelayed = true;
+    return;
+  }
+
+  // Otherwise, if the streams have not been touched then just pre-emptively
+  // close them now.  This handles the case where someone retrieves a Response
+  // from the Cache, but never accesses the body.  We should not keep the
+  // Worker alive until that Response is GC'd just because of its ignored
+  // body stream.
+
   // Begin shutting down all streams.  This is the same as if the parent had
   // asked us to shutdown.  So simulate the CloseAll IPC message.
   RecvCloseAll();
 }
 
 void
-CacheStreamControlChild::SerializeControl(PCacheReadStream* aReadStreamOut)
+CacheStreamControlChild::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   aReadStreamOut->controlParent() = nullptr;
   aReadStreamOut->controlChild() = this;
 }
 
 void
-CacheStreamControlChild::SerializeFds(PCacheReadStream* aReadStreamOut,
+CacheStreamControlChild::SerializeFds(CacheReadStream* aReadStreamOut,
                                       const nsTArray<FileDescriptor>& aFds)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   PFileDescriptorSetChild* fdSet = nullptr;
   if (!aFds.IsEmpty()) {
     fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
     for (uint32_t i = 1; i < aFds.Length(); ++i) {
       unused << fdSet->SendAddFileDescriptor(aFds[i]);
@@ -92,17 +108,17 @@ CacheStreamControlChild::SerializeFds(PC
   if (fdSet) {
     aReadStreamOut->fds() = fdSet;
   } else {
     aReadStreamOut->fds() = void_t();
   }
 }
 
 void
-CacheStreamControlChild::DeserializeFds(const PCacheReadStream& aReadStream,
+CacheStreamControlChild::DeserializeFds(const CacheReadStream& aReadStream,
                                         nsTArray<FileDescriptor>& aFdsOut)
 {
   if (aReadStream.fds().type() !=
       OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
     return;
   }
 
   auto fdSetActor = static_cast<FileDescriptorSetChild*>(
@@ -115,16 +131,25 @@ CacheStreamControlChild::DeserializeFds(
   unused << fdSetActor->Send__delete__(fdSetActor);
 }
 
 void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   unused << SendNoteClosed(aId);
+
+  // A stream has closed.  If we delayed StartDestry() due to this stream
+  // being read, then we should check to see if any of the remaining streams
+  // are active.  If none of our other streams have been read, then we can
+  // proceed with the shutdown now.
+  if (mDestroyDelayed && !HasEverBeenRead()) {
+    mDestroyDelayed = false;
+    RecvCloseAll();
+  }
 }
 
 #ifdef DEBUG
 void
 CacheStreamControlChild::AssertOwningThread()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
 }
--- a/dom/cache/CacheStreamControlChild.h
+++ b/dom/cache/CacheStreamControlChild.h
@@ -26,24 +26,24 @@ public:
   CacheStreamControlChild();
   ~CacheStreamControlChild();
 
   // ActorChild methods
   virtual void StartDestroy() override;
 
   // StreamControl methods
   virtual void
-  SerializeControl(PCacheReadStream* aReadStreamOut) override;
+  SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
-  SerializeFds(PCacheReadStream* aReadStreamOut,
+  SerializeFds(CacheReadStream* aReadStreamOut,
                const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
 
   virtual void
-  DeserializeFds(const PCacheReadStream& aReadStream,
+  DeserializeFds(const CacheReadStream& aReadStream,
                  nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
 
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
@@ -51,16 +51,17 @@ private:
 #endif
 
   // PCacheStreamControlChild methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
   virtual bool RecvClose(const nsID& aId) override;
   virtual bool RecvCloseAll() override;
 
   bool mDestroyStarted;
+  bool mDestroyDelayed;
 
   NS_DECL_OWNINGTHREAD
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/unused.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
@@ -40,25 +40,25 @@ CacheStreamControlParent::CacheStreamCon
 CacheStreamControlParent::~CacheStreamControlParent()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   MOZ_ASSERT(!mStreamList);
   MOZ_COUNT_DTOR(cache::CacheStreamControlParent);
 }
 
 void
-CacheStreamControlParent::SerializeControl(PCacheReadStream* aReadStreamOut)
+CacheStreamControlParent::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   aReadStreamOut->controlChild() = nullptr;
   aReadStreamOut->controlParent() = this;
 }
 
 void
-CacheStreamControlParent::SerializeFds(PCacheReadStream* aReadStreamOut,
+CacheStreamControlParent::SerializeFds(CacheReadStream* aReadStreamOut,
                                        const nsTArray<FileDescriptor>& aFds)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   PFileDescriptorSetParent* fdSet = nullptr;
   if (!aFds.IsEmpty()) {
     fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
     for (uint32_t i = 1; i < aFds.Length(); ++i) {
       unused << fdSet->SendAddFileDescriptor(aFds[i]);
@@ -68,17 +68,17 @@ CacheStreamControlParent::SerializeFds(P
   if (fdSet) {
     aReadStreamOut->fds() = fdSet;
   } else {
     aReadStreamOut->fds() = void_t();
   }
 }
 
 void
-CacheStreamControlParent::DeserializeFds(const PCacheReadStream& aReadStream,
+CacheStreamControlParent::DeserializeFds(const CacheReadStream& aReadStream,
                                          nsTArray<FileDescriptor>& aFdsOut)
 {
   if (aReadStream.fds().type() !=
       OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
     return;
   }
 
   FileDescriptorSetParent* fdSetActor =
--- a/dom/cache/CacheStreamControlParent.h
+++ b/dom/cache/CacheStreamControlParent.h
@@ -27,24 +27,24 @@ public:
 
   void SetStreamList(StreamList* aStreamList);
   void Close(const nsID& aId);
   void CloseAll();
   void Shutdown();
 
   // StreamControl methods
   virtual void
-  SerializeControl(PCacheReadStream* aReadStreamOut) override;
+  SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
-  SerializeFds(PCacheReadStream* aReadStreamOut,
+  SerializeFds(CacheReadStream* aReadStreamOut,
                const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
 
   virtual void
-  DeserializeFds(const PCacheReadStream& aReadStream,
+  DeserializeFds(const CacheReadStream& aReadStream,
                  nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
 
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
rename from dom/cache/PCacheTypes.ipdlh
rename to dom/cache/CacheTypes.ipdlh
--- a/dom/cache/PCacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -1,95 +1,244 @@
 /* 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";
-using RequestCache from "mozilla/dom/FetchIPCUtils.h";
-using RequestContext from "mozilla/dom/FetchIPCUtils.h";
-using mozilla::dom::ResponseType from "mozilla/dom/FetchIPCUtils.h";
+using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
+using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
+using RequestMode from "mozilla/dom/cache/IPCUtils.h";
+using RequestCache from "mozilla/dom/cache/IPCUtils.h";
+using RequestContext from "mozilla/dom/cache/IPCUtils.h";
+using ResponseType from "mozilla/dom/cache/IPCUtils.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-struct PCacheQueryParams
+struct CacheQueryParams
 {
   bool ignoreSearch;
   bool ignoreMethod;
   bool ignoreVary;
   bool cacheNameSet;
   nsString cacheName;
 };
 
-struct PCacheReadStream
+struct CacheReadStream
 {
   nsID id;
   OptionalInputStreamParams params;
   OptionalFileDescriptorSet fds;
   nullable PCacheStreamControl control;
   nullable PCachePushStream pushStream;
 };
 
-union PCacheReadStreamOrVoid
+union CacheReadStreamOrVoid
 {
   void_t;
-  PCacheReadStream;
+  CacheReadStream;
 };
 
-struct PCacheRequest
+struct HeadersEntry
+{
+  nsCString name;
+  nsCString value;
+};
+
+struct CacheRequest
 {
   nsCString method;
   nsString url;
   nsString urlWithoutQuery;
-  PHeadersEntry[] headers;
+  HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   nsString referrer;
   RequestMode mode;
   RequestCredentials credentials;
-  PCacheReadStreamOrVoid body;
+  CacheReadStreamOrVoid body;
   uint32_t contentPolicyType;
   RequestContext context;
   RequestCache requestCache;
 };
 
-union PCacheRequestOrVoid
+union CacheRequestOrVoid
 {
   void_t;
-  PCacheRequest;
+  CacheRequest;
 };
 
-struct PCacheResponse
+struct CacheResponse
 {
   ResponseType type;
   nsString url;
   uint32_t status;
   nsCString statusText;
-  PHeadersEntry[] headers;
+  HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
-  PCacheReadStreamOrVoid body;
+  CacheReadStreamOrVoid body;
   nsCString securityInfo;
 };
 
-union PCacheResponseOrVoid
+union CacheResponseOrVoid
 {
   void_t;
-  PCacheResponse;
+  CacheResponse;
 };
 
 struct CacheRequestResponse
 {
-  PCacheRequest request;
-  PCacheResponse response;
+  CacheRequest request;
+  CacheResponse response;
+};
+
+struct CacheMatchArgs
+{
+  CacheRequest request;
+  CacheQueryParams params;
+};
+
+struct CacheMatchAllArgs
+{
+  CacheRequestOrVoid requestOrVoid;
+  CacheQueryParams params;
+};
+
+struct CacheAddAllArgs
+{
+  CacheRequest[] requestList;
+};
+
+struct CachePutAllArgs
+{
+  CacheRequestResponse[] requestResponseList;
+};
+
+struct CacheDeleteArgs
+{
+  CacheRequest request;
+  CacheQueryParams params;
+};
+
+struct CacheKeysArgs
+{
+  CacheRequestOrVoid requestOrVoid;
+  CacheQueryParams params;
+};
+
+struct StorageMatchArgs
+{
+  CacheRequest request;
+  CacheQueryParams 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
+{
+  CacheResponseOrVoid responseOrVoid;
+};
+
+struct CacheMatchAllResult
+{
+  CacheResponse[] responseList;
+};
+
+struct CacheAddAllResult
+{
+};
+
+struct CachePutAllResult
+{
+};
+
+struct CacheDeleteResult
+{
+  bool success;
+};
+
+struct CacheKeysResult
+{
+  CacheRequest[] requestList;
+};
+
+struct StorageMatchResult
+{
+  CacheResponseOrVoid 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/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -727,16 +727,17 @@ Context::Context(Manager* aManager)
 
 void
 Context::Dispatch(nsIEventTarget* aTarget, Action* aAction)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
   MOZ_ASSERT(aTarget);
   MOZ_ASSERT(aAction);
 
+  MOZ_ASSERT(mState != STATE_CONTEXT_CANCELED);
   if (mState == STATE_CONTEXT_CANCELED) {
     return;
   } else if (mState == STATE_CONTEXT_INIT) {
     PendingAction* pending = mPendingActions.AppendElement();
     pending->mTarget = aTarget;
     pending->mAction = aAction;
     return;
   }
@@ -761,21 +762,28 @@ Context::CancelAll()
     ActivityList::ForwardIterator iter(mActivityList);
     while (iter.HasMore()) {
       iter.GetNext()->Cancel();
     }
   }
   AllowToClose();
 }
 
+bool
+Context::IsCanceled() const
+{
+  NS_ASSERT_OWNINGTHREAD(Context);
+  return mState == STATE_CONTEXT_CANCELED;
+}
+
 void
 Context::Invalidate()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  mManager->Invalidate();
+  mManager->NoteClosing();
   CancelAll();
 }
 
 void
 Context::AllowToClose()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
   if (mThreadsafeHandle) {
--- a/dom/cache/Context.h
+++ b/dom/cache/Context.h
@@ -121,16 +121,19 @@ public:
 
   // Cancel any Actions running or waiting to run.  This should allow the
   // Context to be released and Listener::RemoveContext() will be called
   // when complete.
   //
   // Only callable from the thread that created the Context.
   void CancelAll();
 
+  // True if CancelAll() has been called.
+  bool IsCanceled() const;
+
   // Like CancelAll(), but also marks the Manager as "invalid".
   void Invalidate();
 
   // Remove any self references and allow the Context to be released when
   // there are no more Actions to process.
   void AllowToClose();
 
   // Cancel any Actions running or waiting to run that operate on the given
--- 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;
@@ -141,40 +142,40 @@ DBAction::OpenConnection(const QuotaInfo
     rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn));
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Check the schema to make sure it is not too old.
   int32_t schemaVersion = 0;
   rv = conn->GetSchemaVersion(&schemaVersion);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-  if (schemaVersion > 0 && schemaVersion < DBSchema::kMaxWipeSchemaVersion) {
+  if (schemaVersion > 0 && schemaVersion < db::kMaxWipeSchemaVersion) {
     conn = nullptr;
     rv = WipeDatabase(dbFile, aDBDir);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn));
   }
 
-  rv = DBSchema::InitializeConnection(conn);
+  rv = db::InitializeConnection(conn);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   conn.forget(aConnOut);
 
   return rv;
 }
 
 nsresult
 DBAction::WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir)
 {
   nsresult rv = aDBFile->Remove(false);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Delete the morgue as well.
-  rv = FileUtils::BodyDeleteDir(aDBDir);
+  rv = BodyDeleteDir(aDBDir);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 SyncDBAction::SyncDBAction(Mode aMode)
   : DBAction(aMode)
 {
--- 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/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -3,37 +3,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/DBSchema.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/dom/InternalHeaders.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
+#include "mozilla/dom/cache/Types.h"
+#include "mozilla/dom/cache/TypeUtils.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsCRT.h"
 #include "nsHttp.h"
 #include "mozilla/dom/HeadersBinding.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/ResponseBinding.h"
-#include "Types.h"
 #include "nsIContentPolicy.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
+namespace db {
 
-const int32_t DBSchema::kMaxWipeSchemaVersion = 6;
-const int32_t DBSchema::kLatestSchemaVersion = 6;
-const int32_t DBSchema::kMaxEntriesPerStatement = 255;
+const int32_t kMaxWipeSchemaVersion = 6;
+
+namespace {
+
+const int32_t kLatestSchemaVersion = 6;
+const int32_t kMaxEntriesPerStatement = 255;
+
+} // anonymous namespace
 
 // If any of the static_asserts below fail, it means that you have changed
 // the corresponding WebIDL enum in a way that may be incompatible with the
 // existing data stored in the DOM Cache.  You would need to update the Cache
 // database schema accordingly and adjust the failing static_assert.
 static_assert(int(HeadersGuardEnum::None) == 0 &&
               int(HeadersGuardEnum::Request) == 1 &&
               int(HeadersGuardEnum::Request_no_cors) == 2 &&
@@ -135,21 +142,58 @@ static_assert(nsIContentPolicy::TYPE_INV
               nsIContentPolicy::TYPE_WEBSOCKET == 16 &&
               nsIContentPolicy::TYPE_CSP_REPORT == 17 &&
               nsIContentPolicy::TYPE_XSLT == 18 &&
               nsIContentPolicy::TYPE_BEACON == 19 &&
               nsIContentPolicy::TYPE_FETCH == 20 &&
               nsIContentPolicy::TYPE_IMAGESET == 21,
               "nsContentPolicytType values are as expected");
 
-using mozilla::void_t;
+namespace {
+
+typedef int32_t EntryId;
 
-// static
+static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
+                         nsTArray<EntryId>& aEntryIdListOut);
+static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
+                           const CacheRequest& aRequest,
+                           const CacheQueryParams& aParams,
+                           nsTArray<EntryId>& aEntryIdListOut,
+                           uint32_t aMaxResults = UINT32_MAX);
+static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
+                                  const CacheRequest& aRequest,
+                                  EntryId entryId, bool* aSuccessOut);
+static nsresult DeleteEntries(mozIStorageConnection* aConn,
+                              const nsTArray<EntryId>& aEntryIdList,
+                              nsTArray<nsID>& aDeletedBodyIdListOut,
+                              uint32_t aPos=0, int32_t aLen=-1);
+static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
+                            const CacheRequest& aRequest,
+                            const nsID* aRequestBodyId,
+                            const CacheResponse& aResponse,
+                            const nsID* aResponseBodyId);
+static nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
+                             SavedResponse* aSavedResponseOut);
+static nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
+                            SavedRequest* aSavedRequestOut);
+
+static void AppendListParamsToQuery(nsACString& aQuery,
+                                    const nsTArray<EntryId>& aEntryIdList,
+                                    uint32_t aPos, int32_t aLen);
+static nsresult BindListParamsToQuery(mozIStorageStatement* aState,
+                                      const nsTArray<EntryId>& aEntryIdList,
+                                      uint32_t aPos, int32_t aLen);
+static nsresult BindId(mozIStorageStatement* aState, uint32_t aPos,
+                       const nsID* aId);
+static nsresult ExtractId(mozIStorageStatement* aState, uint32_t aPos,
+                          nsID* aIdOut);
+} // anonymous namespace
+
 nsresult
-DBSchema::CreateSchema(mozIStorageConnection* aConn)
+CreateSchema(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsAutoCString pragmas(
     // Enable auto-vaccum but in incremental mode in order to avoid doing a lot
     // of work at the end of each transaction.
     "PRAGMA auto_vacuum = INCREMENTAL; "
@@ -279,19 +323,18 @@ DBSchema::CreateSchema(mozIStorageConnec
 
   if (schemaVersion != kLatestSchemaVersion) {
     return NS_ERROR_FAILURE;
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::InitializeConnection(mozIStorageConnection* aConn)
+InitializeConnection(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   // This function needs to perform per-connection initialization tasks that
   // need to happen regardless of the schema.
 
   nsAutoCString pragmas(
@@ -310,19 +353,18 @@ DBSchema::InitializeConnection(mozIStora
   );
 
   nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return NS_OK;
 }
 
-// static
 nsresult
-DBSchema::CreateCache(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
+CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aCacheIdOut);
 
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "INSERT INTO caches DEFAULT VALUES;"
   ));
@@ -340,20 +382,19 @@ DBSchema::CreateCache(mozIStorageConnect
   if (NS_WARN_IF(!hasMoreData)) { return NS_ERROR_UNEXPECTED; }
 
   rv = state->GetInt64(0, aCacheIdOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::DeleteCache(mozIStorageConnection* aConn, CacheId aCacheId,
-                      nsTArray<nsID>& aDeletedBodyIdListOut)
+DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
+              nsTArray<nsID>& aDeletedBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   // Delete the bodies explicitly as we need to read out the body IDs
   // anyway.  These body IDs must be deleted one-by-one as content may
   // still be referencing them invidivually.
   nsAutoTArray<EntryId, 256> matches;
@@ -374,20 +415,19 @@ DBSchema::DeleteCache(mozIStorageConnect
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::IsCacheOrphaned(mozIStorageConnection* aConn,
-                          CacheId aCacheId, bool* aOrphanedOut)
+IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
+                bool* aOrphanedOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aOrphanedOut);
 
   // err on the side of not deleting user data
   *aOrphanedOut = false;
 
@@ -409,23 +449,22 @@ DBSchema::IsCacheOrphaned(mozIStorageCon
   rv = state->GetInt32(0, &refCount);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aOrphanedOut = refCount == 0;
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
-                     const PCacheRequest& aRequest,
-                     const PCacheQueryParams& aParams,
-                     bool* aFoundResponseOut,
-                     SavedResponse* aSavedResponseOut)
+CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
+           const CacheRequest& aRequest,
+           const CacheQueryParams& aParams,
+           bool* aFoundResponseOut,
+           SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aFoundResponseOut);
   MOZ_ASSERT(aSavedResponseOut);
 
   *aFoundResponseOut = false;
 
@@ -441,29 +480,28 @@ DBSchema::CacheMatch(mozIStorageConnecti
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   aSavedResponseOut->mCacheId = aCacheId;
   *aFoundResponseOut = true;
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
-                        const PCacheRequestOrVoid& aRequestOrVoid,
-                        const PCacheQueryParams& aParams,
-                        nsTArray<SavedResponse>& aSavedResponsesOut)
+CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
+              const CacheRequestOrVoid& aRequestOrVoid,
+              const CacheQueryParams& aParams,
+              nsTArray<SavedResponse>& aSavedResponsesOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   nsresult rv;
 
   nsAutoTArray<EntryId, 256> matches;
-  if (aRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t) {
+  if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     rv = QueryAll(aConn, aCacheId, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   } else {
     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
@@ -473,50 +511,48 @@ DBSchema::CacheMatchAll(mozIStorageConne
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     savedResponse.mCacheId = aCacheId;
     aSavedResponsesOut.AppendElement(savedResponse);
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
-                   const PCacheRequest& aRequest,
-                   const nsID* aRequestBodyId,
-                   const PCacheResponse& aResponse,
-                   const nsID* aResponseBodyId,
-                   nsTArray<nsID>& aDeletedBodyIdListOut)
+CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
+         const CacheRequest& aRequest,
+         const nsID* aRequestBodyId,
+         const CacheResponse& aResponse,
+         const nsID* aResponseBodyId,
+         nsTArray<nsID>& aDeletedBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
-  PCacheQueryParams params(false, false, false, false,
+  CacheQueryParams params(false, false, false, false,
                            NS_LITERAL_STRING(""));
   nsAutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = InsertEntry(aConn, aCacheId, aRequest, aRequestBodyId, aResponse,
                    aResponseBodyId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
-                      const PCacheRequest& aRequest,
-                      const PCacheQueryParams& aParams,
-                      nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
+CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
+            const CacheRequest& aRequest,
+            const CacheQueryParams& aParams,
+            nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aSuccessOut);
 
   *aSuccessOut = false;
 
   nsAutoTArray<EntryId, 256> matches;
@@ -530,29 +566,28 @@ DBSchema::CacheDelete(mozIStorageConnect
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aSuccessOut = true;
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
-                    const PCacheRequestOrVoid& aRequestOrVoid,
-                    const PCacheQueryParams& aParams,
-                    nsTArray<SavedRequest>& aSavedRequestsOut)
+CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
+          const CacheRequestOrVoid& aRequestOrVoid,
+          const CacheQueryParams& aParams,
+          nsTArray<SavedRequest>& aSavedRequestsOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   nsresult rv;
 
   nsAutoTArray<EntryId, 256> matches;
-  if (aRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t) {
+  if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     rv = QueryAll(aConn, aCacheId, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   } else {
     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
@@ -562,24 +597,23 @@ DBSchema::CacheKeys(mozIStorageConnectio
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     savedRequest.mCacheId = aCacheId;
     aSavedRequestsOut.AppendElement(savedRequest);
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::StorageMatch(mozIStorageConnection* aConn,
-                       Namespace aNamespace,
-                       const PCacheRequest& aRequest,
-                       const PCacheQueryParams& aParams,
-                       bool* aFoundResponseOut,
-                       SavedResponse* aSavedResponseOut)
+StorageMatch(mozIStorageConnection* aConn,
+             Namespace aNamespace,
+             const CacheRequest& aRequest,
+             const CacheQueryParams& aParams,
+             bool* aFoundResponseOut,
+             SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aFoundResponseOut);
   MOZ_ASSERT(aSavedResponseOut);
 
   *aFoundResponseOut = false;
 
@@ -634,21 +668,20 @@ DBSchema::StorageMatch(mozIStorageConnec
       aSavedResponseOut->mCacheId = cacheIdList[i];
       return rv;
     }
   }
 
   return NS_OK;
 }
 
-// static
 nsresult
-DBSchema::StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
-                            const nsAString& aKey, bool* aFoundCacheOut,
-                            CacheId* aCacheIdOut)
+StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
+                  const nsAString& aKey, bool* aFoundCacheOut,
+                  CacheId* aCacheIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aFoundCacheOut);
   MOZ_ASSERT(aCacheIdOut);
 
   *aFoundCacheOut = false;
 
@@ -674,20 +707,19 @@ DBSchema::StorageGetCacheId(mozIStorageC
 
   rv = state->GetInt64(0, aCacheIdOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aFoundCacheOut = true;
   return rv;
 }
 
-// static
 nsresult
-DBSchema::StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
-                          const nsAString& aKey, CacheId aCacheId)
+StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
+                const nsAString& aKey, CacheId aCacheId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO storage (namespace, key, cache_id) VALUES(?1, ?2, ?3);"
   ), getter_AddRefs(state));
@@ -703,20 +735,19 @@ DBSchema::StoragePutCache(mozIStorageCon
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
-                             const nsAString& aKey)
+StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
+                   const nsAString& aKey)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM storage WHERE namespace=?1 AND key=?2;"
   ), getter_AddRefs(state));
@@ -729,20 +760,19 @@ DBSchema::StorageForgetCache(mozIStorage
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
-                         nsTArray<nsString>& aKeysOut)
+StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
+               nsTArray<nsString>& aKeysOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT key FROM storage WHERE namespace=?1 ORDER BY rowid;"
   ), getter_AddRefs(state));
@@ -757,20 +787,21 @@ DBSchema::StorageGetKeys(mozIStorageConn
     rv = state->GetString(0, key);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     aKeysOut.AppendElement(key);
   }
 
   return rv;
 }
 
-// static
+namespace {
+
 nsresult
-DBSchema::QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
-                   nsTArray<EntryId>& aEntryIdListOut)
+QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
+         nsTArray<EntryId>& aEntryIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT id FROM entries WHERE cache_id=?1 ORDER BY id;"
   ), getter_AddRefs(state));
@@ -785,23 +816,22 @@ DBSchema::QueryAll(mozIStorageConnection
     rv = state->GetInt32(0, &entryId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     aEntryIdListOut.AppendElement(entryId);
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
-                     const PCacheRequest& aRequest,
-                     const PCacheQueryParams& aParams,
-                     nsTArray<EntryId>& aEntryIdListOut,
-                     uint32_t aMaxResults)
+QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
+           const CacheRequest& aRequest,
+           const CacheQueryParams& aParams,
+           nsTArray<EntryId>& aEntryIdListOut,
+           uint32_t aMaxResults)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aMaxResults > 0);
 
   if (!aParams.ignoreMethod() && !aRequest.method().LowerCaseEqualsLiteral("get")
                               && !aRequest.method().LowerCaseEqualsLiteral("head"))
   {
@@ -863,21 +893,20 @@ DBSchema::QueryCache(mozIStorageConnecti
     if (aEntryIdListOut.Length() == aMaxResults) {
       return NS_OK;
     }
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::MatchByVaryHeader(mozIStorageConnection* aConn,
-                            const PCacheRequest& aRequest,
-                            EntryId entryId, bool* aSuccessOut)
+MatchByVaryHeader(mozIStorageConnection* aConn,
+                  const CacheRequest& aRequest,
+                  EntryId entryId, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   *aSuccessOut = false;
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
@@ -908,34 +937,36 @@ DBSchema::MatchByVaryHeader(mozIStorageC
     "SELECT name, value FROM request_headers "
     "WHERE entry_id=?1;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32Parameter(0, entryId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  nsRefPtr<InternalHeaders> cachedHeaders = new InternalHeaders(HeadersGuardEnum::None);
+  nsRefPtr<InternalHeaders> cachedHeaders =
+    new InternalHeaders(HeadersGuardEnum::None);
 
   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     nsAutoCString name;
     nsAutoCString value;
     rv = state->GetUTF8String(0, name);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     rv = state->GetUTF8String(1, value);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     ErrorResult errorResult;
 
     cachedHeaders->Append(name, value, errorResult);
     if (errorResult.Failed()) { return errorResult.ErrorCode(); };
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  nsRefPtr<InternalHeaders> queryHeaders = new InternalHeaders(aRequest.headers());
+  nsRefPtr<InternalHeaders> queryHeaders =
+    TypeUtils::ToInternalHeaders(aRequest.headers());
 
   // Assume the vary headers match until we find a conflict
   bool varyHeadersMatch = true;
 
   for (uint32_t i = 0; i < varyValues.Length(); ++i) {
     // Extract the header names inside the Vary header value.
     nsAutoCString varyValue(varyValues[i]);
     char* rawBuffer = varyValue.BeginWriting();
@@ -974,22 +1005,21 @@ DBSchema::MatchByVaryHeader(mozIStorageC
       break;
     }
   }
 
   *aSuccessOut = varyHeadersMatch;
   return rv;
 }
 
-// static
 nsresult
-DBSchema::DeleteEntries(mozIStorageConnection* aConn,
-                        const nsTArray<EntryId>& aEntryIdList,
-                        nsTArray<nsID>& aDeletedBodyIdListOut,
-                        uint32_t aPos, int32_t aLen)
+DeleteEntries(mozIStorageConnection* aConn,
+              const nsTArray<EntryId>& aEntryIdList,
+              nsTArray<nsID>& aDeletedBodyIdListOut,
+              uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   if (aEntryIdList.IsEmpty()) {
     return NS_OK;
   }
 
@@ -1063,23 +1093,22 @@ DBSchema::DeleteEntries(mozIStorageConne
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
-                      const PCacheRequest& aRequest,
-                      const nsID* aRequestBodyId,
-                      const PCacheResponse& aResponse,
-                      const nsID* aResponseBodyId)
+InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
+            const CacheRequest& aRequest,
+            const nsID* aRequestBodyId,
+            const CacheResponse& aResponse,
+            const nsID* aResponseBodyId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO entries ("
       "request_method, "
@@ -1190,17 +1219,17 @@ DBSchema::InsertEntry(mozIStorageConnect
     "INSERT INTO request_headers ("
       "name, "
       "value, "
       "entry_id "
     ") VALUES (?1, ?2, ?3)"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  const nsTArray<PHeadersEntry>& requestHeaders = aRequest.headers();
+  const nsTArray<HeadersEntry>& requestHeaders = aRequest.headers();
   for (uint32_t i = 0; i < requestHeaders.Length(); ++i) {
     rv = state->BindUTF8StringParameter(0, requestHeaders[i].name());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->BindUTF8StringParameter(1, requestHeaders[i].value());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->BindInt32Parameter(2, entryId);
@@ -1214,17 +1243,17 @@ DBSchema::InsertEntry(mozIStorageConnect
     "INSERT INTO response_headers ("
       "name, "
       "value, "
       "entry_id "
     ") VALUES (?1, ?2, ?3)"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  const nsTArray<PHeadersEntry>& responseHeaders = aResponse.headers();
+  const nsTArray<HeadersEntry>& responseHeaders = aResponse.headers();
   for (uint32_t i = 0; i < responseHeaders.Length(); ++i) {
     rv = state->BindUTF8StringParameter(0, responseHeaders[i].name());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->BindUTF8StringParameter(1, responseHeaders[i].value());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->BindInt32Parameter(2, entryId);
@@ -1232,20 +1261,19 @@ DBSchema::InsertEntry(mozIStorageConnect
 
     rv = state->Execute();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
-                       SavedResponse* aSavedResponseOut)
+ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
+             SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aSavedResponseOut);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
@@ -1315,34 +1343,33 @@ DBSchema::ReadResponse(mozIStorageConnec
     "WHERE entry_id=?1;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32Parameter(0, aEntryId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
-    PHeadersEntry header;
+    HeadersEntry header;
 
     rv = state->GetUTF8String(0, header.name());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->GetUTF8String(1, header.value());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     aSavedResponseOut->mValue.headers().AppendElement(header);
   }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
-                      SavedRequest* aSavedRequestOut)
+ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
+            SavedRequest* aSavedRequestOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aSavedRequestOut);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
@@ -1434,65 +1461,62 @@ DBSchema::ReadRequest(mozIStorageConnect
     "WHERE entry_id=?1;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32Parameter(0, aEntryId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
-    PHeadersEntry header;
+    HeadersEntry header;
 
     rv = state->GetUTF8String(0, header.name());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->GetUTF8String(1, header.value());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     aSavedRequestOut->mValue.headers().AppendElement(header);
   }
 
   return rv;
 }
 
-// static
 void
-DBSchema::AppendListParamsToQuery(nsACString& aQuery,
-                                  const nsTArray<EntryId>& aEntryIdList,
-                                  uint32_t aPos, int32_t aLen)
+AppendListParamsToQuery(nsACString& aQuery,
+                        const nsTArray<EntryId>& aEntryIdList,
+                        uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
   for (int32_t i = aPos; i < aLen; ++i) {
     if (i == 0) {
       aQuery.AppendLiteral("?");
     } else {
       aQuery.AppendLiteral(",?");
     }
   }
 }
 
-// static
 nsresult
-DBSchema::BindListParamsToQuery(mozIStorageStatement* aState,
-                                const nsTArray<EntryId>& aEntryIdList,
-                                uint32_t aPos, int32_t aLen)
+BindListParamsToQuery(mozIStorageStatement* aState,
+                      const nsTArray<EntryId>& aEntryIdList,
+                      uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
   for (int32_t i = aPos; i < aLen; ++i) {
     nsresult rv = aState->BindInt32Parameter(i, aEntryIdList[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
-// static
 nsresult
-DBSchema::BindId(mozIStorageStatement* aState, uint32_t aPos, const nsID* aId)
+BindId(mozIStorageStatement* aState, uint32_t aPos, const nsID* aId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aState);
   nsresult rv;
 
   if (!aId) {
     rv = aState->BindNullParameter(aPos);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1502,29 +1526,31 @@ DBSchema::BindId(mozIStorageStatement* a
   char idBuf[NSID_LENGTH];
   aId->ToProvidedString(idBuf);
   rv = aState->BindUTF8StringParameter(aPos, nsAutoCString(idBuf));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
-// static
 nsresult
-DBSchema::ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
+ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aState);
   MOZ_ASSERT(aIdOut);
 
   nsAutoCString idString;
   nsresult rv = aState->GetUTF8String(aPos, idString);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool success = aIdOut->Parse(idString.get());
   if (NS_WARN_IF(!success)) { return NS_ERROR_UNEXPECTED; }
 
   return rv;
 }
 
+} // anonymouns namespace
+
+} // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/DBSchema.h
+++ b/dom/cache/DBSchema.h
@@ -9,136 +9,107 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/cache/Types.h"
 #include "nsError.h"
 #include "nsString.h"
 #include "nsTArrayForwardDeclare.h"
 
 class mozIStorageConnection;
-class mozIStorageStatement;
 struct nsID;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-class PCacheQueryParams;
-class PCacheRequest;
-class PCacheRequestOrVoid;
-class PCacheResponse;
+class CacheQueryParams;
+class CacheRequest;
+class CacheRequestOrVoid;
+class CacheResponse;
 struct SavedRequest;
 struct SavedResponse;
 
-// TODO: remove static class and use functions in cache namespace (bug 1110485)
-class DBSchema final
-{
-public:
-  static nsresult CreateSchema(mozIStorageConnection* aConn);
-  static nsresult InitializeConnection(mozIStorageConnection* aConn);
+namespace db {
+
+nsresult
+CreateSchema(mozIStorageConnection* aConn);
 
-  static nsresult CreateCache(mozIStorageConnection* aConn,
-                              CacheId* aCacheIdOut);
-  // TODO: improve naming (confusing with CacheDelete) (bug 1110485)
-  static nsresult DeleteCache(mozIStorageConnection* aConn, CacheId aCacheId,
-                              nsTArray<nsID>& aDeletedBodyIdListOut);
+nsresult
+InitializeConnection(mozIStorageConnection* aConn);
 
-  // TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
-  static nsresult IsCacheOrphaned(mozIStorageConnection* aConn,
-                                  CacheId aCacheId, bool* aOrphanedOut);
+nsresult
+CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut);
+
+nsresult
+DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
+              nsTArray<nsID>& aDeletedBodyIdListOut);
 
-  static nsresult CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
-                             const PCacheRequest& aRequest,
-                             const PCacheQueryParams& aParams,
-                             bool* aFoundResponseOut,
-                             SavedResponse* aSavedResponseOut);
-  static nsresult CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
-                                const PCacheRequestOrVoid& aRequestOrVoid,
-                                const PCacheQueryParams& aParams,
-                                nsTArray<SavedResponse>& aSavedResponsesOut);
-  static nsresult CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
-                           const PCacheRequest& aRequest,
-                           const nsID* aRequestBodyId,
-                           const PCacheResponse& aResponse,
-                           const nsID* aResponseBodyId,
-                           nsTArray<nsID>& aDeletedBodyIdListOut);
-  static nsresult CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
-                              const PCacheRequest& aRequest,
-                              const PCacheQueryParams& aParams,
-                              nsTArray<nsID>& aDeletedBodyIdListOut,
-                              bool* aSuccessOut);
-  static nsresult CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
-                            const PCacheRequestOrVoid& aRequestOrVoid,
-                            const PCacheQueryParams& aParams,
-                            nsTArray<SavedRequest>& aSavedRequestsOut);
+// TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
+nsresult
+IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
+                bool* aOrphanedOut);
+
+nsresult
+CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
+           const CacheRequest& aRequest, const CacheQueryParams& aParams,
+           bool* aFoundResponseOut, SavedResponse* aSavedResponseOut);
+
+nsresult
+CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
+              const CacheRequestOrVoid& aRequestOrVoid,
+              const CacheQueryParams& aParams,
+              nsTArray<SavedResponse>& aSavedResponsesOut);
+
+nsresult
+CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
+         const CacheRequest& aRequest,
+         const nsID* aRequestBodyId,
+         const CacheResponse& aResponse,
+         const nsID* aResponseBodyId,
+         nsTArray<nsID>& aDeletedBodyIdListOut);
 
-  static nsresult StorageMatch(mozIStorageConnection* aConn,
-                               Namespace aNamespace,
-                               const PCacheRequest& aRequest,
-                               const PCacheQueryParams& aParams,
-                               bool* aFoundResponseOut,
-                               SavedResponse* aSavedResponseOut);
-  static nsresult StorageGetCacheId(mozIStorageConnection* aConn,
-                                    Namespace aNamespace, const nsAString& aKey,
-                                    bool* aFoundCacheOut, CacheId* aCacheIdOut);
-  static nsresult StoragePutCache(mozIStorageConnection* aConn,
-                                  Namespace aNamespace, const nsAString& aKey,
-                                  CacheId aCacheId);
-  static nsresult StorageForgetCache(mozIStorageConnection* aConn,
-                                     Namespace aNamespace,
-                                     const nsAString& aKey);
-  static nsresult StorageGetKeys(mozIStorageConnection* aConn,
-                                 Namespace aNamespace,
-                                 nsTArray<nsString>& aKeysOut);
+nsresult
+CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
+            const CacheRequest& aRequest,
+            const CacheQueryParams& aParams,
+            nsTArray<nsID>& aDeletedBodyIdListOut,
+            bool* aSuccessOut);
 
-  // We will wipe out databases with a schema versions less than this.
-  static const int32_t kMaxWipeSchemaVersion;
+nsresult
+CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
+          const CacheRequestOrVoid& aRequestOrVoid,
+          const CacheQueryParams& aParams,
+          nsTArray<SavedRequest>& aSavedRequestsOut);
 
-private:
-  typedef int32_t EntryId;
+nsresult
+StorageMatch(mozIStorageConnection* aConn,
+             Namespace aNamespace,
+             const CacheRequest& aRequest,
+             const CacheQueryParams& aParams,
+             bool* aFoundResponseOut,
+             SavedResponse* aSavedResponseOut);
 
-  static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
-                           nsTArray<EntryId>& aEntryIdListOut);
-  static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
-                             const PCacheRequest& aRequest,
-                             const PCacheQueryParams& aParams,
-                             nsTArray<EntryId>& aEntryIdListOut,
-                             uint32_t aMaxResults = UINT32_MAX);
-  static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
-                                    const PCacheRequest& aRequest,
-                                    EntryId entryId, bool* aSuccessOut);
-  static nsresult DeleteEntries(mozIStorageConnection* aConn,
-                                const nsTArray<EntryId>& aEntryIdList,
-                                nsTArray<nsID>& aDeletedBodyIdListOut,
-                                uint32_t aPos=0, int32_t aLen=-1);
-  static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
-                              const PCacheRequest& aRequest,
-                              const nsID* aRequestBodyId,
-                              const PCacheResponse& aResponse,
-                              const nsID* aResponseBodyId);
-  static nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
-                               SavedResponse* aSavedResponseOut);
-  static nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
-                              SavedRequest* aSavedRequestOut);
+nsresult
+StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
+                  const nsAString& aKey, bool* aFoundCacheOut,
+                  CacheId* aCacheIdOut);
+
+nsresult
+StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
+                const nsAString& aKey, CacheId aCacheId);
 
-  static void AppendListParamsToQuery(nsACString& aQuery,
-                                      const nsTArray<EntryId>& aEntryIdList,
-                                      uint32_t aPos, int32_t aLen);
-  static nsresult BindListParamsToQuery(mozIStorageStatement* aState,
-                                        const nsTArray<EntryId>& aEntryIdList,
-                                        uint32_t aPos, int32_t aLen);
-  static nsresult BindId(mozIStorageStatement* aState, uint32_t aPos,
-                         const nsID* aId);
-  static nsresult ExtractId(mozIStorageStatement* aState, uint32_t aPos,
-                            nsID* aIdOut);
+nsresult
+StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
+                   const nsAString& aKey);
 
-  DBSchema() = delete;
-  ~DBSchema() = delete;
+nsresult
+StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
+               nsTArray<nsString>& aKeysOut);
 
-  static const int32_t kLatestSchemaVersion;
-  static const int32_t kMaxEntriesPerStatement;
-};
+// We will wipe out databases with a schema versions less than this.
+extern const int32_t kMaxWipeSchemaVersion;
 
+} // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_DBSchema_h
--- a/dom/cache/FetchPut.cpp
+++ b/dom/cache/FetchPut.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/dom/cache/ManagerId.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsCRT.h"
 #include "nsHttp.h"
 
 namespace mozilla {
 namespace dom {
@@ -93,34 +92,33 @@ protected:
 
 private:
   nsRefPtr<FetchPut> mFetchPut;
   nsRefPtr<InternalResponse> mInternalResponse;
 };
 
 // static
 nsresult
-FetchPut::Create(Listener* aListener, Manager* aManager,
-                 RequestId aRequestId, CacheId aCacheId,
-                 const nsTArray<PCacheRequest>& aRequests,
+FetchPut::Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
+                 const nsTArray<CacheRequest>& aRequests,
                  const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
                  FetchPut** aFetchPutOut)
 {
   MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length());
 
   // The FetchDriver requires that all requests have a referrer already set.
 #ifdef DEBUG
   for (uint32_t i = 0; i < aRequests.Length(); ++i) {
     if (aRequests[i].referrer() == EmptyString()) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 #endif
 
-  nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aRequestId, aCacheId,
+  nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aCacheId,
                                         aRequests, aRequestStreams);
 
   nsresult rv = ref->DispatchToMainThread();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   ref.forget(aFetchPutOut);
 
   return NS_OK;
@@ -128,35 +126,33 @@ FetchPut::Create(Listener* aListener, Ma
 
 void
 FetchPut::ClearListener()
 {
   MOZ_ASSERT(mListener);
   mListener = nullptr;
 }
 
-FetchPut::FetchPut(Listener* aListener, Manager* aManager,
-                   RequestId aRequestId, CacheId aCacheId,
-                   const nsTArray<PCacheRequest>& aRequests,
+FetchPut::FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
+                   const nsTArray<CacheRequest>& aRequests,
                    const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams)
   : mListener(aListener)
   , mManager(aManager)
-  , mRequestId(aRequestId)
   , mCacheId(aCacheId)
   , mInitiatingThread(NS_GetCurrentThread())
   , mStateList(aRequests.Length())
   , mPendingCount(0)
 {
   MOZ_ASSERT(mListener);
   MOZ_ASSERT(mManager);
   MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length());
 
   for (uint32_t i = 0; i < aRequests.Length(); ++i) {
     State* s = mStateList.AppendElement();
-    s->mPCacheRequest = aRequests[i];
+    s->mCacheRequest = aRequests[i];
     s->mRequestStream = aRequestStreams[i];
   }
 
   mManager->AddRefCacheId(mCacheId);
 }
 
 FetchPut::~FetchPut()
 {
@@ -206,24 +202,24 @@ FetchPut::DoFetchOnMainThread()
 
   nsRefPtr<ManagerId> managerId = mManager->GetManagerId();
   nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
   mPendingCount = mStateList.Length();
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    MaybeSetError(rv);
+    MaybeSetError(ErrorResult(rv));
     MaybeCompleteOnMainThread();
     return;
   }
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     nsRefPtr<InternalRequest> internalRequest =
-      ToInternalRequest(mStateList[i].mPCacheRequest);
+      ToInternalRequest(mStateList[i].mCacheRequest);
 
     // If there is a stream we must clone it so that its still available
     // to store in the cache later;
     if (mStateList[i].mRequestStream) {
       internalRequest->SetBody(mStateList[i].mRequestStream);
       nsRefPtr<InternalRequest> clone = internalRequest->Clone();
 
       // The copy construction clone above can change the source stream,
@@ -235,17 +231,17 @@ FetchPut::DoFetchOnMainThread()
 
     nsRefPtr<FetchDriver> fetchDriver = new FetchDriver(internalRequest,
                                                         principal,
                                                         loadGroup);
 
     mStateList[i].mFetchObserver = new FetchObserver(this);
     rv = fetchDriver->Fetch(mStateList[i].mFetchObserver);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      MaybeSetError(rv);
+      MaybeSetError(ErrorResult(rv));
       mStateList[i].mFetchObserver = nullptr;
       mPendingCount -= 1;
       continue;
     }
   }
 
   // If they all failed, then we might need to complete main thread immediately
   MaybeCompleteOnMainThread();
@@ -253,26 +249,26 @@ FetchPut::DoFetchOnMainThread()
 
 void
 FetchPut::FetchComplete(FetchObserver* aObserver,
                         InternalResponse* aInternalResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aInternalResponse->IsError() && !mResult.Failed()) {
-    MaybeSetError(NS_ERROR_FAILURE);
+    MaybeSetError(ErrorResult(NS_ERROR_FAILURE));
   }
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     if (mStateList[i].mFetchObserver == aObserver) {
       ErrorResult rv;
-      ToPCacheResponseWithoutBody(mStateList[i].mPCacheResponse,
+      ToCacheResponseWithoutBody(mStateList[i].mCacheResponse,
                                   *aInternalResponse, rv);
       if (rv.Failed()) {
-        mResult = Move(rv);
+        MaybeSetError(Move(rv));
       } else {
         aInternalResponse->GetBody(getter_AddRefs(mStateList[i].mResponseStream));
       }
       mStateList[i].mFetchObserver = nullptr;
       MOZ_ASSERT(mPendingCount > 0);
       mPendingCount -= 1;
       MaybeCompleteOnMainThread();
       return;
@@ -311,67 +307,67 @@ FetchPut::DoPutOnWorkerThread()
 
   putList.SetCapacity(mStateList.Length());
   requestStreamList.SetCapacity(mStateList.Length());
   responseStreamList.SetCapacity(mStateList.Length());
 
   for (uint32_t i = 0; i < mStateList.Length(); ++i) {
     // The spec requires us to catch if content tries to insert a set of
     // requests that would overwrite each other.
-    if (MatchInPutList(mStateList[i].mPCacheRequest, putList)) {
-      MaybeSetError(NS_ERROR_DOM_INVALID_STATE_ERR);
+    if (MatchInPutList(mStateList[i].mCacheRequest, putList)) {
+      MaybeSetError(ErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
       MaybeNotifyListener();
       return;
     }
 
     CacheRequestResponse* entry = putList.AppendElement();
-    entry->request() = mStateList[i].mPCacheRequest;
-    entry->response() = mStateList[i].mPCacheResponse;
+    entry->request() = mStateList[i].mCacheRequest;
+    entry->response() = mStateList[i].mCacheResponse;
     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,
+FetchPut::MatchInPutList(const CacheRequest& aRequest,
                          const nsTArray<CacheRequestResponse>& aPutList)
 {
   // This method implements the SW spec QueryCache algorithm against an
   // in memory array of Request/Response objects.  This essentially the
   // same algorithm that is implemented in DBSchema.cpp.  Unfortunately
   // we cannot unify them because when operating against the real database
   // we don't want to load all request/response objects into memory.
 
   if (!aRequest.method().LowerCaseEqualsLiteral("get") &&
       !aRequest.method().LowerCaseEqualsLiteral("head")) {
     return false;
   }
 
   nsRefPtr<InternalHeaders> requestHeaders =
-    new InternalHeaders(aRequest.headers());
+    ToInternalHeaders(aRequest.headers());
 
   for (uint32_t i = 0; i < aPutList.Length(); ++i) {
-    const PCacheRequest& cachedRequest = aPutList[i].request();
-    const PCacheResponse& cachedResponse = aPutList[i].response();
+    const CacheRequest& cachedRequest = aPutList[i].request();
+    const CacheResponse& cachedResponse = aPutList[i].response();
 
     // If the URLs don't match, then just skip to the next entry.
     if (aRequest.url() != cachedRequest.url()) {
       continue;
     }
 
     nsRefPtr<InternalHeaders> cachedRequestHeaders =
-      new InternalHeaders(cachedRequest.headers());
+      ToInternalHeaders(cachedRequest.headers());
 
     nsRefPtr<InternalHeaders> cachedResponseHeaders =
-      new InternalHeaders(cachedResponse.headers());
+      ToInternalHeaders(cachedResponse.headers());
 
     nsAutoTArray<nsCString, 16> varyHeaders;
     ErrorResult rv;
     cachedResponseHeaders->GetAll(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
     MOZ_ALWAYS_TRUE(!rv.Failed());
 
     // Assume the vary headers match until we find a conflict
     bool varyHeadersMatch = true;
@@ -421,45 +417,49 @@ FetchPut::MatchInPutList(const PCacheReq
       return true;
     }
   }
 
   return false;
 }
 
 void
-FetchPut::OnCachePutAll(RequestId aRequestId, nsresult aRv)
+FetchPut::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+                       CacheId aOpenedCacheId,
+                       const nsTArray<SavedResponse>& aSavedResponseList,
+                       const nsTArray<SavedRequest>& aSavedRequestList,
+                       StreamList* aStreamList)
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
-  MaybeSetError(aRv);
+  MOZ_ASSERT(aResult.type() == CacheOpResult::TCachePutAllResult);
+  MaybeSetError(Move(aRv));
   MaybeNotifyListener();
 }
 
 void
-FetchPut::MaybeSetError(nsresult aRv)
+FetchPut::MaybeSetError(ErrorResult&& aRv)
 {
-  if (mResult.Failed() || NS_SUCCEEDED(aRv)) {
+  if (mResult.Failed() || !aRv.Failed()) {
     return;
   }
-  mResult.Throw(aRv);
+  mResult = Move(aRv);
 }
 
 void
 FetchPut::MaybeNotifyListener()
 {
   MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
   if (!mListener) {
     return;
   }
   // CacheParent::OnFetchPut can lead to the destruction of |this| when the
   // object is removed from CacheParent::mFetchPutList, so make sure that
   // doesn't happen until this method returns.
   nsRefPtr<FetchPut> kungFuDeathGrip(this);
-  mListener->OnFetchPut(this, mRequestId, mResult);
-  mResult.ClearMessage(); // This may contain a TypeError.
+  mListener->OnFetchPut(this, Move(mResult));
 }
 
 nsIGlobalObject*
 FetchPut::GetGlobalObject() const
 {
   MOZ_CRASH("No global object in parent-size FetchPut operation!");
 }
 
--- a/dom/cache/FetchPut.h
+++ b/dom/cache/FetchPut.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_cache_FetchPut_h
 #define mozilla_dom_cache_FetchPut_h
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/cache/Manager.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "nsRefPtr.h"
 #include "nsTArray.h"
 #include <utility>
 
 class nsIInputStream;
 class nsIRunnable;
@@ -35,78 +35,81 @@ class FetchPut final : public Manager::L
 {
 public:
   typedef std::pair<nsRefPtr<Request>, nsRefPtr<Response>> PutPair;
 
   class Listener
   {
   public:
     virtual void
-    OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, const ErrorResult& aRv) = 0;
+    OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) = 0;
   };
 
   static nsresult
-  Create(Listener* aListener, Manager* aManager,
-         RequestId aRequestId, CacheId aCacheId,
-         const nsTArray<PCacheRequest>& aRequests,
+  Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
+         const nsTArray<CacheRequest>& aRequests,
          const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
          FetchPut** aFetchPutOut);
 
   void ClearListener();
 
 private:
   class Runnable;
   class FetchObserver;
   friend class FetchObserver;
   struct State
   {
-    PCacheRequest mPCacheRequest;
+    CacheRequest mCacheRequest;
     nsCOMPtr<nsIInputStream> mRequestStream;
     nsRefPtr<FetchObserver> mFetchObserver;
-    PCacheResponse mPCacheResponse;
+    CacheResponse mCacheResponse;
     nsCOMPtr<nsIInputStream> mResponseStream;
 
     nsRefPtr<Request> mRequest;
     nsRefPtr<Response> mResponse;
   };
 
-  FetchPut(Listener* aListener, Manager* aManager,
-           RequestId aRequestId, CacheId aCacheId,
-           const nsTArray<PCacheRequest>& aRequests,
+  FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
+           const nsTArray<CacheRequest>& 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,
+  static bool MatchInPutList(const CacheRequest& aRequest,
                              const nsTArray<CacheRequestResponse>& aPutList);
-  virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
 
-  void MaybeSetError(nsresult aRv);
+  virtual void
+  OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
+               CacheId aOpenedCacheId,
+               const nsTArray<SavedResponse>& aSavedResponseList,
+               const nsTArray<SavedRequest>& aSavedRequestList,
+               StreamList* aStreamList) override;
+
+  void MaybeSetError(ErrorResult&& aRv);
   void MaybeNotifyListener();
 
   // TypeUtils methods
   virtual nsIGlobalObject* GetGlobalObject() const override;
 #ifdef DEBUG
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual CachePushStreamChild*
   CreatePushStream(nsIAsyncInputStream* aStream) override;
 
   Listener* mListener;
   nsRefPtr<Manager> mManager;
-  const RequestId mRequestId;
   const CacheId mCacheId;
   nsCOMPtr<nsIThread> mInitiatingThread;
   nsTArray<State> mStateList;
   uint32_t mPendingCount;
   ErrorResult mResult;
   nsCOMPtr<nsIRunnable> mRunnable;
 
 public:
--- a/dom/cache/FileUtils.cpp
+++ b/dom/cache/FileUtils.cpp
@@ -18,21 +18,34 @@
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::dom::quota::FileInputStream;
 using mozilla::dom::quota::FileOutputStream;
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
-using mozilla::unused;
+
+namespace {
+
+enum BodyFileType
+{
+  BODY_FILE_FINAL,
+  BODY_FILE_TMP
+};
+
+nsresult
+BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
+             nsIFile** aBodyFileOut);
+
+} // anonymous namespace
 
 // static
 nsresult
-FileUtils::BodyCreateDir(nsIFile* aBaseDir)
+BodyCreateDir(nsIFile* aBaseDir)
 {
   MOZ_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> aBodyDir;
   nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
@@ -44,17 +57,17 @@ FileUtils::BodyCreateDir(nsIFile* aBaseD
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 // static
 nsresult
-FileUtils::BodyDeleteDir(nsIFile* aBaseDir)
+BodyDeleteDir(nsIFile* aBaseDir)
 {
   MOZ_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> aBodyDir;
   nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
@@ -67,18 +80,17 @@ FileUtils::BodyDeleteDir(nsIFile* aBaseD
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 // static
 nsresult
-FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
-                           nsIFile** aCacheDirOut)
+BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut)
 {
   MOZ_ASSERT(aBaseDir);
   MOZ_ASSERT(aCacheDirOut);
 
   *aCacheDirOut = nullptr;
 
   nsresult rv = aBaseDir->Clone(aCacheDirOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -102,21 +114,21 @@ FileUtils::BodyGetCacheDir(nsIFile* aBas
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 // static
 nsresult
-FileUtils::BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
-                                nsIFile* aBaseDir, nsIInputStream* aSource,
-                                void* aClosure,
-                                nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
-                                nsISupports** aCopyContextOut)
+BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
+                     nsIFile* aBaseDir, nsIInputStream* aSource,
+                     void* aClosure,
+                     nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
+                     nsISupports** aCopyContextOut)
 {
   MOZ_ASSERT(aBaseDir);
   MOZ_ASSERT(aSource);
   MOZ_ASSERT(aClosure);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(aIdOut);
   MOZ_ASSERT(aCopyContextOut);
 
@@ -163,31 +175,31 @@ FileUtils::BodyStartWriteStream(const Qu
                     aCopyContextOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 // static
 void
-FileUtils::BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
+BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
 {
   MOZ_ASSERT(aBaseDir);
   MOZ_ASSERT(aCopyContext);
 
   nsresult rv = NS_CancelAsyncCopy(aCopyContext, NS_ERROR_ABORT);
   unused << NS_WARN_IF(NS_FAILED(rv));
 
   // The partially written file must be cleaned up after the async copy
   // makes its callback.
 }
 
 // static
 nsresult
-FileUtils::BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
+BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
 {
   MOZ_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(tmpFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsCOMPtr<nsIFile> finalFile;
@@ -201,18 +213,18 @@ FileUtils::BodyFinalizeWrite(nsIFile* aB
   rv = tmpFile->RenameTo(nullptr, finalFileName);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 // static
 nsresult
-FileUtils::BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
-                    const nsID& aId, nsIInputStream** aStreamOut)
+BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
+         nsIInputStream** aStreamOut)
 {
   MOZ_ASSERT(aBaseDir);
   MOZ_ASSERT(aStreamOut);
 
   nsCOMPtr<nsIFile> finalFile;
   nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL,
                              getter_AddRefs(finalFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -229,17 +241,17 @@ FileUtils::BodyOpen(const QuotaInfo& aQu
 
   fileStream.forget(aStreamOut);
 
   return rv;
 }
 
 // static
 nsresult
-FileUtils::BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
+BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
 {
   nsresult rv = NS_OK;
 
   for (uint32_t i = 0; i < aIdList.Length(); ++i) {
     nsCOMPtr<nsIFile> tmpFile;
     rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_TMP,
                       getter_AddRefs(tmpFile));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -268,20 +280,21 @@ FileUtils::BodyDeleteFiles(nsIFile* aBas
 
     // Again, only treat removal as hard failure in debug build.
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   return NS_OK;
 }
 
-// static
+namespace {
+
 nsresult
-FileUtils::BodyIdToFile(nsIFile* aBaseDir, const nsID& aId,
-                        BodyFileType aType, nsIFile** aBodyFileOut)
+BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
+             nsIFile** aBodyFileOut)
 {
   MOZ_ASSERT(aBaseDir);
   MOZ_ASSERT(aBodyFileOut);
 
   *aBodyFileOut = nullptr;
 
   nsresult rv = BodyGetCacheDir(aBaseDir, aId, aBodyFileOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -299,11 +312,13 @@ FileUtils::BodyIdToFile(nsIFile* aBaseDi
   }
 
   rv = (*aBodyFileOut)->Append(fileName);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
+} // anonymous namespace
+
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/FileUtils.h
+++ b/dom/cache/FileUtils.h
@@ -14,59 +14,44 @@
 
 struct nsID;
 class nsIFile;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-// TODO: remove static class and use functions in cache namespace (bug 1110485)
-class FileUtils final
-{
-public:
-  enum BodyFileType
-  {
-    BODY_FILE_FINAL,
-    BODY_FILE_TMP
-  };
+nsresult
+BodyCreateDir(nsIFile* aBaseDir);
 
-  static nsresult BodyCreateDir(nsIFile* aBaseDir);
-  // Note that this function can only be used during the initialization of the
-  // database.  We're unlikely to be able to delete the DB successfully past
-  // that point due to the file being in use.
-  static nsresult BodyDeleteDir(nsIFile* aBaseDir);
-  static nsresult BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
-                                  nsIFile** aCacheDirOut);
+// Note that this function can only be used during the initialization of the
+// database.  We're unlikely to be able to delete the DB successfully past
+// that point due to the file being in use.
+nsresult
+BodyDeleteDir(nsIFile* aBaseDir);
+
+nsresult
+BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut);
 
-  static nsresult
-  BodyStartWriteStream(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
-                       nsIInputStream* aSource, void* aClosure,
-                       nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
-                       nsISupports** aCopyContextOut);
+nsresult
+BodyStartWriteStream(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
+                     nsIInputStream* aSource, void* aClosure,
+                     nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
+                     nsISupports** aCopyContextOut);
 
-  static void
-  BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext);
-
-  static nsresult
-  BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
+void
+BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext);
 
-  static nsresult
-  BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
-           nsIInputStream** aStreamOut);
-
-  static nsresult
-  BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
+nsresult
+BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
 
-private:
-  static nsresult
-  BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
-               nsIFile** aBodyFileOut);
+nsresult
+BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
+         nsIInputStream** aStreamOut);
 
-  FileUtils() = delete;
-  ~FileUtils() = delete;
-};
+nsresult
+BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_FileUtils_h
--- a/dom/cache/IPCUtils.h
+++ b/dom/cache/IPCUtils.h
@@ -3,20 +3,57 @@
 /* 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_IPCUtils_h
 #define mozilla_dom_cache_IPCUtils_h
 
 #include "ipc/IPCMessageUtils.h"
+
+// Fix X11 header brain damage that conflicts with HeadersGuardEnum::None
+#undef None
+
+#include "mozilla/dom/HeadersBinding.h"
+#include "mozilla/dom/RequestBinding.h"
+#include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/cache/Types.h"
 
 namespace IPC {
   template<>
+  struct ParamTraits<mozilla::dom::HeadersGuardEnum> :
+    public ContiguousEnumSerializer<mozilla::dom::HeadersGuardEnum,
+                                    mozilla::dom::HeadersGuardEnum::None,
+                                    mozilla::dom::HeadersGuardEnum::EndGuard_> {};
+  template<>
+  struct ParamTraits<mozilla::dom::RequestMode> :
+    public ContiguousEnumSerializer<mozilla::dom::RequestMode,
+                                    mozilla::dom::RequestMode::Same_origin,
+                                    mozilla::dom::RequestMode::EndGuard_> {};
+  template<>
+  struct ParamTraits<mozilla::dom::RequestCredentials> :
+    public ContiguousEnumSerializer<mozilla::dom::RequestCredentials,
+                                    mozilla::dom::RequestCredentials::Omit,
+                                    mozilla::dom::RequestCredentials::EndGuard_> {};
+  template<>
+  struct ParamTraits<mozilla::dom::RequestCache> :
+    public ContiguousEnumSerializer<mozilla::dom::RequestCache,
+                                    mozilla::dom::RequestCache::Default,
+                                    mozilla::dom::RequestCache::EndGuard_> {};
+  template<>
+  struct ParamTraits<mozilla::dom::RequestContext> :
+    public ContiguousEnumSerializer<mozilla::dom::RequestContext,
+                                    mozilla::dom::RequestContext::Audio,
+                                    mozilla::dom::RequestContext::EndGuard_> {};
+  template<>
+  struct ParamTraits<mozilla::dom::ResponseType> :
+    public ContiguousEnumSerializer<mozilla::dom::ResponseType,
+                                    mozilla::dom::ResponseType::Basic,
+                                    mozilla::dom::ResponseType::EndGuard_> {};
+  template<>
   struct ParamTraits<mozilla::dom::cache::Namespace> :
     public ContiguousEnumSerializer<mozilla::dom::cache::Namespace,
                                     mozilla::dom::cache::DEFAULT_NAMESPACE,
                                     mozilla::dom::cache::NUMBER_OF_NAMESPACES>
   {};
 }
 
 #endif // mozilla_dom_cache_IPCUtils_h
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -11,17 +11,17 @@
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/cache/Context.h"
 #include "mozilla/dom/cache/DBAction.h"
 #include "mozilla/dom/cache/DBSchema.h"
 #include "mozilla/dom/cache/FileUtils.h"
 #include "mozilla/dom/cache/ManagerId.h"
-#include "mozilla/dom/cache/PCacheTypes.h"
+#include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozStorageHelper.h"
 #include "nsAutoPtr.h"
 #include "nsIInputStream.h"
 #include "nsID.h"
@@ -29,20 +29,21 @@
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsTObserverArray.h"
 
 namespace {
 
 using mozilla::unused;
 using mozilla::dom::cache::Action;
-using mozilla::dom::cache::DBSchema;
-using mozilla::dom::cache::FileUtils;
+using mozilla::dom::cache::BodyCreateDir;
+using mozilla::dom::cache::BodyDeleteFiles;
 using mozilla::dom::cache::QuotaInfo;
 using mozilla::dom::cache::SyncDBAction;
+using mozilla::dom::cache::db::CreateSchema;
 
 // An Action that is executed when a Context is first created.  It ensures that
 // the directory and database are setup properly.  This lets other actions
 // not worry about these details.
 class SetupAction final : public SyncDBAction
 {
 public:
   SetupAction()
@@ -54,23 +55,23 @@ public:
                         mozIStorageConnection* aConn) override
   {
     // TODO: init maintainance marker (bug 1110446)
     // TODO: perform maintainance if necessary (bug 1110446)
     // TODO: find orphaned caches in database (bug 1110446)
     // TODO: have Context create/delete marker files in constructor/destructor
     //       and only do expensive maintenance if that marker is present (bug 1110446)
 
-    nsresult rv = FileUtils::BodyCreateDir(aDBDir);
+    nsresult rv = BodyCreateDir(aDBDir);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    rv = DBSchema::CreateSchema(aConn);
+    rv = CreateSchema(aConn);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     return rv;
   }
 };
@@ -108,17 +109,17 @@ public:
     }
 
     rv = dbDir->Append(NS_LITERAL_STRING("cache"));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aResolver->Resolve(rv);
       return;
     }
 
-    rv = FileUtils::BodyDeleteFiles(dbDir, mDeletedBodyIdList);
+    rv = BodyDeleteFiles(dbDir, mDeletedBodyIdList);
     unused << NS_WARN_IF(NS_FAILED(rv));
 
     aResolver->Resolve(rv);
   }
 
 private:
   nsTArray<nsID> mDeletedBodyIdList;
 };
@@ -151,16 +152,17 @@ public:
     nsRefPtr<Manager> ref = Get(aManagerId);
     if (!ref) {
       // TODO: replace this with a thread pool (bug 1119864)
       nsCOMPtr<nsIThread> ioThread;
       rv = NS_NewNamedThread("DOMCacheThread", getter_AddRefs(ioThread));
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
       ref = new Manager(aManagerId, ioThread);
+      ref->Init();
 
       MOZ_ASSERT(!sFactory->mManagerList.Contains(ref));
       sFactory->mManagerList.AppendElement(ref);
     }
 
     ref.forget(aManagerOut);
 
     return NS_OK;
@@ -175,17 +177,17 @@ public:
     if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
 
     ManagerList::ForwardIterator iter(sFactory->mManagerList);
     while (iter.HasMore()) {
       nsRefPtr<Manager> manager = iter.GetNext();
       // If there is an invalid Manager finishing up and a new Manager
       // is created for the same origin, then the new Manager will
       // be blocked until QuotaManager finishes clearing the origin.
-      if (manager->IsValid() && *manager->mManagerId == *aManagerId) {
+      if (!manager->IsClosing() && *manager->mManagerId == *aManagerId) {
         return manager.forget();
       }
     }
 
     return nullptr;
   }
 
   static void
@@ -404,43 +406,41 @@ StaticRefPtr<nsIThread> Manager::Factory
 // ----------------------------------------------------------------------------
 
 // Abstract class to help implement the various Actions.  The vast majority
 // of Actions are synchronous and need to report back to a Listener on the
 // Manager.
 class Manager::BaseAction : public SyncDBAction
 {
 protected:
-  BaseAction(Manager* aManager, ListenerId aListenerId, RequestId aRequestId)
+  BaseAction(Manager* aManager, ListenerId aListenerId)
     : SyncDBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
-    , mRequestId (aRequestId)
   {
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) = 0;
+  Complete(Listener* aListener, ErrorResult&& aRv) = 0;
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
   {
     NS_ASSERT_OWNINGTHREAD(Manager::BaseAction);
     Listener* listener = mManager->GetListener(mListenerId);
     if (listener) {
-      Complete(listener, aRv);
+      Complete(listener, ErrorResult(aRv));
     }
 
     // ensure we release the manager on the initiating thread
     mManager = nullptr;
   }
 
   nsRefPtr<Manager> mManager;
   const ListenerId mListenerId;
-  const RequestId mRequestId;
 };
 
 // ----------------------------------------------------------------------------
 
 // Action that is executed when we determine that content has stopped using
 // a Cache object that has been orphaned.
 class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
 {
@@ -453,17 +453,17 @@ public:
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    nsresult rv = DBSchema::DeleteCache(aConn, mCacheId, mDeletedBodyIdList);
+    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     return rv;
   }
 
@@ -483,158 +483,149 @@ 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 = db::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,
-                             getter_AddRefs(stream));
+    rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
 
     mStreamList->Add(mResponse.mBodyId, stream);
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (!mFoundResponse) {
-      aListener->OnCacheMatch(mRequestId, aRv, nullptr, nullptr);
+      aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()));
     } else {
       mStreamList->Activate(mCacheId);
-      aListener->OnCacheMatch(mRequestId, aRv, &mResponse, mStreamList);
+      aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()), mResponse,
+                              mStreamList);
     }
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequest mRequest;
-  const PCacheQueryParams mParams;
+  const CacheMatchArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   bool mFoundResponse;
   SavedResponse mResponse;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheMatchAllAction final : public Manager::BaseAction
 {
 public:
   CacheMatchAllAction(Manager* aManager, ListenerId aListenerId,
-                      RequestId aRequestId, CacheId aCacheId,
-                      const PCacheRequestOrVoid& aRequestOrVoid,
-                      const PCacheQueryParams& aParams,
+                      CacheId aCacheId, const CacheMatchAllArgs& aArgs,
                       StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequestOrVoid(aRequestOrVoid)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::CacheMatchAll(aConn, mCacheId, mRequestOrVoid,
-                                          mParams, mSavedResponses);
+    nsresult rv = db::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;
-      rv = FileUtils::BodyOpen(aQuotaInfo, aDBDir,
-                               mSavedResponses[i].mBodyId,
-                               getter_AddRefs(stream));
+      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
+                    getter_AddRefs(stream));
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
       if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
 
       mStreamList->Add(mSavedResponses[i].mBodyId, stream);
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheMatchAll(mRequestId, aRv, mSavedResponses, mStreamList);
+    aListener->OnOpComplete(Move(aRv), CacheMatchAllResult(), mSavedResponses,
+                            mStreamList);
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequestOrVoid mRequestOrVoid;
-  const PCacheQueryParams mParams;
+  const CacheMatchAllArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   nsTArray<SavedResponse> mSavedResponses;
 };
 
 // ----------------------------------------------------------------------------
 
 // This is the most complex Action.  It puts a request/response pair into the
 // Cache.  It does not complete until all of the body data has been saved to
 // disk.  This means its an asynchronous Action.
 class Manager::CachePutAllAction final : public DBAction
 {
 public:
   CachePutAllAction(Manager* aManager, ListenerId aListenerId,
-                    RequestId aRequestId, CacheId aCacheId,
+                    CacheId aCacheId,
                     const nsTArray<CacheRequestResponse>& aPutList,
                     const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
                     const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
     : DBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
-    , mRequestId(aRequestId)
     , mCacheId(aCacheId)
     , mList(aPutList.Length())
     , mExpectedAsyncCopyCompletions(1)
     , mAsyncResult(NS_OK)
     , mMutex("cache::Manager::CachePutAllAction")
   {
     MOZ_ASSERT(!aPutList.IsEmpty());
     MOZ_ASSERT(aPutList.Length() == aRequestStreamList.Length());
@@ -763,35 +754,35 @@ private:
 
     mozStorageTransaction trans(mConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     nsresult rv = NS_OK;
     for (uint32_t i = 0; i < mList.Length(); ++i) {
       Entry& e = mList[i];
       if (e.mRequestStream) {
-        rv = FileUtils::BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
+        rv = BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           DoResolve(rv);
           return;
         }
       }
       if (e.mResponseStream) {
-        rv = FileUtils::BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
+        rv = BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           DoResolve(rv);
           return;
         }
       }
 
-      rv = DBSchema::CachePut(mConn, mCacheId, e.mRequest,
-                              e.mRequestStream ? &e.mRequestBodyId : nullptr,
-                              e.mResponse,
-                              e.mResponseStream ? &e.mResponseBodyId : nullptr,
-                              mDeletedBodyIdList);
+      rv = db::CachePut(mConn, mCacheId, e.mRequest,
+                        e.mRequestStream ? &e.mRequestBodyId : nullptr,
+                        e.mResponse,
+                        e.mResponseStream ? &e.mResponseBodyId : nullptr,
+                        mDeletedBodyIdList);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         DoResolve(rv);
         return;
       }
     }
 
     rv = trans.Commit();
     unused << NS_WARN_IF(NS_FAILED(rv));
@@ -809,17 +800,17 @@ private:
       mList[i].mResponseStream = nullptr;
     }
 
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
 
     Listener* listener = mManager->GetListener(mListenerId);
     mManager = nullptr;
     if (listener) {
-      listener->OnCachePutAll(mRequestId, aRv);
+      listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
     }
   }
 
   virtual void
   CancelOnInitiatingThread() override
   {
     NS_ASSERT_OWNINGTHREAD(Action);
     Action::CancelOnInitiatingThread();
@@ -829,22 +820,22 @@ private:
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     NS_ASSERT_OWNINGTHREAD(Action);
     return aCacheId == mCacheId;
   }
 
   struct Entry
   {
-    PCacheRequest mRequest;
+    CacheRequest mRequest;
     nsCOMPtr<nsIInputStream> mRequestStream;
     nsID mRequestBodyId;
     nsCOMPtr<nsISupports> mRequestCopyContext;
 
-    PCacheResponse mResponse;
+    CacheResponse mResponse;
     nsCOMPtr<nsIInputStream> mResponseStream;
     nsID mResponseBodyId;
     nsCOMPtr<nsISupports> mResponseCopyContext;
   };
 
   enum StreamId
   {
     RequestStream,
@@ -875,20 +866,19 @@ private:
     }
 
     if (!source) {
       return NS_OK;
     }
 
     nsCOMPtr<nsISupports> copyContext;
 
-    nsresult rv = FileUtils::BodyStartWriteStream(aQuotaInfo, mDBDir, source,
-                                                  this, AsyncCopyCompleteFunc,
-                                                  bodyId,
-                                                  getter_AddRefs(copyContext));
+    nsresult rv = BodyStartWriteStream(aQuotaInfo, mDBDir, source, this,
+                                       AsyncCopyCompleteFunc, bodyId,
+                                       getter_AddRefs(copyContext));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     mBodyIdWrittenList.AppendElement(*bodyId);
 
     if (copyContext) {
       MutexAutoLock lock(mMutex);
       mCopyContextList.AppendElement(copyContext);
     }
@@ -899,17 +889,17 @@ private:
   }
 
   void
   CancelAllStreamCopying()
   {
     // May occur on either owning thread or target thread
     MutexAutoLock lock(mMutex);
     for (uint32_t i = 0; i < mCopyContextList.Length(); ++i) {
-      FileUtils::BodyCancelWrite(mDBDir, mCopyContextList[i]);
+      BodyCancelWrite(mDBDir, mCopyContextList[i]);
     }
     mCopyContextList.Clear();
   }
 
   static void
   AsyncCopyCompleteFunc(void* aClosure, nsresult aRv)
   {
     // May be on any thread, including STS event target.
@@ -942,17 +932,17 @@ private:
     {
       MutexAutoLock lock(mMutex);
       MOZ_ASSERT(mCopyContextList.IsEmpty());
     }
 #endif
 
     // Clean up any files we might have written before hitting the error.
     if (NS_FAILED(aRv)) {
-      FileUtils::BodyDeleteFiles(mDBDir, mBodyIdWrittenList);
+      BodyDeleteFiles(mDBDir, mBodyIdWrittenList);
     }
 
     // Must be released on the target thread where it was opened.
     mConn = nullptr;
 
     // Drop our ref to the target thread as we are done with this thread.
     // Also makes our thread assertions catch any incorrect method calls
     // after resolve.
@@ -962,17 +952,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,388 +982,425 @@ 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 = db::CacheDelete(aConn, mCacheId, mArgs.request(),
+                                  mArgs.params(), mDeletedBodyIdList,
+                                  &mSuccess);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mSuccess = false;
       return rv;
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
-    aListener->OnCacheDelete(mRequestId, aRv, mSuccess);
+    aListener->OnOpComplete(Move(aRv), CacheDeleteResult(mSuccess));
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequest mRequest;
-  const PCacheQueryParams mParams;
+  const CacheDeleteArgs mArgs;
   bool mSuccess;
   nsTArray<nsID> mDeletedBodyIdList;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheKeysAction final : public Manager::BaseAction
 {
 public:
   CacheKeysAction(Manager* aManager, ListenerId aListenerId,
-                  RequestId aRequestId, CacheId aCacheId,
-                  const PCacheRequestOrVoid& aRequestOrVoid,
-                  const PCacheQueryParams& aParams,
+                  CacheId aCacheId, const CacheKeysArgs& aArgs,
                   StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
-    , mRequestOrVoid(aRequestOrVoid)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::CacheKeys(aConn, mCacheId, mRequestOrVoid, mParams,
-                                      mSavedRequests);
+    nsresult rv = db::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;
-      rv = FileUtils::BodyOpen(aQuotaInfo, aDBDir,
-                               mSavedRequests[i].mBodyId,
-                               getter_AddRefs(stream));
+      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
+                    getter_AddRefs(stream));
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
       if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
 
       mStreamList->Add(mSavedRequests[i].mBodyId, stream);
     }
 
     return rv;
   }
 
   virtual void
-  Complete(Listener* aListener, nsresult aRv) override
+  Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mStreamList->Activate(mCacheId);
-    aListener->OnCacheKeys(mRequestId, aRv, mSavedRequests, mStreamList);
+    aListener->OnOpComplete(Move(aRv), CacheKeysResult(), mSavedRequests,
+                            mStreamList);
     mStreamList = nullptr;
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
-  const PCacheRequestOrVoid mRequestOrVoid;
-  const PCacheQueryParams mParams;
+  const CacheKeysArgs mArgs;
   nsRefPtr<StreamList> mStreamList;
   nsTArray<SavedRequest> mSavedRequests;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::StorageMatchAction final : public Manager::BaseAction
 {
 public:
   StorageMatchAction(Manager* aManager, ListenerId aListenerId,
-                     RequestId aRequestId, Namespace aNamespace,
-                     const PCacheRequest& aRequest,
-                     const PCacheQueryParams& aParams,
+                     Namespace aNamespace,
+                     const StorageMatchArgs& aArgs,
                      StreamList* aStreamList)
-    : BaseAction(aManager, aListenerId, aRequestId)
+    : BaseAction(aManager, aListenerId)
     , mNamespace(aNamespace)
-    , mRequest(aRequest)
-    , mParams(aParams)
+    , mArgs(aArgs)
     , mStreamList(aStreamList)
     , mFoundResponse(false)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
-    nsresult rv = DBSchema::StorageMatch(aConn, mNamespace, mRequest, mParams,
-                                         &mFoundResponse, &mSavedResponse);
+    nsresult rv = db::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;