Merge autoland to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 01 Jun 2017 11:54:08 -0400
changeset 409903 cac2fd43de81d44b36396704220996b0c4a39f1a
parent 409879 96b243f2267710b81f5908c7de98fa046c715610 (current diff)
parent 409902 4a2b5682bddf10d2a9a2ee6d7d67f8391553c06b (diff)
child 409921 62005e6aecdf95c9cffe5fb825d93123ec49c4b3
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to m-c. a=merge
--- a/browser/components/resistfingerprinting/test/browser/head.js
+++ b/browser/components/resistfingerprinting/test/browser/head.js
@@ -10,34 +10,37 @@
 async function calcMaximumAvailSize(aChromeWidth, aChromeHeight) {
   let chromeUIWidth;
   let chromeUIHeight;
   let testPath = "http://example.net/browser/browser/" +
                  "components/resistFingerprinting/test/browser/"
 
   // If the chrome UI dimensions is not given, we will calculate it.
   if (!aChromeWidth || !aChromeHeight) {
+    let win = await BrowserTestUtils.openNewBrowserWindow();
+
     let tab = await BrowserTestUtils.openNewForegroundTab(
-      gBrowser, testPath + "file_dummy.html");
+      win.gBrowser, testPath + "file_dummy.html");
 
     let contentSize = await ContentTask.spawn(tab.linkedBrowser, null, async function() {
       let result = {
         width: content.innerWidth,
         height: content.innerHeight
       };
 
       return result;
     });
 
     // Calculate the maximum available window size which is depending on the
     // available screen space.
-    chromeUIWidth = window.outerWidth - contentSize.width;
-    chromeUIHeight = window.outerHeight - contentSize.height;
+    chromeUIWidth = win.outerWidth - contentSize.width;
+    chromeUIHeight = win.outerHeight - contentSize.height;
 
     await BrowserTestUtils.removeTab(tab);
+    await BrowserTestUtils.closeWindow(win);
   } else {
     chromeUIWidth = aChromeWidth;
     chromeUIHeight = aChromeHeight;
   }
 
   let availWidth = window.screen.availWidth;
   let availHeight = window.screen.availHeight;
 
--- a/browser/components/uitour/test/browser_trackingProtection_tour.js
+++ b/browser/components/uitour/test/browser_trackingProtection_tour.js
@@ -36,24 +36,36 @@ add_UITour_task(async function test_bloc
   await checkToggleTarget("controlCenter-trackingBlock");
   TrackingProtection.enableForCurrentPage();
 });
 
 
 async function checkToggleTarget(targetID) {
   let popup = document.getElementById("UITourTooltip");
 
+  let trackerOpened = new Promise(function(resolve, reject) {
+    Services.obs.addObserver(function onopen(subject) {
+      let asciiSpec = subject.QueryInterface(Ci.nsIHttpChannel).URI.asciiSpec;
+      if (asciiSpec === "https://tracking.example.com/") {
+        Services.obs.removeObserver(onopen, "http-on-opening-request");
+        resolve();
+      }
+    }, "http-on-opening-request");
+  });
+
   await ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     let doc = content.document;
     let iframe = doc.createElement("iframe");
     iframe.setAttribute("id", "tracking-element");
     iframe.setAttribute("src", "https://tracking.example.com/");
     doc.body.insertBefore(iframe, doc.body.firstChild);
   });
 
+  await trackerOpened;
+
   let testTargetAvailability = async function(expectedAvailable) {
     let data = await getConfigurationPromise("availableTargets");
     let available = (data.targets.indexOf(targetID) != -1);
     is(available, expectedAvailable, "Target has expected availability.");
   };
   await testTargetAvailability(false);
   await showMenuPromise("controlCenter");
   await testTargetAvailability(true);
--- a/browser/themes/linux/moz.build
+++ b/browser/themes/linux/moz.build
@@ -3,9 +3,11 @@
 # 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/.
 
 DIRS += ['communicator']
 
 JAR_MANIFESTS += ['jar.mn']
 
+DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
+
 include('../tab-svgs.mozbuild')
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1,33 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @import url("chrome://global/skin/");
 
 %include shared.inc
 %define toolbarButtonPressed :hover:active:not([disabled="true"]):not([cui-areatype="menu-panel"])
-%define windowButtonMarginTop 11px
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
 %include ../shared/browser.inc.css
 
 :root {
-  --space-above-tabbar: 9px;
   --tabs-toolbar-color: #333;
 
   --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) + 1px);
 
 %ifdef MOZ_PHOTON_THEME
   --toolbarbutton-border-radius: 4px;
 %else
+  --space-above-tabbar: 9px;
+
   --toolbarbutton-border-radius: 3px;
 
   --toolbarbutton-hover-background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
   --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
 
   --backbutton-border-color: rgba(0,0,0,0.2);
   --backbutton-background: linear-gradient(rgba(255,255,255,0.9),
                                            rgba(255,255,255,0.7)) repeat-x;
@@ -129,29 +129,37 @@ toolbar:-moz-lwtheme {
   height: 22px; /* The native titlebar on OS X is 22px tall. */
 }
 
 /**
  * For tabs in titlebar on OS X, we stretch the titlebar down so that the
  * tabstrip can overlap it.
  */
 #main-window[tabsintitlebar] > #titlebar {
+%ifndef MOZ_PHOTON_THEME
   min-height: calc(var(--tab-min-height) + var(--space-above-tabbar) - var(--tab-toolbar-navbar-overlap));
+%else
+  min-height: calc(var(--tab-min-height) - var(--tab-toolbar-navbar-overlap));
+%endif
 }
 
 /**
  * We also vertically center the window buttons.
  */
 #titlebar-buttonbox-container {
   -moz-box-align: start;
 }
 
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
-  margin-top: @windowButtonMarginTop@;
+%ifdef MOZ_PHOTON_THEME
+  margin-top: 6px;
+%else
+  margin-top: 11px;
+%endif
 }
 
 #main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
   margin-top: 3px;
 }
 
 #main-window[customize-entered] > #titlebar {
@@ -1530,20 +1538,21 @@ toolbarbutton.chevron > .toolbarbutton-m
   -moz-appearance: toolbar;
 }
 
 #TabsToolbar:not(:-moz-lwtheme) {
   color: var(--tabs-toolbar-color);
   text-shadow: @loweredShadow@;
 }
 
+%ifndef MOZ_PHOTON_THEME
 #navigator-toolbox[inFullscreen] > #TabsToolbar {
   padding-top: var(--space-above-tabbar);
 }
-
+%endif
 #tabbrowser-tabs {
   -moz-box-align: stretch;
 }
 
 .tabs-newtab-button > .toolbarbutton-icon {
   padding: 6px 0 4px;
 }
 
@@ -2205,17 +2214,19 @@ html|*.addon-webext-perm-list {
     background-image: url("chrome://browser/skin/privatebrowsing-mask@2x.png");
   }
   #main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
     background-image: url("chrome://browser/skin/privatebrowsing-mask-short@2x.png");
   }
 }
 
 #TabsToolbar > .private-browsing-indicator {
+%ifndef MOZ_PHOTON_THEME
   transform: translateY(calc(-1 * var(--space-above-tabbar)));
+%endif
   /* We offset by 38px for mask graphic, plus 4px to account for the
    * margin-left, which sums to 42px.
    */
   margin-right: -42px;
 }
 
 #main-window[privatebrowsingmode=temporary] .titlebar-placeholder[type="fullscreen-button"],
 #main-window[privatebrowsingmode=temporary] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
--- a/browser/themes/osx/compacttheme.css
+++ b/browser/themes/osx/compacttheme.css
@@ -23,22 +23,23 @@
   -moz-appearance: none !important;
 }
 
 /* Get rid of 1px bright strip at the top of window */
 #main-window[tabsintitlebar] #titlebar-content {
   background: var(--chrome-background-color);
 }
 
+%ifndef MOZ_PHOTON_THEME
 /* Resize things so that the native titlebar is in line with the tabs */
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
   margin-top: 6px;
 }
-
+%endif
 /* Prevent the hover styling from on the identity icon from overlapping the
    urlbar border. */
 #identity-box {
   margin-top: -1px !important;
   margin-bottom: -1px !important;
   padding-top: 3px !important;
   padding-bottom: 3px !important;
 }
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -1,13 +1,26 @@
 /* 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/. */
 
 %ifdef MOZ_PHOTON_THEME
+%ifdef CAN_DRAW_IN_TITLEBAR
+/* Add space for dragging the window */
+%ifdef MENUBAR_CAN_AUTOHIDE
+:root[tabsintitlebar] #toolbar-menubar[autohide=true] ~ #TabsToolbar {
+  padding-inline-start: 40px;
+}
+%else
+:root[tabsintitlebar] #TabsToolbar {
+  padding-inline-start: 40px;
+}
+%endif
+%endif
+
 /* Go button */
 .urlbar-go-button {
   padding: 0 3px;
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
 }
 
 .urlbar-go-button {
   -moz-image-region: rect(0, 42px, 14px, 28px);
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -4,17 +4,19 @@
 
 /* compacttheme.css is loaded in browser.xul after browser.css when it is
    preffed on.  The bulk of the styling is here in the shared file, but
    there are overrides for each platform in their compacttheme.css files. */
 
 :root {
   --tab-toolbar-navbar-overlap: 0px;
   --navbar-tab-toolbar-highlight-overlap: 0px;
+%ifndef MOZ_PHOTON_THEME
   --space-above-tabbar: 0px;
+%endif
   --toolbarbutton-text-shadow: none;
   --backbutton-urlbar-overlap: 0px;
   /* 18px icon + 2 * 5px padding + 1 * 1px border */
   --forwardbutton-width: 29px;
 }
 
 :root:-moz-lwtheme-brighttext {
   /* Chrome */
@@ -93,22 +95,23 @@ toolbar:-moz-lwtheme-darktext {
   --toolbarbutton-hover-boxshadow: none;
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,0.1);
   --toolbarbutton-active-background: #d7d7d8 border-box;
   --toolbarbutton-active-boxshadow: none;
   --toolbarbutton-active-bordercolor: rgba(0,0,0,0.15);
   --toolbarbutton-checkedhover-backgroundcolor: #d7d7d8;
 }
 
+%ifndef MOZ_PHOTON_THEME
 /* Give some space to drag the window around while customizing
    (normal space to left and right of tabs doesn't work in this case) */
 #main-window[tabsintitlebar][customizing] {
   --space-above-tabbar: 9px;
 }
-
+%endif
 /* Override @tabCurveHalfWidth@ and @tabCurveWidth@.  XXX: Switch to a CSS variable once the perf is sorted out - bug 1088771 */
 .tab-background-middle {
   border-left-width: 0;
   border-right-width: 0;
   margin: 0;
 }
 
 .tab-background,
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -333,21 +333,22 @@
 
     #main-window[sizemode=normal] #browser-bottombox {
       border: 1px solid @toolbarShadowColor@;
       border-top-style: none;
       background-clip: padding-box;
     }
   }
 
+%ifndef MOZ_PHOTON_THEME
   #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 1px;
     padding-right: 1px;
   }
-
+%endif
   #appcontent:not(:-moz-lwtheme) {
     background-color: -moz-dialog;
   }
 }
 
 @media (-moz-windows-glass) {
   #main-window[sizemode=normal] #nav-bar {
     border-top-left-radius: 2.5px;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -12,23 +12,23 @@
 %include windowsShared.inc
 %define toolbarShadowColor hsla(209,67%,12%,0.35)
 
 %include ../shared/browser.inc.css
 
 :root {
   --titlebar-text-color: inherit;
 
-  --space-above-tabbar: 15px;
-
   --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px);
 
 %ifdef MOZ_PHOTON_THEME
   --toolbarbutton-border-radius: 2px;
 %else
+  --space-above-tabbar: 15px;
+
   --toolbarbutton-border-radius: 1px;
 
   --toolbarbutton-hover-background: rgba(0,0,0,.1);
   --toolbarbutton-active-background: rgba(0,0,0,.15);
 
   --backbutton-border-color: var(--urlbar-border-color-hover);
   --backbutton-background: rgba(255,255,255,.15);
 
@@ -84,29 +84,30 @@ toolbar[brighttext] {
 }
 
 /* Hides the titlebar-placeholder underneath the window caption buttons when we
    are not autohiding the menubar. */
 #toolbar-menubar:not([autohide="true"]) + #TabsToolbar > .titlebar-placeholder[type="caption-buttons"] {
   display: none;
 }
 
+%ifndef MOZ_PHOTON_THEME
 /* We want a 4px gap between the TabsToolbar and the toolbar-menubar when the
    toolbar-menu is displayed, and a 16px gap when it is not. 1px is taken care
    of by the (light) outer shadow of the tab, the remaining 3/15 are these margins. */
 #toolbar-menubar:not([autohide=true]) ~ #TabsToolbar:not([inFullscreen]),
 #toolbar-menubar[autohide=true]:not([inactive]) ~ #TabsToolbar:not([inFullscreen]) {
   margin-top: 3px;
 }
 
 #main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen])[chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar,
 #main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar {
   margin-top: var(--space-above-tabbar);
 }
-
+%endif
 #navigator-toolbox,
 #navigator-toolbox > toolbar {
   -moz-appearance: none;
 }
 
 #navigator-toolbox::after {
   content: "";
   display: -moz-box;
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -67,20 +67,21 @@
 @media (min-resolution: 1.1dppx) {
   #TabsToolbar[brighttext] .tab-close-button:-moz-lwtheme-darktext:not([selected="true"]) {
     list-style-image: url("chrome://global/skin/icons/close@2x.png");
   }
 }
 
 @media (-moz-os-version: windows-win7),
        (-moz-os-version: windows-win8) {
+%ifndef MOZ_PHOTON_THEME
   :root {
     --space-above-tabbar: 15px;
   }
-
+%endif
   /* It'd be nice if there was an element in the scrollbox's inner content
      that collapsed to the current width of the tabs. Since there isn't we
      need to handle overflowing and non-overflowing tabs separately.
 
      In the case of overflowing tabs, set a border-top on the entire container,
      otherwise we need to set it on each element individually */
   #main-window[sizemode=normal] .tabbrowser-tabs[overflow="true"] {
     background-clip: padding-box;
--- a/browser/themes/windows/moz.build
+++ b/browser/themes/windows/moz.build
@@ -4,10 +4,11 @@
 # 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/.
 
 DIRS += ['communicator']
 
 JAR_MANIFESTS += ['jar.mn']
 
 DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
+DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
 
 include('../tab-svgs.mozbuild')
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -852,16 +852,19 @@ var gDevToolsBrowser = exports.gDevTools
 
     gDevToolsBrowser._pingTelemetry();
     gDevToolsBrowser._telemetry = null;
 
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._forgetBrowserWindow(win);
     }
 
+    // Remove scripts loaded in content process to support the Browser Content Toolbox.
+    DebuggerServer.removeContentServerScript();
+
     gDevTools.destroy({ shuttingDown });
   },
 };
 
 // Handle all already registered tools,
 gDevTools.getToolDefinitionArray()
          .forEach(def => gDevToolsBrowser._addToolToWindows(def));
 // and the new ones.
new file mode 100644
--- /dev/null
+++ b/devtools/server/content-process-debugger-server.js
@@ -0,0 +1,26 @@
+/* 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/. */
+
+/* global addMessageListener, removeMessageListener */
+
+"use strict";
+
+const { utils: Cu } = Components;
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+function onInit(message) {
+  // Only reply if we are in a real content process
+  if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+    let {init} = Cu.import("resource://devtools/server/content-server.jsm", {});
+    init(message);
+  }
+}
+
+function onClose() {
+  removeMessageListener("debug:init-content-server", onInit);
+  removeMessageListener("debug:close-content-server", onClose);
+}
+
+addMessageListener("debug:init-content-server", onInit);
+addMessageListener("debug:close-content-server", onClose);
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -64,16 +64,19 @@ if (isWorker) {
   const VERBOSE_PREF = "devtools.debugger.log.verbose";
 
   flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
   flags.wantVerbose =
     Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
     Services.prefs.getBoolPref(VERBOSE_PREF);
 }
 
+const CONTENT_PROCESS_DBG_SERVER_SCRIPT =
+  "resource://devtools/server/content-process-debugger-server.js";
+
 function loadSubScript(url) {
   try {
     let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                    .getService(Ci.mozIJSSubScriptLoader);
     loader.loadSubScript(url, this);
   } catch (e) {
     let errorStr = "Error loading: " + url + ":\n" +
                    (e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n" : "") +
@@ -144,16 +147,18 @@ function ModuleAPI() {
 }
 
 /** *
  * Public API
  */
 var DebuggerServer = {
   _listeners: [],
   _initialized: false,
+  // Flag to check if the content process debugger server script was already loaded.
+  _contentProcessScriptLoaded: false,
   // Map of global actor names to actor constructors provided by extensions.
   globalActorFactories: {},
   // Map of tab actor names to actor constructors provided by extensions.
   tabActorFactories: {},
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
   LONG_STRING_READ_LENGTH: 65 * 1024,
@@ -750,17 +755,26 @@ var DebuggerServer = {
 
       dumpn("establishing forwarding for process with prefix " + prefix);
 
       actor = msg.json.actor;
 
       deferred.resolve(actor);
     });
 
-    mm.sendAsyncMessage("DevTools:InitDebuggerServer", {
+    // Load the content process debugger server script only once.
+    if (!this._contentProcessScriptLoaded) {
+      // Load the process script that will receive the debug:init-content-server message
+      Services.ppmm.loadProcessScript(CONTENT_PROCESS_DBG_SERVER_SCRIPT, true);
+      this._contentProcessScriptLoaded = true;
+    }
+
+    // Send a message to the content process debugger server script to forward it the
+    // prefix.
+    mm.sendAsyncMessage("debug:init-content-server", {
       prefix: prefix
     });
 
     function onClose() {
       Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
       events.off(connection, "closed", onClose);
       if (childTransport) {
         // If we have a child transport, the actor has already
@@ -1364,16 +1378,30 @@ var DebuggerServer = {
         for (let connID of Object.getOwnPropertyNames(this._connections)) {
           this._connections[connID].rootActor.removeActorByName(name);
         }
       }
     }
   },
 
   /**
+   * Called when DevTools are unloaded to remove the contend process server script for the
+   * list of scripts loaded for each new content process. Will also remove message
+   * listeners from already loaded scripts.
+   */
+  removeContentServerScript() {
+    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_DBG_SERVER_SCRIPT);
+    try {
+      Services.ppmm.broadcastAsyncMessage("debug:close-content-server");
+    } catch (e) {
+      // Nothing to do
+    }
+  },
+
+  /**
    * ⚠ TESTING ONLY! ⚠ Searches all active connections for an actor matching an ID.
    * This is helpful for some tests which depend on reaching into the server to check some
    * properties of an actor.
    */
   _searchAllConnectionsForActor(actorID) {
     for (let connID of Object.getOwnPropertyNames(this._connections)) {
       let actor = this._connections[connID].getActor(actorID);
       if (actor) {
--- a/devtools/server/moz.build
+++ b/devtools/server/moz.build
@@ -13,16 +13,17 @@ DIRS += [
 ]
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 DevToolsModules(
     'child.js',
+    'content-process-debugger-server.js',
     'content-server.jsm',
     'css-logic.js',
     'event-parsers.js',
     'main.js',
     'primitive.js',
     'service-worker-child.js',
     'websocket-server.js',
     'worker.js'
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -29,16 +29,21 @@ H264Converter::H264Converter(PlatformDec
   , mDecoder(nullptr)
   , mGMPCrashHelper(aParams.mCrashHelper)
   , mLastError(NS_OK)
   , mType(aParams.mType)
   , mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent)
   , mDecoderOptions(aParams.mOptions)
 {
   CreateDecoder(mOriginalConfig, aParams.mDiagnostics);
+  if (mDecoder) {
+    MOZ_ASSERT(mp4_demuxer::AnnexB::HasSPS(mOriginalConfig.mExtraData));
+    // The video metadata contains out of band SPS/PPS (AVC1) store it.
+    mOriginalExtraData = mOriginalConfig.mExtraData;
+  }
 }
 
 H264Converter::~H264Converter()
 {
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 H264Converter::Init()
@@ -74,16 +79,25 @@ H264Converter::Decode(MediaRawData* aSam
     // contain a SPS.
     rv = CreateDecoderAndInit(aSample);
     if (rv == NS_ERROR_NOT_INITIALIZED) {
       // We are missing the required SPS to create the decoder.
       // Ignore for the time being, the MediaRawData will be dropped.
       return DecodePromise::CreateAndResolve(DecodedData(), __func__);
     }
   } else {
+    // Initialize the members that we couldn't if the extradata was given during
+    // H264Converter's construction.
+    if (!mNeedAVCC) {
+      mNeedAVCC =
+        Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
+    }
+    if (!mCanRecycleDecoder) {
+      mCanRecycleDecoder = Some(CanRecycleDecoder());
+    }
     rv = CheckForSPSChange(aSample);
   }
 
   if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
     // The decoder is pending initialization.
     RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
     return p;
   }
@@ -94,21 +108,16 @@ H264Converter::Decode(MediaRawData* aSam
                   RESULT_DETAIL("Unable to create H264 decoder")),
       __func__);
   }
 
   if (mNeedKeyframe && !aSample->mKeyframe) {
     return DecodePromise::CreateAndResolve(DecodedData(), __func__);
   }
 
-  if (!mNeedAVCC) {
-    mNeedAVCC =
-      Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
-  }
-
   if (!*mNeedAVCC
       && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) {
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("ConvertSampleToAnnexB")),
       __func__);
   }
 
@@ -255,16 +264,21 @@ H264Converter::CreateDecoderAndInit(Medi
   return rv;
 }
 
 void
 H264Converter::OnDecoderInitDone(const TrackType aTrackType)
 {
   mInitPromiseRequest.Complete();
   RefPtr<MediaRawData> sample = mPendingSample.forget();
+
+  mNeedAVCC =
+    Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
+  mCanRecycleDecoder = Some(CanRecycleDecoder());
+
   DecodeFirstSample(sample);
 }
 
 void
 H264Converter::OnDecoderInitFailed(const MediaResult& aError)
 {
   mInitPromiseRequest.Complete();
   mDecodePromise.Reject(
@@ -284,19 +298,16 @@ H264Converter::CanRecycleDecoder() const
 void
 H264Converter::DecodeFirstSample(MediaRawData* aSample)
 {
   if (mNeedKeyframe && !aSample->mKeyframe) {
     mDecodePromise.Resolve(DecodedData(), __func__);
     return;
   }
 
-  mNeedAVCC =
-    Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
-
   if (!*mNeedAVCC
       && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) {
     mDecodePromise.Reject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("ConvertSampleToAnnexB")),
       __func__);
     return;
   }
@@ -317,25 +328,40 @@ H264Converter::DecodeFirstSample(MediaRa
     ->Track(mDecodePromiseRequest);
 }
 
 nsresult
 H264Converter::CheckForSPSChange(MediaRawData* aSample)
 {
   RefPtr<MediaByteBuffer> extra_data =
     mp4_demuxer::AnnexB::ExtractExtraData(aSample);
-  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)
-      || mp4_demuxer::AnnexB::CompareExtraData(extra_data,
-                                               mCurrentConfig.mExtraData)) {
-        return NS_OK;
-      }
+  if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
+    MOZ_ASSERT(mCanRecycleDecoder.isSome());
+    if (!*mCanRecycleDecoder) {
+      // If the decoder can't be recycled, the out of band extradata will never
+      // change as the H264Converter will be recreated by the MediaFormatReader
+      // instead. So there's no point in testing for changes.
+      return NS_OK;
+    }
+    // This sample doesn't contain inband SPS/PPS
+    // We now check if the out of band one has changed.
+    if (mp4_demuxer::AnnexB::HasSPS(aSample->mExtraData) &&
+        !mp4_demuxer::AnnexB::CompareExtraData(aSample->mExtraData,
+                                               mOriginalExtraData)) {
+      extra_data = mOriginalExtraData = aSample->mExtraData;
+    }
+  }
+  if (mp4_demuxer::AnnexB::CompareExtraData(extra_data,
+                                            mCurrentConfig.mExtraData)) {
+    return NS_OK;
+  }
 
   RefPtr<MediaRawData> sample = aSample;
-
-  if (CanRecycleDecoder()) {
+  MOZ_ASSERT(mCanRecycleDecoder.isSome());
+  if (*mCanRecycleDecoder) {
     // Do not recreate the decoder, reuse it.
     UpdateConfigFromExtraData(extra_data);
     if (!sample->mTrackInfo) {
       sample->mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, 0);
     }
     mNeedKeyframe = true;
     return NS_OK;
   }
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -76,16 +76,18 @@ private:
 
   bool CanRecycleDecoder() const;
 
   void DecodeFirstSample(MediaRawData* aSample);
 
   RefPtr<PlatformDecoderModule> mPDM;
   const VideoInfo mOriginalConfig;
   VideoInfo mCurrentConfig;
+  // Current out of band extra data (as found in metadata's VideoInfo).
+  RefPtr<MediaByteBuffer> mOriginalExtraData;
   RefPtr<layers::KnowsCompositor> mKnowsCompositor;
   RefPtr<layers::ImageContainer> mImageContainer;
   const RefPtr<TaskQueue> mTaskQueue;
   RefPtr<MediaRawData> mPendingSample;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
   MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
   MozPromiseHolder<DecodePromise> mDecodePromise;
@@ -95,13 +97,14 @@ private:
 
   RefPtr<GMPCrashHelper> mGMPCrashHelper;
   Maybe<bool> mNeedAVCC;
   nsresult mLastError;
   bool mNeedKeyframe = true;
   const TrackInfo::TrackType mType;
   MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
   const CreateDecoderParams::OptionSet mDecoderOptions;
+  Maybe<bool> mCanRecycleDecoder;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_H264Converter_h
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -28,16 +28,17 @@
 #include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
 #include "mozilla/layout/VsyncChild.h"
+#include "mozilla/net/HttpBackgroundChannelChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "mozilla/dom/WebAuthnTransactionChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
 
@@ -554,16 +555,34 @@ bool
 BackgroundChildImpl::DeallocPWebAuthnTransactionChild(PWebAuthnTransactionChild* aActor)
 {
   MOZ_ASSERT(aActor);
   RefPtr<dom::WebAuthnTransactionChild> child =
     dont_AddRef(static_cast<dom::WebAuthnTransactionChild*>(aActor));
   return true;
 }
 
+net::PHttpBackgroundChannelChild*
+BackgroundChildImpl::AllocPHttpBackgroundChannelChild(const uint64_t& aChannelId)
+{
+  MOZ_CRASH("PHttpBackgroundChannelChild actor should be manually constructed!");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelChild* aActor)
+{
+  // The reference is increased in BackgroundChannelCreateCallback::ActorCreated
+  // of HttpBackgroundChannelChild.cpp. We should decrease it after IPC
+  // destroyed.
+  RefPtr<net::HttpBackgroundChannelChild> child =
+    dont_AddRef(static_cast<net::HttpBackgroundChannelChild*>(aActor));
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 mozilla::ipc::IPCResult
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -197,16 +197,22 @@ protected:
   OnChannelReceivedMessage(const Message& aMsg) override;
 #endif
 
   virtual PWebAuthnTransactionChild*
   AllocPWebAuthnTransactionChild() override;
 
   virtual bool
   DeallocPWebAuthnTransactionChild(PWebAuthnTransactionChild* aActor) override;
+
+  virtual PHttpBackgroundChannelChild*
+  AllocPHttpBackgroundChannelChild(const uint64_t& aChannelId) override;
+
+  virtual bool
+  DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/layout/VsyncParent.h"
+#include "mozilla/net/HttpBackgroundChannelParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "mozilla/dom/WebAuthnTransactionParent.h"
 #include "mozilla/Preferences.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsProxyRelease.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
@@ -867,16 +868,63 @@ BackgroundParentImpl::AllocPWebAuthnTran
 bool
 BackgroundParentImpl::DeallocPWebAuthnTransactionParent(dom::PWebAuthnTransactionParent *aActor)
 {
   MOZ_ASSERT(aActor);
   delete aActor;
   return true;
 }
 
+net::PHttpBackgroundChannelParent*
+BackgroundParentImpl::AllocPHttpBackgroundChannelParent(const uint64_t& aChannelId)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  RefPtr<net::HttpBackgroundChannelParent> actor =
+    new net::HttpBackgroundChannelParent();
+
+  // hold extra refcount for IPDL
+  return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPHttpBackgroundChannelConstructor(
+                                      net::PHttpBackgroundChannelParent *aActor,
+                                      const uint64_t& aChannelId)
+{
+  MOZ_ASSERT(aActor);
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  net::HttpBackgroundChannelParent* aParent =
+    static_cast<net::HttpBackgroundChannelParent*>(aActor);
+
+  if (NS_WARN_IF(NS_FAILED(aParent->Init(aChannelId)))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  return IPC_OK();
+}
+
+bool
+BackgroundParentImpl::DeallocPHttpBackgroundChannelParent(
+                                      net::PHttpBackgroundChannelParent *aActor)
+{
+  MOZ_ASSERT(aActor);
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // release extra refcount hold by AllocPHttpBackgroundChannelParent
+  RefPtr<net::HttpBackgroundChannelParent> actor =
+    dont_AddRef(static_cast<net::HttpBackgroundChannelParent*>(aActor));
+
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mozilla::ipc::AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -221,14 +221,23 @@ protected:
   virtual bool
   DeallocPGamepadTestChannelParent(PGamepadTestChannelParent* aActor) override;
 
   virtual PWebAuthnTransactionParent*
   AllocPWebAuthnTransactionParent() override;
 
   virtual bool
   DeallocPWebAuthnTransactionParent(PWebAuthnTransactionParent* aActor) override;
+
+  virtual PHttpBackgroundChannelParent*
+  AllocPHttpBackgroundChannelParent(const uint64_t& aChannelId) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPHttpBackgroundChannelConstructor(PHttpBackgroundChannelParent *aActor,
+                                        const uint64_t& aChannelId) override;
+  virtual bool
+  DeallocPHttpBackgroundChannelParent(PHttpBackgroundChannelParent *aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -9,16 +9,17 @@ include protocol PBackgroundTest;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
+include protocol PHttpBackgroundChannel;
 include protocol PIPCBlobInputStream;
 include protocol PPendingIPCBlob;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PQuota;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PServiceWorkerManager;
@@ -56,16 +57,17 @@ sync protocol PBackground
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
+  manages PHttpBackgroundChannel;
   manages PIPCBlobInputStream;
   manages PPendingIPCBlob;
   manages PMessagePort;
   manages PCameras;
   manages PQuota;
   manages PChildToParentStream;
   manages PParentToChildStream;
   manages PServiceWorkerManager;
@@ -110,16 +112,18 @@ parent:
   async PQuota();
 
   async PFileSystemRequest(FileSystemParams params);
 
   async PGamepadEventChannel();
 
   async PGamepadTestChannel();
 
+  async PHttpBackgroundChannel(uint64_t channelId);
+
   async PWebAuthnTransaction();
 
 child:
   async PCache();
   async PCacheStreamControl();
 
   async PParentToChildStream();
 
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -350,25 +350,28 @@ CSSStyleSheet::CSSStyleSheet(css::SheetP
   mInner->AddSheet(this);
 }
 
 CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
                              CSSStyleSheet* aParentToUse,
                              dom::CSSImportRule* aOwnerRuleToUse,
                              nsIDocument* aDocumentToUse,
                              nsINode* aOwningNodeToUse)
-  : StyleSheet(aCopy, aOwnerRuleToUse, aDocumentToUse, aOwningNodeToUse),
-    mInRuleProcessorCache(false),
-    mScopeElement(nullptr),
-    mRuleProcessors(nullptr)
+  : StyleSheet(aCopy,
+               aParentToUse,
+               aOwnerRuleToUse,
+               aDocumentToUse,
+               aOwningNodeToUse)
+  , mInRuleProcessorCache(false)
+  , mScopeElement(nullptr)
+  , mRuleProcessors(nullptr)
 {
-  mParent = aParentToUse;
-
   if (mDirty) { // CSSOM's been there, force full copy now
-    NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
+    NS_ASSERTION(mInner->mComplete,
+                 "Why have rules been accessed on an incomplete sheet?");
     // FIXME: handle failure?
     //
     // NOTE: It's important to call this from the subclass, since it could
     // access uninitialized members otherwise.
     EnsureUniqueInner();
   }
 }
 
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -79,22 +79,25 @@ ServoStyleSheet::ServoStyleSheet(css::Sh
   mInner->AddSheet(this);
 }
 
 ServoStyleSheet::ServoStyleSheet(const ServoStyleSheet& aCopy,
                                  ServoStyleSheet* aParentToUse,
                                  dom::CSSImportRule* aOwnerRuleToUse,
                                  nsIDocument* aDocumentToUse,
                                  nsINode* aOwningNodeToUse)
-  : StyleSheet(aCopy, aOwnerRuleToUse, aDocumentToUse, aOwningNodeToUse)
+  : StyleSheet(aCopy,
+               aParentToUse,
+               aOwnerRuleToUse,
+               aDocumentToUse,
+               aOwningNodeToUse)
 {
-  mParent = aParentToUse;
-
   if (mDirty) { // CSSOM's been there, force full copy now
-    NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
+    NS_ASSERTION(mInner->mComplete,
+                 "Why have rules been accessed on an incomplete sheet?");
     // FIXME: handle failure?
     //
     // NOTE: It's important to call this from the subclass, since this could
     // access uninitialized members otherwise.
     EnsureUniqueInner();
   }
 }
 
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -29,29 +29,30 @@ StyleSheet::StyleSheet(StyleBackendType 
   , mDisabled(false)
   , mDocumentAssociationMode(NotOwnedByDocument)
   , mInner(nullptr)
   , mDirty(false)
 {
 }
 
 StyleSheet::StyleSheet(const StyleSheet& aCopy,
+                       StyleSheet* aParentToUse,
                        dom::CSSImportRule* aOwnerRuleToUse,
                        nsIDocument* aDocumentToUse,
                        nsINode* aOwningNodeToUse)
-  : mParent(nullptr)
+  : mParent(aParentToUse)
   , mTitle(aCopy.mTitle)
   , mDocument(aDocumentToUse)
   , mOwningNode(aOwningNodeToUse)
   , mOwnerRule(aOwnerRuleToUse)
   , mParsingMode(aCopy.mParsingMode)
   , mType(aCopy.mType)
   , mDisabled(aCopy.mDisabled)
-    // We only use this constructor during cloning.  It's the cloner's
-    // responsibility to notify us if we end up being owned by a document.
+  // We only use this constructor during cloning.  It's the cloner's
+  // responsibility to notify us if we end up being owned by a document.
   , mDocumentAssociationMode(NotOwnedByDocument)
   , mInner(aCopy.mInner) // Shallow copy, but concrete subclasses will fix up.
   , mDirty(aCopy.mDirty)
 {
   MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
   mInner->AddSheet(this);
 
   if (aCopy.mMedia) {
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -48,16 +48,17 @@ class Rule;
  */
 class StyleSheet : public nsIDOMCSSStyleSheet
                  , public nsICSSLoaderObserver
                  , public nsWrapperCache
 {
 protected:
   StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode);
   StyleSheet(const StyleSheet& aCopy,
+             StyleSheet* aParentToUse,
              dom::CSSImportRule* aOwnerRuleToUse,
              nsIDocument* aDocumentToUse,
              nsINode* aOwningNodeToUse);
   virtual ~StyleSheet();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(StyleSheet,
--- a/media/libstagefright/binding/AnnexB.cpp
+++ b/media/libstagefright/binding/AnnexB.cpp
@@ -272,33 +272,41 @@ AnnexB::ConvertSampleToAVCC(mozilla::Med
 }
 
 already_AddRefed<mozilla::MediaByteBuffer>
 AnnexB::ExtractExtraData(const mozilla::MediaRawData* aSample)
 {
   MOZ_ASSERT(IsAVCC(aSample));
 
   RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer;
-  if (HasSPS(aSample->mExtraData)) {
-    // We already have an explicit extradata, re-use it.
-    extradata = aSample->mExtraData;
-    return extradata.forget();
-  }
 
   // SPS content
   mozilla::Vector<uint8_t> sps;
   ByteWriter spsw(sps);
   int numSps = 0;
   // PPS content
   mozilla::Vector<uint8_t> pps;
   ByteWriter ppsw(pps);
   int numPps = 0;
 
   int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
-  ByteReader reader(aSample->Data(), aSample->Size());
+
+  size_t sampleSize = aSample->Size();
+  if (aSample->mCrypto.mValid) {
+    // The content is encrypted, we can only parse the non-encrypted data.
+    MOZ_ASSERT(aSample->mCrypto.mPlainSizes.Length() > 0);
+    if (aSample->mCrypto.mPlainSizes.Length() == 0 ||
+        aSample->mCrypto.mPlainSizes[0] > sampleSize) {
+      // This is invalid content.
+      return nullptr;
+    }
+    sampleSize = aSample->mCrypto.mPlainSizes[0];
+  }
+
+  ByteReader reader(aSample->Data(), sampleSize);
 
   // Find SPS and PPS NALUs in AVCC data
   while (reader.Remaining() > nalLenSize) {
     uint32_t nalLen;
     switch (nalLenSize) {
       case 1: nalLen = reader.ReadU8();  break;
       case 2: nalLen = reader.ReadU16(); break;
       case 3: nalLen = reader.ReadU24(); break;
@@ -361,57 +369,16 @@ AnnexB::HasSPS(const mozilla::MediaByteB
     return false;
   }
   uint8_t numSps = reader.ReadU8() & 0x1f;
 
   return numSps > 0;
 }
 
 bool
-AnnexB::HasPPS(const mozilla::MediaRawData* aSample)
-{
-  return HasPPS(aSample->mExtraData);
-}
-
-bool
-AnnexB::HasPPS(const mozilla::MediaByteBuffer* aExtraData)
-{
-  if (!aExtraData) {
-    return false;
-  }
-
-  ByteReader reader(aExtraData);
-  const uint8_t* ptr = reader.Read(5);
-  if (!ptr || !reader.CanRead8()) {
-    return false;
-  }
-  uint8_t numSps = reader.ReadU8() & 0x1f;
-  // Skip over the included SPS.
-  for (uint8_t i = 0; i < numSps; i++) {
-    if (reader.Remaining() < 3) {
-      return false;
-    }
-    uint16_t length = reader.ReadU16();
-    if ((reader.PeekU8() & 0x1f) != 7) {
-      // Not an SPS NAL type.
-      return false;
-    }
-    if (!reader.Read(length)) {
-      return false;
-    }
-  }
-  if (!reader.CanRead8()) {
-    return false;
-  }
-  uint8_t numPps = reader.ReadU8();
-
-  return numPps > 0;
-}
-
-bool
 AnnexB::ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample)
 {
   MOZ_ASSERT(IsAVCC(aSample));
 
   int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
 
   if (nalLenSize == 4) {
     return true;
--- a/media/libstagefright/binding/H264.cpp
+++ b/media/libstagefright/binding/H264.cpp
@@ -176,37 +176,16 @@ SPSData::SPSData()
   video_format = 5;
   colour_primaries = 2;
   transfer_characteristics = 2;
   sample_ratio = 1.0;
   memset(scaling_matrix4x4, 16, sizeof(scaling_matrix4x4));
   memset(scaling_matrix8x8, 16, sizeof(scaling_matrix8x8));
 }
 
-PPSData::PPSData()
-{
-  PodZero(this);
-  memset(scaling_matrix4x4, 16, sizeof(scaling_matrix4x4));
-  memset(scaling_matrix8x8, 16, sizeof(scaling_matrix8x8));
-}
-
-const uint8_t H264::ZZ_SCAN[16] = { 0,  1,  4,  8,
-                                    5,  2,  3,  6,
-                                    9, 12, 13, 10,
-                                    7, 11, 14, 15 };
-
-const uint8_t H264::ZZ_SCAN8[64] = {  0,  1,  8, 16,  9,  2,  3, 10,
-                                     17, 24, 32, 25, 18, 11,  4,  5,
-                                     12, 19, 26, 33, 40, 48, 41, 34,
-                                     27, 20, 13,  6,  7, 14, 21, 28,
-                                     35, 42, 49, 56, 57, 50, 43, 36,
-                                     29, 22, 15, 23, 30, 37, 44, 51,
-                                     58, 59, 52, 45, 38, 31, 39, 46,
-                                     53, 60, 61, 54, 47, 55, 62, 63 };
-
 /* static */ already_AddRefed<mozilla::MediaByteBuffer>
 H264::DecodeNALUnit(const mozilla::MediaByteBuffer* aNAL)
 {
   MOZ_ASSERT(aNAL);
 
   if (aNAL->Length() < 4) {
     return nullptr;
   }
@@ -602,79 +581,56 @@ H264::vui_parameters(BitReader& aBr, SPS
   }
   return true;
 }
 
 /* static */ bool
 H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
                              SPSData& aDest)
 {
-  H264ParametersSet ps;
-  if (!DecodeSPSDataSetFromExtraData(aExtraData, ps.SPSes)) {
-    return false;
-  }
-
-  uint16_t spsId = 0;
-  if (DecodePPSDataSetFromExtraData(aExtraData, ps.SPSes, ps.PPSes)) {
-    // We can't know which PPS is in use without parsing slice header if we
-    // have multiple PPSes, so we always use the first one.
-    spsId = ps.PPSes[0].seq_parameter_set_id;
-  }
-
-  aDest = Move(ps.SPSes[spsId]);
-  return true;
-}
-
-/* static */ bool
-H264::DecodeSPSDataSetFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
-                                    SPSDataSet& aDest)
-{
   if (!AnnexB::HasSPS(aExtraData)) {
     return false;
   }
   ByteReader reader(aExtraData);
 
   if (!reader.Read(5)) {
     return false;
   }
 
   uint8_t numSps = reader.ReadU8() & 0x1f;
   if (!numSps) {
     // No SPS.
     return false;
   }
 
-  for (uint32_t idx = 0; idx < numSps; idx++) {
-    uint16_t length = reader.ReadU16();
+  if (numSps > 1) {
+    NS_WARNING("Multiple SPS, only decoding the first one");
+  }
 
-    if ((reader.PeekU8() & 0x1f) != H264_NAL_SPS) {
-      // Not a SPS NAL type.
-      return false;
-    }
-    const uint8_t* ptr = reader.Read(length);
-    if (!ptr) {
-      return false;
-    }
+  uint16_t length = reader.ReadU16();
 
-    RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer;
-    rawNAL->AppendElements(ptr, length);
-
-    RefPtr<mozilla::MediaByteBuffer> sps = DecodeNALUnit(rawNAL);
+  if ((reader.PeekU8() & 0x1f) != H264_NAL_SPS) {
+    // Not a SPS NAL type.
+    return false;
+  }
+  const uint8_t* ptr = reader.Read(length);
+  if (!ptr) {
+    return false;
+  }
 
-    if (!sps) {
-      return false;
-    }
+  RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer;
+  rawNAL->AppendElements(ptr, length);
+
+  RefPtr<mozilla::MediaByteBuffer> sps = DecodeNALUnit(rawNAL);
 
-    SPSData spsData;
-    if (!DecodeSPS(sps, spsData)) {
-      return false;
-    }
-    aDest.AppendElement(spsData);
+  if (!sps) {
+    return false;
   }
-  return true;
+
+  return DecodeSPS(sps, aDest);
 }
 
 /* static */ bool
 H264::EnsureSPSIsSane(SPSData& aSPS)
 {
   bool valid = true;
   static const float default_aspect = 4.0f / 3.0f;
   if (aSPS.sample_ratio <= 0.0f || aSPS.sample_ratio > 6.0f) {
@@ -689,223 +645,16 @@ H264::EnsureSPSIsSane(SPSData& aSPS)
   }
   if (aSPS.max_num_ref_frames > 16) {
     aSPS.max_num_ref_frames = 16;
     valid = false;
   }
   return valid;
 }
 
-/* static */ bool
-H264::DecodePPSDataSetFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
-                                    const SPSDataSet& aSPSes, PPSDataSet& aDest)
-{
-  if (!AnnexB::HasPPS(aExtraData)) {
-    return false;
-  }
-  ByteReader reader(aExtraData);
-
-  if (!reader.Read(5)) {
-    return false;
-  }
-
-  uint8_t numSps = reader.ReadU8() & 0x1f;
-  if (!numSps) {
-    // No SPS.
-    return false;
-  }
-  NS_ASSERTION(numSps == 1, "More than one SPS in extradata");
-  for (uint8_t i = 0; i < numSps; i++) {
-    uint16_t length = reader.ReadU16();
-
-    if ((reader.PeekU8() & 0x1f) != H264_NAL_SPS) {
-      // Not a SPS NAL type.
-      return false;
-    }
-    const uint8_t* ptr = reader.Read(length);
-    if (!ptr) {
-      return false;
-    }
-  }
-  uint8_t numPps = reader.ReadU8();
-  if (!numPps) {
-    // No PPs.
-    return false;
-  }
-
-  for (uint32_t idx = 0; idx < numPps; idx++) {
-    uint16_t length = reader.ReadU16();
-
-    if ((reader.PeekU8() & 0x1f) != H264_NAL_PPS) {
-      // Not a PPS NAL type.
-      return false;
-    }
-    const uint8_t* ptr = reader.Read(length);
-    if (!ptr) {
-      return false;
-    }
-
-    RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer;
-    rawNAL->AppendElements(ptr, length);
-
-    RefPtr<mozilla::MediaByteBuffer> pps = DecodeNALUnit(rawNAL);
-
-    if (!pps) {
-      return false;
-    }
-
-    PPSData ppsData;
-    if (!DecodePPS(pps, aSPSes, ppsData)) {
-      return false;
-    }
-    if (ppsData.pic_parameter_set_id >= aDest.Length()) {
-      aDest.SetLength(ppsData.pic_parameter_set_id + 1);
-    }
-    aDest[ppsData.pic_parameter_set_id] = Move(ppsData);
-  }
-  return true;
-}
-
-/* static */ bool
-H264::DecodePPS(const mozilla::MediaByteBuffer* aPPS, const SPSDataSet& aSPSes,
-                PPSData& aDest)
-{
-  if (!aPPS) {
-    return false;
-  }
-
-  if (aSPSes.IsEmpty()) {
-    return false;
-  }
-
-  BitReader br(aPPS, GetBitLength(aPPS));
-
-  READUE(pic_parameter_set_id, MAX_PPS_COUNT - 1);
-  READUE(seq_parameter_set_id, MAX_SPS_COUNT - 1);
-
-  if (aDest.seq_parameter_set_id >= aSPSes.Length()) {
-    // Invalid SPS id.
-    return false;
-  }
-  const SPSData& sps = aSPSes[aDest.seq_parameter_set_id];
-
-  memcpy(aDest.scaling_matrix4x4, sps.scaling_matrix4x4,
-         sizeof(aDest.scaling_matrix4x4));
-  memcpy(aDest.scaling_matrix8x8, sps.scaling_matrix8x8,
-         sizeof(aDest.scaling_matrix8x8));
-
-  aDest.entropy_coding_mode_flag = br.ReadBit();
-  aDest.bottom_field_pic_order_in_frame_present_flag = br.ReadBit();
-  READUE(num_slice_groups_minus1, 7);
-  if (aDest.num_slice_groups_minus1 > 0) {
-    READUE(slice_group_map_type, 6);
-    switch (aDest.slice_group_map_type) {
-      case 0:
-        for (uint8_t iGroup = 0; iGroup <= aDest.num_slice_groups_minus1;
-             iGroup++) {
-          aDest.run_length_minus1[iGroup] = br.ReadUE();
-        }
-        break;
-      case 2:
-        for (uint8_t iGroup = 0; iGroup < aDest.num_slice_groups_minus1;
-             iGroup++) {
-          aDest.top_left[iGroup] = br.ReadUE();
-          aDest.bottom_right[iGroup] = br.ReadUE();
-        }
-        break;
-      case 3:
-      case 4:
-      case 5:
-        aDest.slice_group_change_direction_flag = br.ReadBit();
-        aDest.slice_group_change_rate_minus1 = br.ReadUE();
-        break;
-      case 6:
-        aDest.pic_size_in_map_units_minus1 = br.ReadUE();
-        for (uint32_t i = 0; i <= aDest.pic_size_in_map_units_minus1; i++) {
-          /* slice_group_id[ i ] identifies a slice group of the i-th slice group map
-            unit in raster scan order. The length of the slice_group_id[i] syntax
-            element is Ceil(Log2(num_slice_groups_minus1+1)) bits. The value of
-            slice_group_id[i] shall be in the range of 0 to num_slice_groups_minus1,
-            inclusive. */
-          br.ReadBits(std::ceil(std::log2(aDest.num_slice_groups_minus1 + 1)));
-        }
-        break;
-      default:
-        return false;
-    }
-  }
-  READUE(num_ref_idx_l0_default_active_minus1, 31);
-  READUE(num_ref_idx_l1_default_active_minus1, 31);
-  aDest.weighted_pred_flag = br.ReadBit();
-  aDest.weighted_bipred_idc = br.ReadBits(2);
-  READSE(pic_init_qp_minus26, -(26 + 6 * sps.bit_depth_luma_minus8), 25);
-  READSE(pic_init_qs_minus26, -26, 26);
-  READSE(chroma_qp_index_offset, -12, 12);
-  aDest.deblocking_filter_control_present_flag = br.ReadBit();
-  aDest.constrained_intra_pred_flag = br.ReadBit();
-  aDest.redundant_pic_cnt_present_flag = br.ReadBit();
-  if (br.BitsLeft()) {
-    aDest.transform_8x8_mode_flag = br.ReadBit();
-    if (br.ReadBit()) { // pic_scaling_matrix_present_flag
-      if (sps.seq_scaling_matrix_present_flag) {
-        scaling_list(br, aDest.scaling_matrix4x4[0], Default_4x4_Intra);
-        scaling_list(br, aDest.scaling_matrix4x4[1], Default_4x4_Intra,
-                     aDest.scaling_matrix4x4[0]);
-        scaling_list(br, aDest.scaling_matrix4x4[2], Default_4x4_Intra,
-                     aDest.scaling_matrix4x4[1]);
-        scaling_list(br, aDest.scaling_matrix4x4[3], Default_4x4_Inter);
-      } else {
-        scaling_list(br, aDest.scaling_matrix4x4[0], Default_4x4_Intra,
-                     Default_4x4_Intra);
-        scaling_list(br, aDest.scaling_matrix4x4[1], Default_4x4_Intra,
-                     aDest.scaling_matrix4x4[0]);
-        scaling_list(br, aDest.scaling_matrix4x4[2], Default_4x4_Intra,
-                     aDest.scaling_matrix4x4[1]);
-        scaling_list(br, aDest.scaling_matrix4x4[3], Default_4x4_Inter,
-                     Default_4x4_Inter);
-      }
-      scaling_list(br, aDest.scaling_matrix4x4[4], Default_4x4_Inter,
-                   aDest.scaling_matrix4x4[3]);
-      scaling_list(br, aDest.scaling_matrix4x4[5], Default_4x4_Inter,
-                   aDest.scaling_matrix4x4[4]);
-      if (aDest.transform_8x8_mode_flag) {
-        if (sps.seq_scaling_matrix_present_flag) {
-          scaling_list(br, aDest.scaling_matrix8x8[0], Default_8x8_Intra);
-          scaling_list(br, aDest.scaling_matrix8x8[1], Default_8x8_Inter);
-        } else {
-          scaling_list(br, aDest.scaling_matrix8x8[0], Default_8x8_Intra,
-                       Default_8x8_Intra);
-          scaling_list(br, aDest.scaling_matrix8x8[1], Default_8x8_Inter,
-                       Default_8x8_Inter);
-        }
-        if (sps.chroma_format_idc == 3) {
-          scaling_list(br, aDest.scaling_matrix8x8[2], Default_8x8_Intra,
-                       aDest.scaling_matrix8x8[0]);
-          scaling_list(br, aDest.scaling_matrix8x8[3], Default_8x8_Inter,
-                       aDest.scaling_matrix8x8[1]);
-          scaling_list(br, aDest.scaling_matrix8x8[4], Default_8x8_Intra,
-                       aDest.scaling_matrix8x8[2]);
-          scaling_list(br, aDest.scaling_matrix8x8[5], Default_8x8_Inter,
-                       aDest.scaling_matrix8x8[3]);
-        }
-      }
-    }
-    READSE(second_chroma_qp_index_offset, -12, 12);
-  }
-  return true;
-}
-
-/* static */ bool
-H264::DecodeParametersSet(const mozilla::MediaByteBuffer* aExtraData,
-                         H264ParametersSet& aDest)
-{
-  return DecodeSPSDataSetFromExtraData(aExtraData, aDest.SPSes) &&
-         DecodePPSDataSetFromExtraData(aExtraData, aDest.SPSes, aDest.PPSes);
-}
-
 /* static */ uint32_t
 H264::ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData)
 {
   uint32_t maxRefFrames = 4;
   // Retrieve video dimensions from H264 SPS NAL.
   SPSData spsdata;
   if (DecodeSPSFromExtraData(aExtraData, spsdata)) {
     // max_num_ref_frames determines the size of the sliding window
--- a/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
@@ -24,25 +24,22 @@ public:
   // Convert a sample from Annex B to AVCC.
   // an AVCC extradata must not be set.
   static bool ConvertSampleToAVCC(mozilla::MediaRawData* aSample);
   static bool ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample);
 
   // Parse an AVCC extradata and construct the Annex B sample header.
   static already_AddRefed<mozilla::MediaByteBuffer> ConvertExtraDataToAnnexB(
     const mozilla::MediaByteBuffer* aExtraData);
-  // Extract SPS and PPS NALs from aSample, aSample must be in AVCC format.
-  // If aSample already contains an extradata with an SPS, it will be returned
-  // otherwise the SPS/PPS NALs are searched in-band.
+  // Extract SPS and PPS NALs from aSample by looking into each NALs.
+  // aSample must be in AVCC format.
   static already_AddRefed<mozilla::MediaByteBuffer> ExtractExtraData(
     const mozilla::MediaRawData* aSample);
   static bool HasSPS(const mozilla::MediaRawData* aSample);
   static bool HasSPS(const mozilla::MediaByteBuffer* aExtraData);
-  static bool HasPPS(const mozilla::MediaRawData* aSample);
-  static bool HasPPS(const mozilla::MediaByteBuffer* aExtraData);
   // Returns true if format is AVCC and sample has valid extradata.
   static bool IsAVCC(const mozilla::MediaRawData* aSample);
   // Returns true if format is AnnexB.
   static bool IsAnnexB(const mozilla::MediaRawData* aSample);
   // Return true if both extradata are equal.
   static bool CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
                                const mozilla::MediaByteBuffer* aExtraData2);
 
--- a/media/libstagefright/binding/include/mp4_demuxer/H264.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h
@@ -398,212 +398,16 @@ struct SPSData
 
   bool scaling_matrix_present;
   uint8_t scaling_matrix4x4[6][16];
   uint8_t scaling_matrix8x8[6][64];
 
   SPSData();
 };
 
-struct PPSData
-{
-  /*
-    H264 decoding parameters according to ITU-T H.264 (T-REC-H.264-201402-I/en)
-   http://www.itu.int/rec/T-REC-H.264-201402-I/en
-   7.3.2.2 Picture parameter set RBSP syntax
-  */
-  /* pic_parameter_set_id identifies the picture parameter set that is referred
-      to in the slice header. The value of pic_parameter_set_id shall be in the
-      range of 0 to 255, inclusive. */
-  uint8_t pic_parameter_set_id;
-
-  /* seq_parameter_set_id refers to the active sequence parameter set. The value
-    of seq_parameter_set_id shall be in the range of 0 to 31, inclusive. */
-  uint8_t seq_parameter_set_id;
-
-  /* entropy_coding_mode_flag selects the entropy decoding method to be applied
-    for the syntax elements for which two descriptors appear in the syntax tables
-    as follows:
-    – If entropy_coding_mode_flag is equal to 0, the method specified by the
-      left descriptor in the syntax table is applied (Exp-Golomb coded, see
-      clause 9.1 or CAVLC, see clause 9.2).
-    – Otherwise (entropy_coding_mode_flag is equal to 1), the method specified
-      by the right descriptor in the syntax table is applied (CABAC, see clause
-      9.3) */
-  bool entropy_coding_mode_flag;
-
-  /* bottom_field_pic_order_in_frame_present_flag equal to 1 specifies that the
-    syntax  elements delta_pic_order_cnt_bottom (when pic_order_cnt_type is
-    equal to 0) or delta_pic_order_cnt[ 1 ] (when pic_order_cnt_type is equal to
-    1), which are related to picture order counts for the bottom field of a
-    coded frame, are present in the slice headers for coded frames as specified
-    in clause 7.3.3. bottom_field_pic_order_in_frame_present_flag equal to 0
-    specifies that the syntax elements delta_pic_order_cnt_bottom and
-    delta_pic_order_cnt[ 1 ] are not present in the slice headers.
-    Also known has pic_order_present_flag. */
-  bool bottom_field_pic_order_in_frame_present_flag;
-
-  /* num_slice_groups_minus1 plus 1 specifies the number of slice groups for a
-    picture. When num_slice_groups_minus1 is equal to 0, all slices of the
-    picture belong to the same slice group. The allowed range of
-    num_slice_groups_minus1 is specified in Annex A. */
-  uint8_t num_slice_groups_minus1;
-
-  /* slice_group_map_type specifies how the mapping of slice group map units to
-    slice groups is coded. The value of slice_group_map_type shall be in the
-    range of 0 to 6, inclusive. */
-  uint8_t slice_group_map_type;
-
-  /* run_length_minus1[i] is used to specify the number of consecutive slice
-    group map units to be assigned to the i-th slice group in raster scan order
-    of slice group map units. The value of run_length_minus1[ i ] shall be in
-    the range of 0 to PicSizeInMapUnits − 1, inclusive. */
-  uint32_t run_length_minus1[8];
-
-  /* top_left[i] and bottom_right[i] specify the top-left and bottom-right
-    corners of a rectangle, respectively. top_left[i] and bottom_right[i] are
-    slice group map unit positions in a raster scan of the picture for the slice
-    group map units. For each rectangle i, all of the following constraints
-    shall be obeyed by the values of the syntax elements top_left[i] and
-    bottom_right[i]:
-      – top_left[ i ] shall be less than or equal to bottom_right[i] and
-        bottom_right[i] shall be less than PicSizeInMapUnits.
-      – (top_left[i] % PicWidthInMbs) shall be less than or equal to the value
-        of (bottom_right[i] % PicWidthInMbs). */
-  uint32_t top_left[8];
-  uint32_t bottom_right[8];
-
-  /* slice_group_change_direction_flag is used with slice_group_map_type to
-    specify the refined map type when slice_group_map_type is 3, 4, or 5. */
-  bool slice_group_change_direction_flag;
-
-  /* slice_group_change_rate_minus1 is used to specify the variable
-    SliceGroupChangeRate. SliceGroupChangeRate specifies the multiple in number
-    of slice group map units by which the size of a slice group can change from
-    one picture to the next. The value of slice_group_change_rate_minus1 shall
-    be in the range of 0 to PicSizeInMapUnits − 1, inclusive.
-    The SliceGroupChangeRate variable is specified as follows:
-      SliceGroupChangeRate = slice_group_change_rate_minus1 + 1 */
-  uint32_t slice_group_change_rate_minus1;
-
-  /* pic_size_in_map_units_minus1 is used to specify the number of slice group
-    map units in the picture. pic_size_in_map_units_minus1 shall be equal to
-    PicSizeInMapUnits − 1. */
-  uint32_t pic_size_in_map_units_minus1;
-
-  /* num_ref_idx_l0_default_active_minus1 specifies how
-    num_ref_idx_l0_active_minus1 is inferred for P, SP, and B slices
-    with num_ref_idx_active_override_flag equal to 0. The value of
-    num_ref_idx_l0_default_active_minus1 shall be in the
-    range of 0 to 31, inclusive. */
-  uint8_t num_ref_idx_l0_default_active_minus1;
-
-  /* num_ref_idx_l1_default_active_minus1 specifies how
-    num_ref_idx_l1_active_minus1 is inferred for B slices with
-    num_ref_idx_active_override_flag equal to 0. The value of
-    num_ref_idx_l1_default_active_minus1 shall be in the range
-    of 0 to 31, inclusive. */
-  uint8_t num_ref_idx_l1_default_active_minus1;
-
-  /* weighted_pred_flag equal to 0 specifies that the default weighted
-    prediction shall be applied to P and SP slices.
-    weighted_pred_flag equal to 1 specifies that explicit weighted prediction
-    shall be applied to P and SP slices.weighted_pred_flag 1 */
-  bool weighted_pred_flag;
-
-  /* weighted_bipred_idc equal to 0 specifies that the default weighted
-     prediction shall be applied to B slices.
-     weighted_bipred_idc equal to 1 specifies that explicit weighted prediction
-     shall be applied to B slices. weighted_bipred_idc equal to 2 specifies that
-     implicit weighted prediction shall be applied to B slices. The value of
-     weighted_bipred_idc shall be in the range of 0 to 2, inclusive. */
-  uint8_t weighted_bipred_idc;
-
-  /* pic_init_qp_minus26 specifies the initial value minus 26 of SliceQP Y for
-     each slice. The initial value is modified at the slice layer when a
-     non-zero value of slice_qp_delta is decoded, and is modified further when a
-     non-zero value of mb_qp_delta is decoded at the macroblock layer.
-     The value of pic_init_qp_minus26 shall be in the range of
-     −(26 + QpBdOffset Y ) to +25, inclusive. */
-  int8_t pic_init_qp_minus26;
-
-  /* pic_init_qs_minus26 specifies the initial value minus 26 of SliceQS Y for
-    all macroblocks in SP or SI slices. The initial value is modified at the
-    slice layer when a non-zero value of slice_qs_delta is decoded.
-    The value of pic_init_qs_minus26 shall be in the range of −26 to +25,
-    inclusive. */
-  int8_t pic_init_qs_minus26;
-
-  /* chroma_qp_index_offset specifies the offset that shall be added to QP Y and
-    QS Y for addressing the table of QP C values for the Cb chroma component.
-    The value of chroma_qp_index_offset shall be in the range of −12 to +12,
-    inclusive. */
-  int8_t chroma_qp_index_offset;
-
-  /* deblocking_filter_control_present_flag equal to 1 specifies that a set of
-    syntax elements controlling the characteristics of the deblocking filter is
-    present in the slice header. deblocking_filter_control_present_flag equal to
-    0 specifies that the set of syntax elements controlling the characteristics
-    of the deblocking filter is not present in the slice headers and their
-    inferred values are in effect. */
-  bool deblocking_filter_control_present_flag;
-
-  /* constrained_intra_pred_flag equal to 0 specifies that intra prediction
-    allows usage of residual data and decoded samples of neighbouring
-    macroblocks coded using Inter macroblock prediction modes for the prediction
-    of macroblocks coded using Intra macroblock prediction modes.
-    constrained_intra_pred_flag equal to 1 specifies constrained intra
-    prediction, in which case prediction of macroblocks coded using Intra
-    macroblock prediction modes only uses residual data and decoded samples from
-    I or SI macroblock types. */
-  bool constrained_intra_pred_flag;
-
-  /* redundant_pic_cnt_present_flag equal to 0 specifies that the
-    redundant_pic_cnt syntax element is not present in slice headers, coded
-    slice data partition B NAL units, and coded slice data partition C NAL units
-    that refer (either directly or by association with a corresponding coded
-    slice data partition A NAL unit) to the picture parameter set.
-    redundant_pic_cnt_present_flag equal to 1 specifies that the
-    redundant_pic_cnt syntax element is present in all slice headers, coded
-    slice data partition B NAL units, and coded slice data partition C NAL units
-    that refer (either directly or by association with a corresponding coded
-    slice data partition A NAL unit) to the picture parameter set. */
-  bool redundant_pic_cnt_present_flag;
-
-  /* transform_8x8_mode_flag equal to 1 specifies that the 8x8 transform
-    decoding process may be in use (see clause 8.5).
-    transform_8x8_mode_flag equal to 0 specifies that the 8x8 transform decoding
-    process is not in use. When transform_8x8_mode_flag is not present, it shall
-    be inferred to be 0. */
-  bool transform_8x8_mode_flag;
-
-  /* second_chroma_qp_index_offset specifies the offset that shall be added to
-    QP Y and QS Y for addressing the table of QP C values for the Cr chroma
-    component.
-    The value of second_chroma_qp_index_offset shall be in the range of
-    −12 to +12, inclusive.
-    When second_chroma_qp_index_offset is not present, it shall be inferred to
-    be equal to chroma_qp_index_offset. */
-  int8_t second_chroma_qp_index_offset;
-
-  uint8_t scaling_matrix4x4[6][16];
-  uint8_t scaling_matrix8x8[6][64];
-
-  PPSData();
-};
-
-typedef AutoTArray<SPSData, MAX_SPS_COUNT> SPSDataSet;
-typedef AutoTArray<PPSData, MAX_PPS_COUNT> PPSDataSet;
-
-struct H264ParametersSet
-{
-  SPSDataSet SPSes;
-  PPSDataSet PPSes;
-};
-
 class H264
 {
 public:
   /* Extract RAW BYTE SEQUENCE PAYLOAD from NAL content.
      Returns nullptr if invalid content.
      This is compliant to ITU H.264 7.3.1 Syntax in tabular form NAL unit syntax
    */
   static already_AddRefed<mozilla::MediaByteBuffer> DecodeNALUnit(
@@ -611,56 +415,35 @@ public:
 
   // Ensure that SPS data makes sense, Return true if SPS data was, and false
   // otherwise. If false, then content will be adjusted accordingly.
   static bool EnsureSPSIsSane(SPSData& aSPS);
 
   static bool DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
                                      SPSData& aDest);
 
-  static bool DecodeParametersSet(const mozilla::MediaByteBuffer* aExtraData,
-                                  H264ParametersSet& aDest);
-
   // If the given aExtraData is valid, return the aExtraData.max_num_ref_frames
   // clamped to be in the range of [4, 16]; otherwise return 4.
   static uint32_t ComputeMaxRefFrames(
     const mozilla::MediaByteBuffer* aExtraData);
 
   enum class FrameType
   {
     I_FRAME,
     OTHER,
     INVALID,
   };
 
   // Returns the frame type. Returns I_FRAME if the sample is an IDR
   // (Instantaneous Decoding Refresh) Picture.
   static FrameType GetFrameType(const mozilla::MediaRawData* aSample);
 
-  // ZigZag index taBles.
-  // Some hardware requires the tables to be in zigzag order.
-  // Use ZZ_SCAN table for the scaling_matrix4x4.
-  // Use ZZ_SCAN8 table for the scaling_matrix8x8.
-  // e.g. dest_scaling_matrix4x4[i,j] = scaling_matrix4x4[ZZ_SCAN(i,j)]
-  static const uint8_t ZZ_SCAN[16];
-  static const uint8_t ZZ_SCAN8[64];
-
 private:
-  static bool DecodeSPSDataSetFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
-                                            SPSDataSet& aDest);
-
-  static bool DecodePPSDataSetFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
-                                            const SPSDataSet& aPS,
-                                            PPSDataSet& aDest);
-
   /* Decode SPS NAL RBSP and fill SPSData structure */
   static bool DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest);
-  /* Decode PPS NAL RBSP and fill PPSData structure */
-  static bool DecodePPS(const mozilla::MediaByteBuffer* aPPS,
-                        const SPSDataSet& aSPSs, PPSData& aDest);
   static bool vui_parameters(BitReader& aBr, SPSData& aDest);
   // Read HRD parameters, all data is ignored.
   static void hrd_parameters(BitReader& aBr);
 };
 
 } // namespace mp4_demuxer
 
 #endif // MP4_DEMUXER_H264_H_
--- a/mfbt/AlreadyAddRefed.h
+++ b/mfbt/AlreadyAddRefed.h
@@ -62,20 +62,17 @@ struct MOZ_MUST_USE_TYPE MOZ_NON_AUTOABL
    * this behavior through the default constructor.
    *
    * We can revert to simply explicit (T*) and implicit (decltype(nullptr)) when
    * nullptr no longer needs to be emulated to support the ancient b2g compiler.
    * (The () overload could also be removed, if desired, if we changed callers.)
    */
   already_AddRefed() : mRawPtr(nullptr) {}
 
-  // The return and argument types here are arbitrarily selected so no
-  // corresponding member function exists.
-  typedef void (already_AddRefed::* MatchNullptr)(double, float);
-  MOZ_IMPLICIT already_AddRefed(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
+  MOZ_IMPLICIT already_AddRefed(decltype(nullptr)) : mRawPtr(nullptr) {}
 
   explicit already_AddRefed(T* aRawPtr) : mRawPtr(aRawPtr) {}
 
   // Disallow copy constructor and copy assignment operator: move semantics used instead.
   already_AddRefed(const already_AddRefed<T>& aOther) = delete;
   already_AddRefed<T>& operator=(const already_AddRefed<T>& aOther) = delete;
 
   already_AddRefed(already_AddRefed<T>&& aOther) : mRawPtr(aOther.take()) {}
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -386,16 +386,17 @@ bool PACResolve(const nsCString &aHostNa
   return GetRunning()->ResolveAddress(aHostName, aNetAddr, aTimeout);
 }
 
 ProxyAutoConfig::ProxyAutoConfig()
   : mJSContext(nullptr)
   , mJSNeedsSetup(false)
   , mShutdown(false)
   , mIncludePath(false)
+  , mExtraHeapSize(0)
 {
   MOZ_COUNT_CTOR(ProxyAutoConfig);
 }
 
 bool
 ProxyAutoConfig::ResolveAddress(const nsCString &aHostName,
                                 NetAddr *aNetAddr,
                                 unsigned int aTimeout)
@@ -546,19 +547,19 @@ static const JSFunctionSpec PACGlobalFun
   JS_FS_END
 };
 
 // JSContextWrapper is a c++ object that manages the context for the JS engine
 // used on the PAC thread. It is initialized and destroyed on the PAC thread.
 class JSContextWrapper
 {
  public:
-  static JSContextWrapper *Create()
+  static JSContextWrapper *Create(uint32_t aExtraHeapSize)
   {
-    JSContext* cx = JS_NewContext(sContextHeapSize);
+    JSContext* cx = JS_NewContext(sContextHeapSize + aExtraHeapSize);
     if (NS_WARN_IF(!cx))
       return nullptr;
 
     JSContextWrapper *entry = new JSContextWrapper(cx);
     if (NS_FAILED(entry->Init())) {
       delete entry;
       return nullptr;
     }
@@ -593,17 +594,17 @@ class JSContextWrapper
   }
 
   bool IsOK()
   {
     return mOK;
   }
 
 private:
-  static const unsigned sContextHeapSize = 4 << 20; // 4 MB
+  static const uint32_t sContextHeapSize = 4 << 20; // 4 MB
 
   JSContext *mContext;
   JS::PersistentRooted<JSObject*> mGlobal;
   bool      mOK;
 
   static const JSClass sGlobalClass;
 
   explicit JSContextWrapper(JSContext* cx)
@@ -671,22 +672,24 @@ void
 ProxyAutoConfig::SetThreadLocalIndex(uint32_t index)
 {
   sRunningIndex = index;
 }
 
 nsresult
 ProxyAutoConfig::Init(const nsCString &aPACURI,
                       const nsCString &aPACScript,
-                      bool aIncludePath)
+                      bool aIncludePath,
+                      uint32_t aExtraHeapSize)
 {
   mPACURI = aPACURI;
   mPACScript = sPacUtils;
   mPACScript.Append(aPACScript);
   mIncludePath = aIncludePath;
+  mExtraHeapSize = aExtraHeapSize;
 
   if (!GetRunning())
     return SetupJS();
 
   mJSNeedsSetup = true;
   return NS_OK;
 }
 
@@ -699,17 +702,17 @@ ProxyAutoConfig::SetupJS()
   delete mJSContext;
   mJSContext = nullptr;
 
   if (mPACScript.IsEmpty())
     return NS_ERROR_FAILURE;
 
   NS_GetCurrentThread()->SetCanInvokeJS(true);
 
-  mJSContext = JSContextWrapper::Create();
+  mJSContext = JSContextWrapper::Create(mExtraHeapSize);
   if (!mJSContext)
     return NS_ERROR_FAILURE;
 
   JSContext* cx = mJSContext->Context();
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, mJSContext->Global());
   AutoPACErrorReporter aper(cx);
 
--- a/netwerk/base/ProxyAutoConfig.h
+++ b/netwerk/base/ProxyAutoConfig.h
@@ -26,17 +26,18 @@ union NetAddr;
 
 class ProxyAutoConfig  {
 public:
   ProxyAutoConfig();
   ~ProxyAutoConfig();
 
   nsresult Init(const nsCString &aPACURI,
                 const nsCString &aPACScript,
-                bool aIncludePath);
+                bool aIncludePath,
+                uint32_t aExtraHeapSize);
   void     SetThreadLocalIndex(uint32_t index);
   void     Shutdown();
   void     GC();
   bool     MyIPAddress(const JS::CallArgs &aArgs);
   bool     ResolveAddress(const nsCString &aHostName,
                           NetAddr *aNetAddr, unsigned int aTimeout);
 
   /**
@@ -89,16 +90,17 @@ private:
                           const JS::CallArgs &aArgs, bool* aResult);
 
   JSContextWrapper *mJSContext;
   bool              mJSNeedsSetup;
   bool              mShutdown;
   nsCString         mPACScript;
   nsCString         mPACURI;
   bool              mIncludePath;
+  uint32_t          mExtraHeapSize;
   nsCString         mRunningHost;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif  // ProxyAutoConfig_h__
--- a/netwerk/base/nsIParentRedirectingChannel.idl
+++ b/netwerk/base/nsIParentRedirectingChannel.idl
@@ -3,16 +3,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIParentChannel.idl"
 
 interface nsITabParent;
 interface nsIChannel;
 interface nsIAsyncVerifyRedirectCallback;
 
+[builtinclass, uuid(01987690-48cf-45de-bae3-e143c2adc2a8)]
+interface nsIAsyncVerifyRedirectReadyCallback : nsISupports
+{
+  /**
+   * Asynchronous callback when redirected channel finishes the preparation for
+   * completing the verification procedure.
+   *
+   * @param result
+   *    SUCCEEDED if preparation for redirection verification succceed.
+   *    If FAILED the redirection must be aborted.
+   */
+  void readyToVerify(in nsresult result);
+};
+
 /**
  * Implemented by chrome side of IPC protocols that support redirect responses.
  */
 
 [scriptable, uuid(3ed1d288-5324-46ee-8a98-33ac37d1080b)]
 interface nsIParentRedirectingChannel : nsIParentChannel
 {
   /**
@@ -29,16 +43,27 @@ interface nsIParentRedirectingChannel : 
    *    nsIChannelEventSink defines it
    */
   void startRedirect(in uint32_t newChannelId,
                      in nsIChannel newChannel,
                      in uint32_t redirectFlags,
                      in nsIAsyncVerifyRedirectCallback callback);
 
   /**
+   * Called to new channel when the original channel got Redirect2Verify
+   * response from child. Callback will be invoked when the new channel
+   * finishes the preparation for Redirect2Verify and can be called immediately.
+   *
+   * @param callback
+   *    redirect ready callback, will be called when redirect verification
+   *    procedure can proceed.
+   */
+  void continueVerification(in nsIAsyncVerifyRedirectReadyCallback callback);
+
+  /**
    * Called after we are done with redirecting process and we know if to
    * redirect or not.  Forward the redirect result to the child process.  From
    * that moment the nsIParentChannel implementation expects it will be
    * forwarded all notifications from the 'real' channel.
    *
    * Primarilly used by HttpChannelParentListener::OnRedirectResult and kept
    * as mActiveChannel and mRedirectChannel in that class.
    */
--- a/netwerk/base/nsPACMan.cpp
+++ b/netwerk/base/nsPACMan.cpp
@@ -45,16 +45,42 @@ HttpRequestSucceeded(nsIStreamLoader *lo
   if (httpChannel) {
     // failsafe
     Unused << httpChannel->GetRequestSucceeded(&result);
   }
 
   return result;
 }
 
+// Read preference setting of extra JavaScript context heap size.
+// PrefService tends to be run on main thread, where ProxyAutoConfig runs on
+// ProxyResolution thread, so it's read here and passed to ProxyAutoConfig.
+static uint32_t
+GetExtraJSContextHeapSize()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  static int32_t extraSize = -1;
+
+  if (extraSize < 0) {
+    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    int32_t value;
+
+    if (prefs && NS_SUCCEEDED(prefs->GetIntPref(
+        "network.proxy.autoconfig_extra_jscontext_heap_size", &value))) {
+      LOG(("autoconfig_extra_jscontext_heap_size: %d\n", value));
+
+      extraSize = value;
+    }
+  }
+
+  return extraSize < 0 ? 0 : extraSize;
+}
+
+
 //-----------------------------------------------------------------------------
 
 // The ExecuteCallback runnable is triggered by
 // nsPACManCallback::OnQueryComplete on the Main thread when its completion is
 // discovered on the pac thread
 
 class ExecuteCallback final : public Runnable
 {
@@ -175,46 +201,52 @@ class ExecutePACThreadAction final : pub
 {
 public:
   // by default we just process the queue
   explicit ExecutePACThreadAction(nsPACMan *aPACMan)
     : mPACMan(aPACMan)
     , mCancel(false)
     , mCancelStatus(NS_OK)
     , mSetupPAC(false)
+    , mExtraHeapSize(0)
   { }
 
   void CancelQueue (nsresult status)
   {
     mCancel = true;
     mCancelStatus = status;
   }
 
-  void SetupPAC (const char *text, uint32_t datalen, nsCString &pacURI)
+  void SetupPAC (const char *text,
+                 uint32_t datalen,
+                 nsCString &pacURI,
+                 uint32_t extraHeapSize)
   {
     mSetupPAC = true;
     mSetupPACData.Assign(text, datalen);
     mSetupPACURI = pacURI;
+    mExtraHeapSize = extraHeapSize;
   }
 
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
     if (mCancel) {
       mPACMan->CancelPendingQ(mCancelStatus);
       mCancel = false;
       return NS_OK;
     }
 
     if (mSetupPAC) {
       mSetupPAC = false;
 
       mPACMan->mPAC.Init(mSetupPACURI,
                          mSetupPACData,
-                         mPACMan->mIncludePath);
+                         mPACMan->mIncludePath,
+                         mExtraHeapSize);
 
       RefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan);
       NS_DispatchToMainThread(runnable);
       return NS_OK;
     }
 
     mPACMan->ProcessPendingQ();
     return NS_OK;
@@ -222,16 +254,17 @@ public:
 
 private:
   RefPtr<nsPACMan> mPACMan;
 
   bool      mCancel;
   nsresult  mCancelStatus;
 
   bool                 mSetupPAC;
+  uint32_t             mExtraHeapSize;
   nsCString            mSetupPACData;
   nsCString            mSetupPACURI;
 };
 
 //-----------------------------------------------------------------------------
 
 PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri,
                                  nsPACManCallback *callback,
@@ -666,17 +699,17 @@ nsPACMan::OnStreamComplete(nsIStreamLoad
 
     // we have succeeded in loading the pac file using a bunch of interfaces that
     // are main thread only, unfortunately we have to initialize the instance of
     // the PAC evaluator (NS_PROXYAUTOCONFIG_CONTRACTID) on the pac thread, because
     // that is where it will be used.
 
     RefPtr<ExecutePACThreadAction> pending =
       new ExecutePACThreadAction(this);
-    pending->SetupPAC(text, dataLen, pacURI);
+    pending->SetupPAC(text, dataLen, pacURI, GetExtraJSContextHeapSize());
     if (mPACThread)
       mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL);
 
     LOG(("OnStreamComplete: process the PAC contents\n"));
 
     // Even if the PAC file could not be parsed, we did succeed in loading the
     // data for it.
     mLoadFailureCount = 0;
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -632,16 +632,28 @@
 #define NS_THROTTLEQUEUE_CID                            \
 { /* 4c39159c-cd90-4dd3-97a7-06af5e6d84c4 */            \
     0x4c39159c,                                         \
     0xcd90,                                             \
     0x4dd3,                                             \
     {0x97, 0xa7, 0x06, 0xaf, 0x5e, 0x6d, 0x84, 0xc4}    \
 }
 
+// Background channel registrar used for pairing HttpChannelParent
+// and its background channel
+#define NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID \
+    "@mozilla.org/network/background-channel-registrar;1"
+#define NS_BACKGROUNDCHANNELREGISTRAR_CID \
+{ /* 6907788a-17cc-4c2a-a7c5-59ad2d9cc079 */          \
+    0x6907788a,                                       \
+    0x17cc,                                           \
+    0x4c2a,                                           \
+    { 0xa7, 0xc5, 0x59, 0xad, 0x2d, 0x9c, 0xc0, 0x79} \
+}
+
 /******************************************************************************
  * netwerk/protocol/ftp/ classes
  */
 
 #define NS_FTPPROTOCOLHANDLER_CID \
 { /* 25029490-F132-11d2-9588-00805F369F95 */         \
     0x25029490,                                      \
     0xf132,                                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -262,29 +262,31 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFt
 #undef LOG_ENABLED
 #include "nsHttpAuthManager.h"
 #include "nsHttpChannelAuthProvider.h"
 #include "nsHttpBasicAuth.h"
 #include "nsHttpDigestAuth.h"
 #include "nsHttpNTLMAuth.h"
 #include "nsHttpActivityDistributor.h"
 #include "ThrottleQueue.h"
+#include "BackgroundChannelRegistrar.h"
 #undef LOG
 #undef LOG_ENABLED
 namespace mozilla {
 namespace net {
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNTLMAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpHandler, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpsHandler, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpAuthManager, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpChannelAuthProvider)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(ThrottleQueue)
+NS_GENERIC_FACTORY_CONSTRUCTOR(BackgroundChannelRegistrar)
 } // namespace net
 } // namespace mozilla
 #endif // !NECKO_PROTOCOL_http
 
 #include "mozilla/net/Dashboard.h"
 namespace mozilla {
 namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
@@ -800,16 +802,17 @@ NS_DEFINE_NAMED_CID(NS_HTTPPROTOCOLHANDL
 NS_DEFINE_NAMED_CID(NS_HTTPSPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPBASICAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPDIGESTAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPNTLMAUTH_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPCHANNELAUTHPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID);
 NS_DEFINE_NAMED_CID(NS_THROTTLEQUEUE_CID);
+NS_DEFINE_NAMED_CID(NS_BACKGROUNDCHANNELREGISTRAR_CID);
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
 NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
 #endif
 #ifdef NECKO_PROTOCOL_res
 NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_EXTENSIONPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID);
@@ -951,16 +954,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_HTTPSPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpsHandlerConstructor },
     { &kNS_HTTPBASICAUTH_CID, false, nullptr, mozilla::net::nsHttpBasicAuthConstructor },
     { &kNS_HTTPDIGESTAUTH_CID, false, nullptr, mozilla::net::nsHttpDigestAuthConstructor },
     { &kNS_HTTPNTLMAUTH_CID, false, nullptr, mozilla::net::nsHttpNTLMAuthConstructor },
     { &kNS_HTTPAUTHMANAGER_CID, false, nullptr, mozilla::net::nsHttpAuthManagerConstructor },
     { &kNS_HTTPCHANNELAUTHPROVIDER_CID, false, nullptr, mozilla::net::nsHttpChannelAuthProviderConstructor },
     { &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor },
     { &kNS_THROTTLEQUEUE_CID, false, nullptr, mozilla::net::ThrottleQueueConstructor },
+    { &kNS_BACKGROUNDCHANNELREGISTRAR_CID, false, nullptr, mozilla::net::BackgroundChannelRegistrarConstructor },
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
     { &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor },
 #endif
 #ifdef NECKO_PROTOCOL_res
     { &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor },
     { &kNS_EXTENSIONPROTOCOLHANDLER_CID, false, nullptr, mozilla::ExtensionProtocolHandlerConstructor },
     { &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLConstructor },
@@ -1112,16 +1116,17 @@ static const mozilla::Module::ContractID
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "https", &kNS_HTTPSPROTOCOLHANDLER_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "basic", &kNS_HTTPBASICAUTH_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "digest", &kNS_HTTPDIGESTAUTH_CID },
     { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "ntlm", &kNS_HTTPNTLMAUTH_CID },
     { NS_HTTPAUTHMANAGER_CONTRACTID, &kNS_HTTPAUTHMANAGER_CID },
     { NS_HTTPCHANNELAUTHPROVIDER_CONTRACTID, &kNS_HTTPCHANNELAUTHPROVIDER_CID },
     { NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID },
     { NS_THROTTLEQUEUE_CONTRACTID, &kNS_THROTTLEQUEUE_CID },
+    { NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID, &kNS_BACKGROUNDCHANNELREGISTRAR_CID },
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID },
 #endif
 #ifdef NECKO_PROTOCOL_res
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource", &kNS_RESPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-extension", &kNS_EXTENSIONPROTOCOLHANDLER_CID },
 #endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
@@ -0,0 +1,97 @@
+/* -*- 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 "BackgroundChannelRegistrar.h"
+
+#include "HttpBackgroundChannelParent.h"
+#include "HttpChannelParent.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(BackgroundChannelRegistrar, nsIBackgroundChannelRegistrar)
+
+BackgroundChannelRegistrar::BackgroundChannelRegistrar()
+{
+  // BackgroundChannelRegistrar is a main-thread-only object.
+  // All the operations should be run on main thread.
+  // It should be used on chrome process only.
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+BackgroundChannelRegistrar::~BackgroundChannelRegistrar()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+BackgroundChannelRegistrar::NotifyChannelLinked(
+  HttpChannelParent* aChannelParent,
+  HttpBackgroundChannelParent* aBgParent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aChannelParent);
+  MOZ_ASSERT(aBgParent);
+
+  aBgParent->LinkToChannel(aChannelParent);
+  aChannelParent->OnBackgroundParentReady(aBgParent);
+}
+
+// nsIBackgroundChannelRegistrar
+void
+BackgroundChannelRegistrar::DeleteChannel(uint64_t aKey)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mChannels.Remove(aKey);
+  mBgChannels.Remove(aKey);
+}
+
+void
+BackgroundChannelRegistrar::LinkHttpChannel(
+  uint64_t aKey,
+  HttpChannelParent* aChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aChannel);
+
+  RefPtr<HttpBackgroundChannelParent> bgParent;
+  bool found = mBgChannels.Remove(aKey, getter_AddRefs(bgParent));
+
+  if (!found) {
+    mChannels.Put(aKey, aChannel);
+    return;
+  }
+
+  MOZ_ASSERT(bgParent);
+  NotifyChannelLinked(aChannel, bgParent);
+}
+
+void
+BackgroundChannelRegistrar::LinkBackgroundChannel(
+  uint64_t aKey,
+  HttpBackgroundChannelParent* aBgChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aBgChannel);
+
+  RefPtr<HttpChannelParent> parent;
+  bool found = mChannels.Remove(aKey, getter_AddRefs(parent));
+
+  if (!found) {
+    mBgChannels.Put(aKey, aBgChannel);
+    return;
+  }
+
+  MOZ_ASSERT(parent);
+  NotifyChannelLinked(parent, aBgChannel);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.h
@@ -0,0 +1,52 @@
+/* -*- 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_net_BackgroundChannelRegistrar_h__
+#define mozilla_net_BackgroundChannelRegistrar_h__
+
+#include "nsIBackgroundChannelRegistrar.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class HttpBackgroundChannelParent;
+class HttpChannelParent;
+
+class BackgroundChannelRegistrar final : public nsIBackgroundChannelRegistrar
+{
+  typedef nsRefPtrHashtable<nsUint64HashKey, HttpChannelParent>
+          ChannelHashtable;
+  typedef nsRefPtrHashtable<nsUint64HashKey, HttpBackgroundChannelParent>
+          BackgroundChannelHashtable;
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIBACKGROUNDCHANNELREGISTRAR
+
+  explicit BackgroundChannelRegistrar();
+
+private:
+  virtual ~BackgroundChannelRegistrar();
+
+  // A helper function for BackgroundChannelRegistrar itself to callback
+  // HttpChannelParent and HttpBackgroundChannelParent when both objects are
+  // ready. aChannelParent and aBgParent is the pair of HttpChannelParent and
+  // HttpBackgroundChannelParent that should be linked together.
+  void NotifyChannelLinked(HttpChannelParent* aChannelParent,
+                           HttpBackgroundChannelParent* aBgParent);
+
+  // Store unlinked HttpChannelParent objects.
+  ChannelHashtable mChannels;
+
+  // Store unlinked HttpBackgroundChannelParent objects.
+  BackgroundChannelHashtable mBgChannels;
+
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_BackgroundChannelRegistrar_h__
--- a/netwerk/protocol/http/HSTSPrimerListener.cpp
+++ b/netwerk/protocol/http/HSTSPrimerListener.cpp
@@ -1,13 +1,14 @@
 /* -*- 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 "HttpLog.h"
 
 #include "nsHttp.h"
 
 #include "HSTSPrimerListener.h"
 #include "nsIHstsPrimingCallback.h"
 #include "nsIPrincipal.h"
 #include "nsSecurityHeaderParser.h"
 #include "nsISiteSecurityService.h"
--- a/netwerk/protocol/http/HSTSPrimerListener.h
+++ b/netwerk/protocol/http/HSTSPrimerListener.h
@@ -7,16 +7,17 @@
 #ifndef HSTSPrimingListener_h__
 #define HSTSPrimingListener_h__
 
 #include "nsCOMPtr.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIStreamListener.h"
 #include "nsIThreadRetargetableStreamListener.h"
+#include "nsITimer.h"
 
 #include "mozilla/Attributes.h"
 
 class nsIPrincipal;
 class nsINetworkInterceptController;
 class nsIHstsPrimingCallback;
 
 namespace mozilla {
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -992,17 +992,17 @@ bool
 Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0)
 {
   // This is annoying, but at least it is O(1)
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 
 #ifndef DEBUG
   // Only do the real verification in debug builds
   return true;
-#endif
+#else //DEBUG
 
   if (!aStream)
     return true;
 
   uint32_t test = 0;
 
   do {
     if (aStream->StreamID() == kDeadStreamID)
@@ -1038,16 +1038,17 @@ Http2Session::VerifyStream(Http2Stream *
 
   LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X "
        "optionalID=0x%X trans=%p test=%d\n",
        this, aStream, aStream->StreamID(),
        aOptionalID, aStream->Transaction(), test));
 
   MOZ_ASSERT(false, "VerifyStream");
   return false;
+#endif //DEBUG
 }
 
 void
 Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
                             errorType aResetCode)
 {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   LOG3(("Http2Session::CleanupStream %p %p 0x%X %" PRIX32 "\n",
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include "HttpBackgroundChannelChild.h"
+
+#include "HttpChannelChild.h"
+#include "MainThreadUtils.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Unused.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+
+using mozilla::ipc::BackgroundChild;
+using mozilla::ipc::IPCResult;
+
+namespace mozilla {
+namespace net {
+
+// Callbacks for PBackgroundChild creation
+class BackgroundChannelCreateCallback final
+  : public nsIIPCBackgroundChildCreateCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+
+  explicit BackgroundChannelCreateCallback(HttpBackgroundChannelChild* aBgChild)
+    : mBgChild(aBgChild)
+  {
+    MOZ_ASSERT(aBgChild);
+  }
+
+private:
+  virtual ~BackgroundChannelCreateCallback() { }
+
+  RefPtr<HttpBackgroundChannelChild> mBgChild;
+};
+
+NS_IMPL_ISUPPORTS(BackgroundChannelCreateCallback,
+                  nsIIPCBackgroundChildCreateCallback)
+
+void
+BackgroundChannelCreateCallback::ActorCreated(PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mBgChild);
+
+  if (!mBgChild->mChannelChild) {
+    // HttpChannelChild is closed during PBackground creation,
+    // abort the rest of steps.
+    return;
+  }
+
+  const uint64_t channelId = mBgChild->mChannelChild->ChannelId();
+  if (!aActor->SendPHttpBackgroundChannelConstructor(mBgChild,
+                                                     channelId)) {
+    ActorFailed();
+    return;
+  }
+
+  // hold extra reference for IPDL
+  RefPtr<HttpBackgroundChannelChild> child = mBgChild;
+  Unused << child.forget().take();
+}
+
+void
+BackgroundChannelCreateCallback::ActorFailed()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBgChild);
+
+  mBgChild->OnBackgroundChannelCreationFailed();
+}
+
+// HttpBackgroundChannelChild
+HttpBackgroundChannelChild::HttpBackgroundChannelChild()
+{
+}
+
+HttpBackgroundChannelChild::~HttpBackgroundChannelChild()
+{
+}
+
+nsresult
+HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild)
+{
+  LOG(("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p channelId=%"
+       PRIu64 "]\n", this, aChannelChild, aChannelChild->ChannelId()));
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_ARG(aChannelChild);
+
+  mChannelChild = aChannelChild;
+
+  if (NS_WARN_IF(!CreateBackgroundChannel())) {
+    mChannelChild = nullptr;
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void
+HttpBackgroundChannelChild::OnChannelClosed()
+{
+  LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // HttpChannelChild is not going to handle any incoming message.
+  mChannelChild = nullptr;
+}
+
+void
+HttpBackgroundChannelChild::OnStartRequestReceived()
+{
+  LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mChannelChild);
+  MOZ_ASSERT(!mStartReceived); // Should only be called once.
+
+  mStartReceived = true;
+
+  nsTArray<nsCOMPtr<nsIRunnable>> runnables;
+  runnables.SwapElements(mQueuedRunnables);
+
+  for (auto event : runnables) {
+    // Note: these runnables call Recv* methods on HttpBackgroundChannelChild
+    // but not the Process* methods on HttpChannelChild.
+    event->Run();
+  }
+
+  // Ensure no new message is enqueued.
+  MOZ_ASSERT(mQueuedRunnables.IsEmpty());
+}
+
+void
+HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed()
+{
+  LOG(("HttpBackgroundChannelChild::OnBackgroundChannelCreationFailed"
+       " [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mChannelChild) {
+    RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+    channelChild->FailedAsyncOpen(NS_ERROR_UNEXPECTED);
+  }
+}
+
+bool
+HttpBackgroundChannelChild::CreateBackgroundChannel()
+{
+  LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<BackgroundChannelCreateCallback> callback =
+    new BackgroundChannelCreateCallback(this);
+
+  return BackgroundChild::GetOrCreateForCurrentThread(callback);
+}
+
+bool
+HttpBackgroundChannelChild::IsWaitingOnStartRequest()
+{
+  // Need to wait for OnStartRequest if it is sent by
+  // parent process but not received by content process.
+  return (mStartSent && !mStartReceived);
+}
+
+// PHttpBackgroundChannelChild
+IPCResult
+HttpBackgroundChannelChild::RecvOnStartRequestSent()
+{
+  LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mStartSent); // Should only receive this message once.
+
+  mStartSent = true;
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvOnTransportAndData(
+                                               const nsresult& aChannelStatus,
+                                               const nsresult& aTransportStatus,
+                                               const uint64_t& aOffset,
+                                               const uint32_t& aCount,
+                                               const nsCString& aData)
+{
+  LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
+         "]\n", aOffset, aCount));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
+                        const uint32_t, const nsCString>(
+        this, &HttpBackgroundChannelChild::RecvOnTransportAndData,
+        aChannelStatus, aTransportStatus, aOffset,
+        aCount, aData));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessOnTransportAndData(aChannelStatus,
+                                           aTransportStatus,
+                                           aOffset,
+                                           aCount,
+                                           aData);
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvOnStopRequest(
+                                            const nsresult& aChannelStatus,
+                                            const ResourceTimingStruct& aTiming)
+{
+  LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
+         static_cast<uint32_t>(aChannelStatus)));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod<const nsresult, const ResourceTimingStruct>(
+        this, &HttpBackgroundChannelChild::RecvOnStopRequest,
+        aChannelStatus, aTiming));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming);
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvOnProgress(const int64_t& aProgress,
+                                           const int64_t& aProgressMax)
+{
+  LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%"
+       PRId64 " max=%" PRId64 "]\n", this, aProgress, aProgressMax));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest [progress=%" PRId64 " max=%"
+         PRId64 "]\n", aProgress, aProgressMax));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod<const int64_t, const int64_t>(
+        this, &HttpBackgroundChannelChild::RecvOnProgress,
+        aProgress, aProgressMax));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessOnProgress(aProgress, aProgressMax);
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus)
+{
+  LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%"
+       PRIx32 "]\n", this, static_cast<uint32_t>(aStatus)));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest [status=%" PRIx32 "]\n",
+         static_cast<uint32_t>(aStatus)));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod<const nsresult>(
+        this, &HttpBackgroundChannelChild::RecvOnStatus, aStatus));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessOnStatus(aStatus);
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvFlushedForDiversion()
+{
+  LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest\n"));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod(this, &HttpBackgroundChannelChild::RecvFlushedForDiversion));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessFlushedForDiversion();
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvDivertMessages()
+{
+  LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  if (IsWaitingOnStartRequest()) {
+    LOG(("  > pending until OnStartRequest\n"));
+
+    mQueuedRunnables.AppendElement(
+      NewRunnableMethod(this, &HttpBackgroundChannelChild::RecvDivertMessages));
+
+    return IPC_OK();
+  }
+
+  mChannelChild->ProcessDivertMessages();
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled()
+{
+  LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingProtectionDisabled [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  // NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
+  // It this be handled as soon as possible
+  mChannelChild->ProcessNotifyTrackingProtectionDisabled();
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvNotifyTrackingResource()
+{
+  LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  // NotifyTrackingResource has no order dependency to OnStartRequest.
+  // It this be handled as soon as possible
+  mChannelChild->ProcessNotifyTrackingResource();
+
+  return IPC_OK();
+}
+
+IPCResult
+HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& info)
+{
+  LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  // SetClassifierMatchedInfo has no order dependency to OnStartRequest.
+  // It this be handled as soon as possible
+  mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(), info.prefix());
+
+  return IPC_OK();
+}
+
+void
+HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Ensure all IPC messages received before ActorDestroy can be
+  // handled correctly. If there is any pending IPC message, destroyed
+  // mChannelChild until those messages are flushed.
+  if (!mQueuedRunnables.IsEmpty()) {
+    LOG(("  > pending until queued messages are flushed\n"));
+    RefPtr<HttpBackgroundChannelChild> self = this;
+    mQueuedRunnables.AppendElement(
+      NS_NewRunnableFunction([self]() {
+        MOZ_ASSERT(NS_IsMainThread());
+        RefPtr<HttpChannelChild> channelChild = self->mChannelChild.forget();
+
+        if (channelChild) {
+          channelChild->OnBackgroundChildDestroyed();
+        }
+      }));
+    return;
+  }
+
+  if (mChannelChild) {
+    RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+
+    channelChild->OnBackgroundChildDestroyed();
+  }
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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_net_HttpBackgroundChannelChild_h
+#define mozilla_net_HttpBackgroundChannelChild_h
+
+#include "mozilla/net/PHttpBackgroundChannelChild.h"
+#include "nsIRunnable.h"
+#include "nsTArray.h"
+
+using mozilla::ipc::IPCResult;
+using mozilla::dom::ClassifierInfo;
+
+namespace mozilla {
+namespace net {
+
+class HttpChannelChild;
+
+class HttpBackgroundChannelChild final : public PHttpBackgroundChannelChild
+{
+  friend class BackgroundChannelCreateCallback;
+public:
+  explicit HttpBackgroundChannelChild();
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelChild)
+
+  // Associate this background channel with a HttpChannelChild and
+  // initiate the createion of the PBackground IPC channel.
+  nsresult Init(HttpChannelChild* aChannelChild);
+
+  // Callback while the associated HttpChannelChild is not going to
+  // handle any incoming messages over background channel.
+  void OnChannelClosed();
+
+  // Callback when OnStartRequest is received and handled by HttpChannelChild.
+  // Enqueued messages in background channel will be flushed.
+  void OnStartRequestReceived();
+
+  // Callback while failed to create PBackground IPC channel.
+  void OnBackgroundChannelCreationFailed();
+
+protected:
+  IPCResult RecvOnTransportAndData(const nsresult& aChannelStatus,
+                                   const nsresult& aTransportStatus,
+                                   const uint64_t& aOffset,
+                                   const uint32_t& aCount,
+                                   const nsCString& aData) override;
+
+  IPCResult RecvOnStopRequest(const nsresult& aChannelStatus,
+                              const ResourceTimingStruct& aTiming) override;
+
+  IPCResult RecvOnProgress(const int64_t& aProgress,
+                           const int64_t& aProgressMax) override;
+
+  IPCResult RecvOnStatus(const nsresult& aStatus) override;
+
+  IPCResult RecvFlushedForDiversion() override;
+
+  IPCResult RecvDivertMessages() override;
+
+  IPCResult RecvOnStartRequestSent() override;
+
+  IPCResult RecvNotifyTrackingProtectionDisabled() override;
+
+  IPCResult RecvNotifyTrackingResource() override;
+
+  IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& info) override;
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  virtual ~HttpBackgroundChannelChild();
+
+  // Initiate the creation of the PBckground IPC channel.
+  // Return false if failed.
+  bool CreateBackgroundChannel();
+
+  // Check OnStartRequest is sent by parent process over main thread IPC
+  // but not yet received on child process.
+  // return true before RecvOnStartRequestSent is invoked.
+  // return false if RecvOnStartRequestSent is invoked but not
+  // OnStartRequestReceived.
+  // return true after both RecvOnStartRequestSend and OnStartRequestReceived
+  // are invoked.
+  bool IsWaitingOnStartRequest();
+
+  // Associated HttpChannelChild for handling the channel events.
+  // Will be removed while failed to create background channel,
+  // destruction of the background channel, or explicitly dissociation
+  // via OnChannelClosed callback.
+  RefPtr<HttpChannelChild> mChannelChild;
+
+  // True if OnStartRequest is received by HttpChannelChild.
+  bool mStartReceived = false;
+
+  // True if OnStartRequest is sent by HttpChannelParent.
+  bool mStartSent = false;
+
+  // Store pending messages that require to be handled after OnStartRequest.
+  // Should be flushed after OnStartRequest is received and handled.
+  nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_HttpBackgroundChannelChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include "HttpBackgroundChannelParent.h"
+
+#include "HttpChannelParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Unused.h"
+#include "nsIBackgroundChannelRegistrar.h"
+#include "nsNetCID.h"
+#include "nsQueryObject.h"
+#include "nsThreadUtils.h"
+
+using mozilla::dom::ContentParent;
+using mozilla::ipc::AssertIsInMainProcess;
+using mozilla::ipc::AssertIsOnBackgroundThread;
+using mozilla::ipc::BackgroundParent;
+using mozilla::ipc::IPCResult;
+using mozilla::ipc::IsOnBackgroundThread;
+
+namespace mozilla {
+namespace net {
+
+/*
+ * Helper class for continuing the AsyncOpen procedure on main thread.
+ */
+class ContinueAsyncOpenRunnable final : public Runnable
+{
+public:
+  ContinueAsyncOpenRunnable(HttpBackgroundChannelParent* aActor,
+                            const uint64_t& aChannelId)
+    : mActor(aActor)
+    , mChannelId(aChannelId)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mActor);
+  }
+
+  NS_IMETHOD Run() override
+  {
+    LOG(("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p channelId=%"
+         PRIu64 "]\n", mActor.get(), mChannelId));
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+      do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+    MOZ_ASSERT(registrar);
+
+    registrar->LinkBackgroundChannel(mChannelId, mActor);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<HttpBackgroundChannelParent> mActor;
+  const uint64_t mChannelId;
+};
+
+HttpBackgroundChannelParent::HttpBackgroundChannelParent()
+  : mIPCOpened(true)
+  , mBackgroundThread(NS_GetCurrentThread())
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+}
+
+HttpBackgroundChannelParent::~HttpBackgroundChannelParent()
+{
+  MOZ_ASSERT(NS_IsMainThread() || IsOnBackgroundThread());
+  MOZ_ASSERT(!mIPCOpened);
+}
+
+nsresult
+HttpBackgroundChannelParent::Init(const uint64_t& aChannelId)
+{
+  LOG(("HttpBackgroundChannelParent::Init [this=%p channelId=%" PRIu64 "]\n",
+       this, aChannelId));
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  RefPtr<ContinueAsyncOpenRunnable> runnable =
+    new ContinueAsyncOpenRunnable(this, aChannelId);
+
+  return NS_DispatchToMainThread(runnable);
+}
+
+void
+HttpBackgroundChannelParent::LinkToChannel(HttpChannelParent* aChannelParent)
+{
+  LOG(("HttpBackgroundChannelParent::LinkToChannel [this=%p channel=%p]\n",
+       this, aChannelParent));
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_DIAGNOSTIC_ASSERT(mIPCOpened);
+  if (!mIPCOpened) {
+    return;
+  }
+
+  mChannelParent = aChannelParent;
+}
+
+void
+HttpBackgroundChannelParent::OnChannelClosed()
+{
+  LOG(("HttpBackgroundChannelParent::OnChannelClosed [this=%p]\n", this));
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mIPCOpened) {
+    return;
+  }
+
+  nsresult rv;
+
+  RefPtr<HttpBackgroundChannelParent> self = this;
+  rv = mBackgroundThread->Dispatch(NS_NewRunnableFunction([self]() {
+    LOG(("HttpBackgroundChannelParent::DeleteRunnable [this=%p]\n", self.get()));
+    AssertIsOnBackgroundThread();
+
+    if (!self->mIPCOpened.compareExchange(true, false)) {
+      return;
+    }
+
+    Unused << self->Send__delete__(self);
+  }), NS_DISPATCH_NORMAL);
+
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+bool
+HttpBackgroundChannelParent::OnStartRequestSent()
+{
+  LOG(("HttpBackgroundChannelParent::OnStartRequestSent [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod(this, &HttpBackgroundChannelParent::OnStartRequestSent),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendOnStartRequestSent();
+}
+
+bool
+HttpBackgroundChannelParent::OnTransportAndData(
+                                               const nsresult& aChannelStatus,
+                                               const nsresult& aTransportStatus,
+                                               const uint64_t& aOffset,
+                                               const uint32_t& aCount,
+                                               const nsCString& aData)
+{
+  LOG(("HttpBackgroundChannelParent::OnTransportAndData [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod<const nsresult, const nsresult, const uint64_t,
+                        const uint32_t, const nsCString>
+        (this, &HttpBackgroundChannelParent::OnTransportAndData,
+         aChannelStatus, aTransportStatus, aOffset, aCount, aData),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendOnTransportAndData(aChannelStatus, aTransportStatus,
+                                aOffset, aCount, aData);
+}
+
+bool
+HttpBackgroundChannelParent::OnStopRequest(const nsresult& aChannelStatus,
+                                           const ResourceTimingStruct& aTiming)
+{
+  LOG(("HttpBackgroundChannelParent::OnStopRequest [this=%p "
+        "status=%" PRIx32 "]\n", this, static_cast<uint32_t>(aChannelStatus)));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod<const nsresult, const ResourceTimingStruct>
+        (this, &HttpBackgroundChannelParent::OnStopRequest,
+         aChannelStatus, aTiming),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendOnStopRequest(aChannelStatus, aTiming);
+}
+
+bool
+HttpBackgroundChannelParent::OnProgress(const int64_t& aProgress,
+                                        const int64_t& aProgressMax)
+{
+  LOG(("HttpBackgroundChannelParent::OnProgress [this=%p progress=%" PRId64
+       " max=%" PRId64 "]\n", this, aProgress, aProgressMax));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod<const int64_t, const int64_t>
+        (this, &HttpBackgroundChannelParent::OnProgress,
+         aProgress, aProgressMax),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendOnProgress(aProgress, aProgressMax);
+}
+
+bool
+HttpBackgroundChannelParent::OnStatus(const nsresult& aStatus)
+{
+  LOG(("HttpBackgroundChannelParent::OnStatus [this=%p stauts=%" PRIx32
+       "]\n", this, static_cast<uint32_t>(aStatus)));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod<const nsresult>
+        (this, &HttpBackgroundChannelParent::OnStatus, aStatus),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendOnStatus(aStatus);
+}
+
+bool
+HttpBackgroundChannelParent::OnDiversion()
+{
+  LOG(("HttpBackgroundChannelParent::OnDiversion [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod(this, &HttpBackgroundChannelParent::OnDiversion),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  if (!SendFlushedForDiversion()) {
+    return false;
+  }
+
+  // The listener chain should now be setup; tell HttpChannelChild to divert
+  // the OnDataAvailables and OnStopRequest to associated HttpChannelParent.
+  if (!SendDivertMessages()) {
+    return false;
+  }
+
+  return true;
+}
+
+bool
+HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled()
+{
+  LOG(("HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod(this, &HttpBackgroundChannelParent::OnNotifyTrackingProtectionDisabled),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendNotifyTrackingProtectionDisabled();
+}
+
+bool
+HttpBackgroundChannelParent::OnNotifyTrackingResource()
+{
+  LOG(("HttpBackgroundChannelParent::OnNotifyTrackingResource [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod(this, &HttpBackgroundChannelParent::OnNotifyTrackingResource),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendNotifyTrackingResource();
+}
+
+bool
+HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
+                                                    const nsACString& aList,
+                                                    const nsACString& aProvider,
+                                                    const nsACString& aPrefix)
+{
+  LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n", this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = mBackgroundThread->Dispatch(
+      NewRunnableMethod<const nsCString, const nsCString, const nsCString>
+        (this, &HttpBackgroundChannelParent::OnSetClassifierMatchedInfo,
+         aList, aProvider, aPrefix),
+      NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  ClassifierInfo info;
+  info.list() = aList;
+  info.prefix() = aPrefix;
+  info.provider() = aProvider;
+
+  return SendSetClassifierMatchedInfo(info);
+}
+
+void
+HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  LOG(("HttpBackgroundChannelParent::ActorDestroy [this=%p]\n", this));
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  mIPCOpened = false;
+
+  RefPtr<HttpBackgroundChannelParent> self = this;
+  DebugOnly<nsresult> rv =
+    NS_DispatchToMainThread(NS_NewRunnableFunction([self]() {
+      MOZ_ASSERT(NS_IsMainThread());
+
+      RefPtr<HttpChannelParent> channelParent =
+        self->mChannelParent.forget();
+
+      if (channelParent) {
+        channelParent->OnBackgroundParentDestroyed();
+      }
+    }));
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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_net_HttpBackgroundChannelParent_h
+#define mozilla_net_HttpBackgroundChannelParent_h
+
+#include "mozilla/net/PHttpBackgroundChannelParent.h"
+#include "mozilla/Atomics.h"
+#include "nsID.h"
+#include "nsISupportsImpl.h"
+
+class nsIEventTarget;
+
+namespace mozilla {
+namespace net {
+
+class HttpChannelParent;
+
+class HttpBackgroundChannelParent final : public PHttpBackgroundChannelParent
+{
+public:
+  explicit HttpBackgroundChannelParent();
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelParent)
+
+  // Try to find associated HttpChannelParent with the same
+  // channel Id.
+  nsresult Init(const uint64_t& aChannelId);
+
+  // Callbacks for BackgroundChannelRegistrar to notify
+  // the associated HttpChannelParent is found.
+  void LinkToChannel(HttpChannelParent* aChannelParent);
+
+  // Callbacks for HttpChannelParent to close the background
+  // IPC channel.
+  void OnChannelClosed();
+
+  // To send OnStartRequestSend message over background channel.
+  bool OnStartRequestSent();
+
+  // To send OnTransportAndData message over background channel.
+  bool OnTransportAndData(const nsresult& aChannelStatus,
+                          const nsresult& aTransportStatus,
+                          const uint64_t& aOffset,
+                          const uint32_t& aCount,
+                          const nsCString& aData);
+
+  // To send OnStopRequest message over background channel.
+  bool OnStopRequest(const nsresult& aChannelStatus,
+                     const ResourceTimingStruct& aTiming);
+
+  // To send OnProgress message over background channel.
+  bool OnProgress(const int64_t& aProgress,
+                  const int64_t& aProgressMax);
+
+  // To send OnStatus message over background channel.
+  bool OnStatus(const nsresult& aStatus);
+
+  // To send FlushedForDiversion and DivertMessages messages
+  // over background channel.
+  bool OnDiversion();
+
+  // To send NotifyTrackingProtectionDisabled message over background channel.
+  bool OnNotifyTrackingProtectionDisabled();
+
+  // To send NotifyTrackingResource message over background channel.
+  bool OnNotifyTrackingResource();
+
+  // To send SetClassifierMatchedInfo message over background channel.
+  bool OnSetClassifierMatchedInfo(const nsACString& aList,
+                                  const nsACString& aProvider,
+                                  const nsACString& aPrefix);
+
+protected:
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  virtual ~HttpBackgroundChannelParent();
+
+  Atomic<bool> mIPCOpened;
+
+  nsCOMPtr<nsIEventTarget> mBackgroundThread;
+
+  // associated HttpChannelParent for generating the channel events
+  RefPtr<HttpChannelParent> mChannelParent;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_HttpBackgroundChannelParent_h
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -57,16 +57,17 @@
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDOMWindowUtils.h"
+#include "nsHttpChannel.h"
 #include "nsRedirectHistoryEntry.h"
 
 #include <algorithm>
 #include "HttpBaseChannel.h"
 
 namespace mozilla {
 namespace net {
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -359,16 +359,21 @@ public: /* Necko internal use only... */
     // |EnsureUploadStreamIsCloneableComplete| to main thread.
     virtual void OnCopyComplete(nsresult aStatus);
 
     void SetIsTrackingResource()
     {
       mIsTrackingResource = true;
     }
 
+    const uint64_t& ChannelId() const
+    {
+      return mChannelId;
+    }
+
 protected:
   // Handle notifying listener, removing from loadgroup if request failed.
   void     DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
   void ReleaseListeners();
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -15,21 +15,24 @@
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 
+#include "AltDataOutputStreamChild.h"
+#include "HttpBackgroundChannelChild.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsPrimitives.h"
 #include "nsChannelClassifier.h"
 #include "nsGlobalWindow.h"
 #include "nsStringStream.h"
+#include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsSerializationHelper.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/ipc/BackgroundUtils.h"
@@ -41,46 +44,30 @@
 #include "mozIThirdPartyUtil.h"
 #include "nsContentSecurityManager.h"
 #include "nsIDeprecationWarner.h"
 #include "nsICompressConvStats.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIEventTarget.h"
+#include "nsRedirectHistoryEntry.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 
-#ifdef OS_POSIX
-#include "chrome/common/file_descriptor_set_posix.h"
-#endif
-
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
-namespace {
-
-const uint32_t kMaxFileDescriptorsPerMessage = 250;
-
-#ifdef OS_POSIX
-// Keep this in sync with other platforms.
-static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
-              "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
-#endif
-
-} // namespace
-
-
 NS_IMPL_ISUPPORTS(InterceptStreamListener,
                   nsIStreamListener,
                   nsIRequestObserver,
                   nsIProgressEventSink)
 
 NS_IMETHODIMP
 InterceptStreamListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
@@ -281,16 +268,36 @@ HttpChannelChild::AddIPDLReference()
 void
 HttpChannelChild::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
   mIPCOpen = false;
   Release();
 }
 
+void
+HttpChannelChild::OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild)
+{
+  LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n",
+       this, aBgChild));
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBgChild);
+
+  MOZ_ASSERT(mBgChild == aBgChild);
+}
+
+void
+HttpChannelChild::OnBackgroundChildDestroyed()
+{
+  LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mBgChild = nullptr;
+}
+
 class AssociateApplicationCacheEvent : public ChannelEvent
 {
   public:
     AssociateApplicationCacheEvent(HttpChannelChild* aChild,
                                    const nsCString &aGroupID,
                                    const nsCString &aClientID)
     : mChild(aChild)
     , groupID(aGroupID)
@@ -434,16 +441,22 @@ HttpChannelChild::RecvOnStartRequest(con
   mEventQ->RunOrEnqueue(new StartRequestEvent(this, channelStatus, responseHead,
                                               useResponseHead, requestHeaders,
                                               isFromCache, cacheEntryAvailable,
                                               cacheExpirationTime,
                                               cachedCharset,
                                               securityInfoSerialization,
                                               selfAddr, peerAddr, cacheKey,
                                               altDataType, altDataLen));
+
+  MOZ_ASSERT(mBgChild);
+  if (mBgChild) {
+    mBgChild->OnStartRequestReceived();
+  }
+
   return IPC_OK();
 }
 
 void
 HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
                                  const nsHttpResponseHead& responseHead,
                                  const bool& useResponseHead,
                                  const nsHttpHeaderArray& requestHeaders,
@@ -655,32 +668,31 @@ class TransportAndDataEvent : public Cha
   HttpChannelChild* mChild;
   nsresult mChannelStatus;
   nsresult mTransportStatus;
   nsCString mData;
   uint64_t mOffset;
   uint32_t mCount;
 };
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvOnTransportAndData(const nsresult& channelStatus,
-                                         const nsresult& transportStatus,
-                                         const uint64_t& offset,
-                                         const uint32_t& count,
-                                         const nsCString& data)
+void
+HttpChannelChild::ProcessOnTransportAndData(const nsresult& aChannelStatus,
+                                            const nsresult& aTransportStatus,
+                                            const uint64_t& aOffset,
+                                            const uint32_t& aCount,
+                                            const nsCString& aData)
 {
-  LOG(("HttpChannelChild::RecvOnTransportAndData [this=%p]\n", this));
+  LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
                      "Should not be receiving any more callbacks from parent!");
-
-  mEventQ->RunOrEnqueue(new TransportAndDataEvent(this, channelStatus,
-                                                  transportStatus, data, offset,
-                                                  count),
+  mEventQ->RunOrEnqueue(new TransportAndDataEvent(this, aChannelStatus,
+                                                  aTransportStatus, aData,
+                                                  aOffset, aCount),
                         mDivertingToParent);
-  return IPC_OK();
 }
 
 class MaybeDivertOnDataHttpEvent : public ChannelEvent
 {
  public:
   MaybeDivertOnDataHttpEvent(HttpChannelChild* child,
                              const nsCString& data,
                              const uint64_t& offset,
@@ -898,27 +910,27 @@ class StopRequestEvent : public ChannelE
     return target.forget();
   }
  private:
   HttpChannelChild* mChild;
   nsresult mChannelStatus;
   ResourceTimingStruct mTiming;
 };
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvOnStopRequest(const nsresult& channelStatus,
-                                    const ResourceTimingStruct& timing)
+void
+HttpChannelChild::ProcessOnStopRequest(const nsresult& aChannelStatus,
+                                       const ResourceTimingStruct& aTiming)
 {
-  LOG(("HttpChannelChild::RecvOnStopRequest [this=%p]\n", this));
+  LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
     "Should not be receiving any more callbacks from parent!");
 
-  mEventQ->RunOrEnqueue(new StopRequestEvent(this, channelStatus, timing),
+  mEventQ->RunOrEnqueue(new StopRequestEvent(this, aChannelStatus, aTiming),
                         mDivertingToParent);
-  return IPC_OK();
 }
 
 class MaybeDivertOnStopHttpEvent : public ChannelEvent
 {
  public:
   MaybeDivertOnStopHttpEvent(HttpChannelChild* child,
                              const nsresult& channelStatus)
   : mChild(child)
@@ -1039,16 +1051,18 @@ HttpChannelChild::OnStopRequest(const ns
     // (although we really shouldn't receive any msgs after OnStop),
     // so make sure this goes out of scope before then.
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
 
     DoOnStopRequest(this, channelStatus, mListenerContext);
     // DoOnStopRequest() calls ReleaseListeners()
   }
 
+  CleanupBackgroundChannel();
+
   // DocumentChannelCleanup actually nulls out mCacheEntry in the parent, which
   // we might need later to open the Alt-Data output stream, so just return here
   if (!mPreferredCachedAltDataType.IsEmpty()) {
     mKeptAlive = true;
     return;
   }
 
   if (mLoadFlags & LOAD_DOCUMENT_URI) {
@@ -1137,22 +1151,23 @@ class ProgressEvent : public ChannelEven
     nsCOMPtr<nsIEventTarget> target = mChild->GetNeckoTarget();
     return target.forget();
   }
  private:
   HttpChannelChild* mChild;
   int64_t mProgress, mProgressMax;
 };
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvOnProgress(const int64_t& progress,
-                                 const int64_t& progressMax)
+void
+HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
+                                    const int64_t& aProgressMax)
 {
-  mEventQ->RunOrEnqueue(new ProgressEvent(this, progress, progressMax));
-  return IPC_OK();
+  LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+  mEventQ->RunOrEnqueue(new ProgressEvent(this, aProgress, aProgressMax));
 }
 
 void
 HttpChannelChild::OnProgress(const int64_t& progress,
                              const int64_t& progressMax)
 {
   LOG(("HttpChannelChild::OnProgress [this=%p progress=%" PRId64 "/%" PRId64 "]\n",
        this, progress, progressMax));
@@ -1192,21 +1207,22 @@ class StatusEvent : public ChannelEvent
     nsCOMPtr<nsIEventTarget> target = mChild->GetNeckoTarget();
     return target.forget();
   }
  private:
   HttpChannelChild* mChild;
   nsresult mStatus;
 };
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvOnStatus(const nsresult& status)
+void
+HttpChannelChild::ProcessOnStatus(const nsresult& aStatus)
 {
-  mEventQ->RunOrEnqueue(new StatusEvent(this, status));
-  return IPC_OK();
+  LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+  mEventQ->RunOrEnqueue(new StatusEvent(this, aStatus));
 }
 
 void
 HttpChannelChild::OnStatus(const nsresult& status)
 {
   LOG(("HttpChannelChild::OnStatus [this=%p status=%" PRIx32 "]\n",
        this, static_cast<uint32_t>(status)));
 
@@ -1261,35 +1277,67 @@ HttpChannelChild::RecvFailedAsyncOpen(co
 
 // We need to have an implementation of this function just so that we can keep
 // all references to mCallOnResume of type HttpChannelChild:  it's not OK in C++
 // to set a member function ptr to a base class function.
 void
 HttpChannelChild::HandleAsyncAbort()
 {
   HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
+
+  // Ignore all the messages from background channel after channel aborted.
+  CleanupBackgroundChannel();
 }
 
 void
 HttpChannelChild::FailedAsyncOpen(const nsresult& status)
 {
   LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
        this, static_cast<uint32_t>(status)));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Might be called twice in race condition in theory.
+  // (one by RecvFailedAsyncOpen, another by
+  // HttpBackgroundChannelChild::ActorFailed)
+  if (NS_WARN_IF(NS_FAILED(mStatus))) {
+    return;
+  }
 
   mStatus = status;
 
   // We're already being called from IPDL, therefore already "async"
   HandleAsyncAbort();
 
   if (mIPCOpen) {
     TrySendDeletingChannel();
   }
 }
 
 void
+HttpChannelChild::CleanupBackgroundChannel()
+{
+  LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
+       this, mBgChild.get()));
+  if (!mBgChild) {
+    return;
+  }
+
+  RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild.forget();
+
+  if (!NS_IsMainThread()) {
+    SystemGroup::Dispatch(
+      "HttpChannelChild::CleanupBackgroundChannel",
+      TaskCategory::Other,
+      NewRunnableMethod(bgChild, &HttpBackgroundChannelChild::OnChannelClosed));
+  } else {
+    bgChild->OnChannelClosed();
+  }
+}
+
+void
 HttpChannelChild::DoNotifyListenerCleanup()
 {
   LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
 
   if (mInterceptListener) {
     mInterceptListener->Cleanup();
     mInterceptListener = nullptr;
   }
@@ -1668,75 +1716,80 @@ class HttpFlushedForDiversionEvent : pub
     MOZ_ASSERT(mChild);
     nsCOMPtr<nsIEventTarget> target = mChild->GetNeckoTarget();
     return target.forget();
   }
  private:
   HttpChannelChild* mChild;
 };
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvFlushedForDiversion()
+void
+HttpChannelChild::ProcessFlushedForDiversion()
 {
-  LOG(("HttpChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
+  LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   mEventQ->RunOrEnqueue(new HttpFlushedForDiversionEvent(this), true);
-
-  return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvNotifyTrackingProtectionDisabled()
+void
+HttpChannelChild::ProcessNotifyTrackingProtectionDisabled()
 {
+  LOG(("HttpChannelChild::ProcessNotifyTrackingProtectionDisabled [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsChannelClassifier::NotifyTrackingProtectionDisabled(this);
-  return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvNotifyTrackingResource()
+void
+HttpChannelChild::ProcessNotifyTrackingResource()
 {
+  LOG(("HttpChannelChild::ProcessNotifyTrackingResource [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
   SetIsTrackingResource();
-  return IPC_OK();
 }
 
 void
 HttpChannelChild::FlushedForDiversion()
 {
   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   // Once this is set, it should not be unset before HttpChannelChild is taken
   // down. After it is set, no OnStart/OnData/OnStop callbacks should be
   // received from the parent channel, nor dequeued from the ChannelEventQueue.
   mFlushedForDiversion = true;
 
   SendDivertComplete();
 }
 
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo)
+void
+HttpChannelChild::ProcessSetClassifierMatchedInfo(const nsCString& aList,
+                                                  const nsCString& aProvider,
+                                                  const nsCString& aPrefix)
 {
-  SetMatchedInfo(aInfo.list(), aInfo.provider(), aInfo.prefix());
-  return IPC_OK();
+  LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  SetMatchedInfo(aList, aProvider, aPrefix);
 }
 
-
-mozilla::ipc::IPCResult
-HttpChannelChild::RecvDivertMessages()
+void
+HttpChannelChild::ProcessDivertMessages()
 {
-  LOG(("HttpChannelChild::RecvDivertMessages [this=%p]\n", this));
+  LOG(("HttpChannelChild::ProcessDivertMessages [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(mDivertingToParent);
   MOZ_RELEASE_ASSERT(mSuspendCount > 0);
 
   // DivertTo() has been called on parent, so we can now start sending queued
   // IPDL messages back to parent listener.
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume()));
-
-  return IPC_OK();
 }
 
 // Returns true if has actually completed the redirect and cleaned up the
 // channel, or false the interception logic kicked in and we need to asyncly
 // call FinishInterceptedRedirect and CleanupRedirectingChannel.
 // The argument is an optional OverrideRunnable that we pass to the redirected
 // channel.
 bool
@@ -1844,16 +1897,30 @@ HttpChannelChild::ConnectParent(uint32_t
                          ->GetBrowserOrId(tabChild);
   if (!gNeckoChild->
         SendPHttpChannelConstructor(this, browser,
                                     IPC::SerializedLoadContext(this),
                                     connectArgs)) {
     return NS_ERROR_FAILURE;
   }
 
+  {
+    MOZ_ASSERT(!mBgChild);
+
+    RefPtr<HttpBackgroundChannelChild> bgChild =
+      new HttpBackgroundChannelChild();
+
+    nsresult rv = bgChild->Init(this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mBgChild = bgChild.forget();
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
                                         nsISupports *aContext)
 {
   LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this));
@@ -2472,16 +2539,36 @@ HttpChannelChild::ContinueAsyncOpen()
 
   PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
   if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
                                                 IPC::SerializedLoadContext(this),
                                                 openArgs)) {
     return NS_ERROR_FAILURE;
   }
 
+  {
+    // Service worker might use the same HttpChannelChild to do async open
+    // twice. Need to disconnect with previous background channel before
+    // creating the new one.
+    if (mBgChild) {
+      RefPtr<HttpBackgroundChannelChild> prevBgChild = mBgChild.forget();
+      prevBgChild->OnChannelClosed();
+    }
+
+    RefPtr<HttpBackgroundChannelChild> bgChild =
+      new HttpBackgroundChannelChild();
+
+    rv = bgChild->Init(this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mBgChild = bgChild.forget();
+  }
+
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -34,16 +34,17 @@
 #include "mozilla/net/DNS.h"
 
 class nsIEventTarget;
 class nsInputStreamPump;
 
 namespace mozilla {
 namespace net {
 
+class HttpBackgroundChannelChild;
 class InterceptedChannelContent;
 class InterceptStreamListener;
 
 class HttpChannelChild final : public PHttpChannelChild
                              , public HttpBaseChannel
                              , public HttpAsyncAborter<HttpChannelChild>
                              , public nsICacheInfoChannel
                              , public nsIProxiedChannel
@@ -105,60 +106,52 @@ public:
   // IPDL holds a reference while the PHttpChannel protocol is live (starting at
   // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of
   // which call NeckoChild::DeallocPHttpChannelChild()).
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
   MOZ_MUST_USE bool IsSuspended();
 
-  mozilla::ipc::IPCResult RecvNotifyTrackingProtectionDisabled() override;
-  mozilla::ipc::IPCResult RecvNotifyTrackingResource() override;
   void FlushedForDiversion();
-  mozilla::ipc::IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo) override;
 
   void OnCopyComplete(nsresult aStatus) override;
 
+  // Callback while background channel is ready.
+  void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
+  // Callback while background channel is destroyed.
+  void OnBackgroundChildDestroyed();
+
 protected:
   mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
                                              const nsHttpResponseHead& responseHead,
                                              const bool& useResponseHead,
                                              const nsHttpHeaderArray& requestHeaders,
                                              const bool& isFromCache,
                                              const bool& cacheEntryAvailable,
                                              const uint32_t& cacheExpirationTime,
                                              const nsCString& cachedCharset,
                                              const nsCString& securityInfoSerialization,
                                              const NetAddr& selfAddr,
                                              const NetAddr& peerAddr,
                                              const int16_t& redirectCount,
                                              const uint32_t& cacheKey,
                                              const nsCString& altDataType,
                                              const int64_t& altDataLen) override;
-  mozilla::ipc::IPCResult RecvOnTransportAndData(const nsresult& channelStatus,
-                                                 const nsresult& status,
-                                                 const uint64_t& offset,
-                                                 const uint32_t& count,
-                                                 const nsCString& data) override;
-  mozilla::ipc::IPCResult RecvOnStopRequest(const nsresult& statusCode, const ResourceTimingStruct& timing) override;
-  mozilla::ipc::IPCResult RecvOnProgress(const int64_t& progress, const int64_t& progressMax) override;
-  mozilla::ipc::IPCResult RecvOnStatus(const nsresult& status) override;
   mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& status) override;
   mozilla::ipc::IPCResult RecvRedirect1Begin(const uint32_t& registrarId,
                                              const URIParams& newURI,
                                              const uint32_t& redirectFlags,
                                              const nsHttpResponseHead& responseHead,
                                              const nsCString& securityInfoSerialization,
                                              const uint64_t& channelId,
                                              const NetAddr& oldPeerAddr) override;
   mozilla::ipc::IPCResult RecvRedirect3Complete() override;
   mozilla::ipc::IPCResult RecvAssociateApplicationCache(const nsCString& groupID,
                                                         const nsCString& clientID) override;
-  mozilla::ipc::IPCResult RecvFlushedForDiversion() override;
-  mozilla::ipc::IPCResult RecvDivertMessages() override;
   mozilla::ipc::IPCResult RecvDeleteSelf() override;
   mozilla::ipc::IPCResult RecvFinishInterceptedRedirect() override;
 
   mozilla::ipc::IPCResult RecvReportSecurityMessage(const nsString& messageTag,
                                                     const nsString& messageCategory) override;
 
   mozilla::ipc::IPCResult RecvIssueDeprecationWarning(const uint32_t& warning,
                                                       const bool& asError) override;
@@ -212,16 +205,36 @@ private:
   // Get event target for processing network events.
   already_AddRefed<nsIEventTarget> GetNeckoTarget();
 
   // Get event target for ODA.
   already_AddRefed<nsIEventTarget> GetODATarget();
 
   MOZ_MUST_USE nsresult ContinueAsyncOpen();
 
+  // Callbacks while receiving OnTransportAndData/OnStopRequest/OnProgress/
+  // OnStatus/FlushedForDiversion/DivertMessages on background IPC channel.
+  void ProcessOnTransportAndData(const nsresult& aChannelStatus,
+                                 const nsresult& aStatus,
+                                 const uint64_t& aOffset,
+                                 const uint32_t& aCount,
+                                 const nsCString& aData);
+  void ProcessOnStopRequest(const nsresult& aStatusCode,
+                            const ResourceTimingStruct& aTiming);
+  void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
+  void ProcessOnStatus(const nsresult& aStatus);
+  void ProcessFlushedForDiversion();
+  void ProcessDivertMessages();
+  void ProcessNotifyTrackingProtectionDisabled();
+  void ProcessNotifyTrackingResource();
+  void ProcessSetClassifierMatchedInfo(const nsCString& aList,
+                                       const nsCString& aProvider,
+                                       const nsCString& aPrefix);
+
+
   void DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
   void DoOnStatus(nsIRequest* aRequest, nsresult status);
   void DoOnProgress(nsIRequest* aRequest, int64_t progress, int64_t progressMax);
   void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
                          uint64_t offset, uint32_t count);
   void DoPreOnStopRequest(nsresult aStatus);
   void DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsISupports* aContext);
 
@@ -307,16 +320,22 @@ private:
   // Set if the corresponding parent channel should force an interception to occur
   // before the network transaction is initiated.
   bool mShouldParentIntercept;
 
   // Set if the corresponding parent channel should suspend after a response
   // is synthesized.
   bool mSuspendParentAfterSynthesizeResponse;
 
+  RefPtr<HttpBackgroundChannelChild> mBgChild;
+
+  // Remove the association with background channel after OnStopRequest
+  // or AsyncAbort.
+  void CleanupBackgroundChannel();
+
   // Needed to call AsyncOpen in FinishInterceptedRedirect
   nsCOMPtr<nsIStreamListener> mInterceptedRedirectListener;
   nsCOMPtr<nsISupports> mInterceptedRedirectContext;
   // Needed to call CleanupRedirectingChannel in FinishInterceptedRedirect
   RefPtr<HttpChannelChild> mInterceptingChannel;
   // Used to call OverrideWithSynthesizedResponse in FinishInterceptedRedirect
   RefPtr<OverrideRunnable> mOverrideRunnable;
 
@@ -398,16 +417,17 @@ private:
   friend class Redirect1Event;
   friend class Redirect3Event;
   friend class DeleteSelfEvent;
   friend class HttpFlushedForDiversionEvent;
   friend class CancelEvent;
   friend class HttpAsyncAborter<HttpChannelChild>;
   friend class InterceptStreamListener;
   friend class InterceptedChannelContent;
+  friend class HttpBackgroundChannelChild;
 };
 
 // A stream listener interposed between the nsInputStreamPump used for intercepted channels
 // and this channel's original listener. This is only used to ensure the original listener
 // sees the channel as the request object, and to synthesize OnStatus and OnProgress notifications.
 class InterceptStreamListener : public nsIStreamListener
                               , public nsIProgressEventSink
 {
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -3,27 +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/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/HttpChannelParent.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
+#include "HttpBackgroundChannelParent.h"
 #include "HttpChannelParentListener.h"
 #include "nsHttpHandler.h"
+#include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPriority.h"
 #include "nsIAuthPromptProvider.h"
+#include "nsIBackgroundChannelRegistrar.h"
 #include "nsSerializationHelper.h"
 #include "nsISerializable.h"
 #include "nsIAssociatedContentSecurity.h"
 #include "nsIApplicationCacheService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
 #include "nsIAuthInformation.h"
@@ -32,36 +38,37 @@
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsICachingChannel.h"
 #include "mozilla/LoadInfo.h"
 #include "nsQueryObject.h"
 #include "mozilla/BasePrincipal.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIPrompt.h"
+#include "nsIRedirectChannelRegistrar.h"
 #include "nsIWindowWatcher.h"
 #include "nsIDocument.h"
+#include "nsStreamUtils.h"
 #include "nsStringStream.h"
+#include "nsIStorageStream.h"
 #include "nsQueryObject.h"
 #include "nsIURIClassifier.h"
-#include "mozilla/dom/ContentParent.h"
 
 using mozilla::BasePrincipal;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
 HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
                                      nsILoadContext* aLoadContext,
                                      PBOverrideStatus aOverrideStatus)
   : mIPCClosed(false)
   , mIgnoreProgress(false)
-  , mSentRedirect1Begin(false)
   , mSentRedirect1BeginFailed(false)
   , mReceivedRedirect2Verify(false)
   , mPBOverride(aOverrideStatus)
   , mLoadContext(aLoadContext)
   , mStatus(NS_OK)
   , mPendingDiversion(false)
   , mDivertingFromChild(false)
   , mDivertedOnStartRequest(false)
@@ -86,31 +93,34 @@ HttpChannelParent::HttpChannelParent(con
   }
 
   mEventQ = new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
 }
 
 HttpChannelParent::~HttpChannelParent()
 {
   LOG(("Destroying HttpChannelParent [this=%p]\n", this));
+  CleanupBackgroundChannel();
 }
 
 void
 HttpChannelParent::ActorDestroy(ActorDestroyReason why)
 {
   // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
   // yet, but child process has crashed.  We must not try to send any more msgs
   // to child, or IPDL will kill chrome process, too.
   mIPCClosed = true;
 
   // If this is an intercepted channel, we need to make sure that any resources are
   // cleaned up to avoid leaks.
   if (mParentListener) {
     mParentListener->ClearInterceptedChannel();
   }
+
+  CleanupBackgroundChannel();
 }
 
 bool
 HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
 {
   LOG(("HttpChannelParent::Init [this=%p]\n", this));
   switch (aArgs.type()) {
   case HttpChannelCreationArgs::THttpChannelOpenArgs:
@@ -146,31 +156,127 @@ HttpChannelParent::Init(const HttpChanne
     return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
   }
   default:
     NS_NOTREACHED("unknown open type");
     return false;
   }
 }
 
+void
+HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv)
+{
+  LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
+       "]\n", this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // TryInvokeAsyncOpen is called more than we expected.
+  // Assert in nightly build but ignore it in release channel.
+  MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0);
+  if (NS_WARN_IF(!mAsyncOpenBarrier)) {
+    return;
+  }
+
+  if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) {
+    // Need to wait for more events.
+    return;
+  }
+
+  InvokeAsyncOpen(aRv);
+}
+
+void
+HttpChannelParent::OnBackgroundParentReady(
+                                         HttpBackgroundChannelParent* aBgParent)
+{
+  LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
+       this, aBgParent));
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mBgParent);
+
+  mBgParent = aBgParent;
+
+  mPromise.ResolveIfExists(true, __func__);
+}
+
+void
+HttpChannelParent::OnBackgroundParentDestroyed()
+{
+  LOG(("HttpChannelParent::OnBackgroundParentDestroyed [this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mPromise.IsEmpty()) {
+    MOZ_ASSERT(!mBgParent);
+    mPromise.Reject(NS_ERROR_FAILURE, __func__);
+    return;
+  }
+
+  if (!mBgParent) {
+    return;
+  }
+
+  // Background channel is closed unexpectly, abort PHttpChannel operation.
+  mBgParent = nullptr;
+  Delete();
+}
+
+void
+HttpChannelParent::CleanupBackgroundChannel()
+{
+  LOG(("HttpChannelParent::CleanupBackgroundChannel [this=%p bgParent=%p]\n",
+       this, mBgParent.get()));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mBgParent) {
+    RefPtr<HttpBackgroundChannelParent> bgParent = mBgParent.forget();
+    bgParent->OnChannelClosed();
+    return;
+  }
+
+  if (!mPromise.IsEmpty()) {
+    mRequest.DisconnectIfExists();
+    mPromise.Reject(NS_ERROR_FAILURE, __func__);
+
+    if (!mChannel) {
+      return;
+    }
+
+    // This HttpChannelParent might still have a reference from
+    // BackgroundChannelRegistrar.
+    nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+      do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+    MOZ_ASSERT(registrar);
+
+    registrar->DeleteChannel(mChannel->ChannelId());
+
+    // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
+    // is still on going. we need to abort AsyncOpen with failure to destroy
+    // PHttpChannel actor.
+    if (mAsyncOpenBarrier) {
+      TryInvokeAsyncOpen(NS_ERROR_FAILURE);
+    }
+  }
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ADDREF(HttpChannelParent)
 NS_IMPL_RELEASE(HttpChannelParent)
 NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
   NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
   NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
   if (aIID.Equals(NS_GET_IID(HttpChannelParent))) {
     foundInterface = static_cast<nsIInterfaceRequestor*>(this);
   } else
 NS_INTERFACE_MAP_END
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIInterfaceRequestor
@@ -228,37 +334,57 @@ HttpChannelParent::GetInterface(const ns
   return QueryInterface(aIID, result);
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::PHttpChannelParent
 //-----------------------------------------------------------------------------
 
 void
+HttpChannelParent::AsyncOpenFailed(nsresult aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_FAILED(aRv));
+
+  // Break the reference cycle among HttpChannelParent,
+  // HttpChannelParentListener, and nsHttpChannel to avoid memory leakage.
+  mChannel = nullptr;
+  mParentListener = nullptr;
+
+  if (!mIPCClosed) {
+    Unused << SendFailedAsyncOpen(aRv);
+  }
+}
+
+void
 HttpChannelParent::InvokeAsyncOpen(nsresult rv)
 {
+  LOG(("HttpChannelParent::InvokeAsyncOpen [this=%p rv=%" PRIx32 "]\n",
+       this, static_cast<uint32_t>(rv)));
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (NS_FAILED(rv)) {
-    Unused << SendFailedAsyncOpen(rv);
+    AsyncOpenFailed(rv);
     return;
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
   if (NS_FAILED(rv)) {
-    Unused << SendFailedAsyncOpen(rv);
+    AsyncOpenFailed(rv);
     return;
   }
   if (loadInfo && loadInfo->GetEnforceSecurity()) {
     rv = mChannel->AsyncOpen2(mParentListener);
   }
   else {
     rv = mChannel->AsyncOpen(mParentListener, nullptr);
   }
   if (NS_FAILED(rv)) {
-    Unused << SendFailedAsyncOpen(rv);
+    AsyncOpenFailed(rv);
   }
 }
 
 namespace {
 class InvokeAsyncOpen : public Runnable
 {
   nsMainThreadPtrHandle<nsIInterfaceRequestor> mChannel;
   nsresult mStatus;
@@ -268,17 +394,17 @@ public:
   : mChannel(aChannel)
   , mStatus(aStatus)
   {
   }
 
   NS_IMETHOD Run()
   {
     RefPtr<HttpChannelParent> channel = do_QueryObject(mChannel.get());
-    channel->InvokeAsyncOpen(mStatus);
+    channel->TryInvokeAsyncOpen(mStatus);
     return NS_OK;
   }
 };
 
 struct UploadStreamClosure {
   nsMainThreadPtrHandle<nsIInterfaceRequestor> mChannel;
 
   explicit UploadStreamClosure(const nsMainThreadPtrHandle<nsIInterfaceRequestor>& aChannel)
@@ -379,82 +505,81 @@ HttpChannelParent::DoAsyncOpen(  const U
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, loadInfo,
                              nullptr, nullptr, aLoadFlags, ios);
   if (NS_FAILED(rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
-  mChannel = do_QueryObject(channel, &rv);
+  RefPtr<nsHttpChannel> httpChannel = do_QueryObject(channel, &rv);
   if (NS_FAILED(rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
   // Set the channelId allocated in child to the parent instance
-  mChannel->SetChannelId(aChannelId);
-  mChannel->SetTopLevelContentWindowId(aContentWindowId);
-  mChannel->SetTopLevelOuterContentWindowId(aTopLevelOuterContentWindowId);
+  httpChannel->SetChannelId(aChannelId);
+  httpChannel->SetTopLevelContentWindowId(aContentWindowId);
+  httpChannel->SetTopLevelOuterContentWindowId(aTopLevelOuterContentWindowId);
 
-  mChannel->SetWarningReporter(this);
-  mChannel->SetTimingEnabled(true);
+  httpChannel->SetWarningReporter(this);
+  httpChannel->SetTimingEnabled(true);
   if (mPBOverride != kPBOverride_Unset) {
-    mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
+    httpChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   }
 
   if (doResumeAt)
-    mChannel->ResumeAt(startPos, entityID);
+    httpChannel->ResumeAt(startPos, entityID);
 
   if (originalUri)
-    mChannel->SetOriginalURI(originalUri);
+    httpChannel->SetOriginalURI(originalUri);
   if (docUri)
-    mChannel->SetDocumentURI(docUri);
+    httpChannel->SetDocumentURI(docUri);
   if (referrerUri) {
-    rv = mChannel->SetReferrerWithPolicyInternal(referrerUri, aReferrerPolicy);
+    rv = httpChannel->SetReferrerWithPolicyInternal(referrerUri, aReferrerPolicy);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
   if (apiRedirectToUri)
-    mChannel->RedirectTo(apiRedirectToUri);
+    httpChannel->RedirectTo(apiRedirectToUri);
   if (topWindowUri) {
-    rv = mChannel->SetTopWindowURI(topWindowUri);
+    rv = httpChannel->SetTopWindowURI(topWindowUri);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
   if (aLoadFlags != nsIRequest::LOAD_NORMAL)
-    mChannel->SetLoadFlags(aLoadFlags);
+    httpChannel->SetLoadFlags(aLoadFlags);
 
   for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
     if (requestHeaders[i].mEmpty) {
-      mChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
+      httpChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
     } else {
-      mChannel->SetRequestHeader(requestHeaders[i].mHeader,
+      httpChannel->SetRequestHeader(requestHeaders[i].mHeader,
                                  requestHeaders[i].mValue,
                                  requestHeaders[i].mMerge);
     }
   }
 
-  mParentListener = new HttpChannelParentListener(this);
+  RefPtr<HttpChannelParentListener> parentListener
+    = new HttpChannelParentListener(this);
 
-  mChannel->SetNotificationCallbacks(mParentListener);
-
-  mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
+  httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
 
   if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
     const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
-    mChannel->SetCorsPreflightParameters(args.unsafeHeaders());
+    httpChannel->SetCorsPreflightParameters(args.unsafeHeaders());
   }
 
-  bool delayAsyncOpen = false;
   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
   if (stream) {
     // FIXME: The fast path of using the existing stream currently only applies to streams
     //   that have had their entire contents serialized from the child at this point.
     //   Once bug 1294446 and bug 1294450 are fixed it is worth revisiting this heuristic.
     nsCOMPtr<nsIIPCSerializableInputStream> completeStream = do_QueryInterface(stream);
     if (!completeStream) {
-      delayAsyncOpen = true;
+      // Wait for completion of async copying IPC upload stream to a local input stream.
+      ++mAsyncOpenBarrier;
 
       // buffer size matches PChildToParentStream transfer size.
       const uint32_t kBufferSize = 32768;
 
       nsCOMPtr<nsIStorageStream> storageStream;
       nsresult rv = NS_NewStorageStream(kBufferSize, UINT32_MAX,
                                         getter_AddRefs(storageStream));
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -489,83 +614,83 @@ HttpChannelParent::DoAsyncOpen(  const U
       // the AsyncOpen process once the full stream has been received.
       rv = NS_AsyncCopy(stream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
                         kBufferSize, // copy segment size
                         UploadCopyComplete, closure.release());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return SendFailedAsyncOpen(rv);
       }
 
-      mChannel->InternalSetUploadStream(newUploadStream);
+      httpChannel->InternalSetUploadStream(newUploadStream);
     } else {
-      mChannel->InternalSetUploadStream(stream);
+      httpChannel->InternalSetUploadStream(stream);
     }
-    mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
+    httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
-    mParentListener->SetupInterception(aSynthesizedResponseHead.get_nsHttpResponseHead());
+    parentListener->SetupInterception(aSynthesizedResponseHead.get_nsHttpResponseHead());
     mWillSynthesizeResponse = true;
-    mChannel->SetCouldBeSynthesized();
+    httpChannel->SetCouldBeSynthesized();
 
     if (!aSecurityInfoSerialization.IsEmpty()) {
       nsCOMPtr<nsISupports> secInfo;
       NS_DeserializeObject(aSecurityInfoSerialization, getter_AddRefs(secInfo));
-      rv = mChannel->OverrideSecurityInfo(secInfo);
+      rv = httpChannel->OverrideSecurityInfo(secInfo);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   } else {
     nsLoadFlags newLoadFlags;
-    mChannel->GetLoadFlags(&newLoadFlags);
+    httpChannel->GetLoadFlags(&newLoadFlags);
     newLoadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
-    mChannel->SetLoadFlags(newLoadFlags);
+    httpChannel->SetLoadFlags(newLoadFlags);
   }
 
   nsCOMPtr<nsISupportsPRUint32> cacheKey =
     do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
   rv = cacheKey->SetData(aCacheKey);
   if (NS_FAILED(rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
-  mChannel->SetCacheKey(cacheKey);
-  mChannel->PreferAlternativeDataType(aPreferredAlternativeType);
+  httpChannel->SetCacheKey(cacheKey);
+  httpChannel->PreferAlternativeDataType(aPreferredAlternativeType);
 
-  mChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
+  httpChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
 
-  mChannel->SetContentType(aContentTypeHint);
+  httpChannel->SetContentType(aContentTypeHint);
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL) {
-    mChannel->SetPriority(priority);
+    httpChannel->SetPriority(priority);
   }
   if (classOfService) {
-    mChannel->SetClassFlags(classOfService);
+    httpChannel->SetClassFlags(classOfService);
   }
-  mChannel->SetRedirectionLimit(redirectionLimit);
-  mChannel->SetAllowSTS(allowSTS);
-  mChannel->SetThirdPartyFlags(thirdPartyFlags);
-  mChannel->SetAllowSpdy(allowSpdy);
-  mChannel->SetAllowAltSvc(allowAltSvc);
-  mChannel->SetBeConservative(beConservative);
-  mChannel->SetInitialRwin(aInitialRwin);
-  mChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
+  httpChannel->SetRedirectionLimit(redirectionLimit);
+  httpChannel->SetAllowSTS(allowSTS);
+  httpChannel->SetThirdPartyFlags(thirdPartyFlags);
+  httpChannel->SetAllowSpdy(allowSpdy);
+  httpChannel->SetAllowAltSvc(allowAltSvc);
+  httpChannel->SetBeConservative(beConservative);
+  httpChannel->SetInitialRwin(aInitialRwin);
+  httpChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
 
-  mChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
-  mChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
-  mChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
-  mChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
-  mChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
-  mChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
+  httpChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
+  httpChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
+  httpChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
+  httpChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
+  httpChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
+  httpChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
 
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
-    do_QueryObject(mChannel);
+    do_QueryObject(httpChannel);
   nsCOMPtr<nsIApplicationCacheService> appCacheService =
     do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
 
   bool setChooseApplicationCache = chooseApplicationCache;
   if (appCacheChan && appCacheService) {
     // We might potentially want to drop this flag (that is TRUE by default)
     // after we successfully associate the channel with an application cache
     // reported by the channel child.  Dropping it here may be too early.
@@ -577,39 +702,83 @@ HttpChannelParent::DoAsyncOpen(  const U
       if (NS_SUCCEEDED(rv)) {
         appCacheChan->SetApplicationCache(appCache);
         setChooseApplicationCache = false;
       }
     }
 
     if (setChooseApplicationCache) {
       OriginAttributes attrs;
-      NS_GetOriginAttributes(mChannel, attrs);
+      NS_GetOriginAttributes(httpChannel, attrs);
 
       nsCOMPtr<nsIPrincipal> principal =
         BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 
       bool chooseAppCache = false;
       // This works because we've already called SetNotificationCallbacks and
       // done mPBOverride logic by this point.
       chooseAppCache = NS_ShouldCheckAppCache(principal);
 
       appCacheChan->SetChooseApplicationCache(chooseAppCache);
     }
   }
 
-  mChannel->SetRequestContextID(aRequestContextID);
+  httpChannel->SetRequestContextID(aRequestContextID);
+
+  // Store the strong reference of channel and parent listener object until
+  // all the initialization procedure is complete without failure, to remove
+  // cycle reference in fail case and to avoid memory leakage.
+  mChannel = httpChannel.forget();
+  mParentListener = parentListener.forget();
+  mChannel->SetNotificationCallbacks(mParentListener);
+
 
   mSuspendAfterSynthesizeResponse = aSuspendAfterSynthesizeResponse;
 
-  if (!delayAsyncOpen) {
-    InvokeAsyncOpen(NS_OK);
+  MOZ_ASSERT(!mBgParent);
+  MOZ_ASSERT(mPromise.IsEmpty());
+  // Wait for HttpBackgrounChannel to continue the async open procedure.
+  ++mAsyncOpenBarrier;
+  RefPtr<GenericPromise> promise = WaitForBgParent();
+  RefPtr<HttpChannelParent> self = this;
+  promise->Then(AbstractThread::MainThread(), __func__,
+                [self]() {
+                  self->mRequest.Complete();
+                  self->TryInvokeAsyncOpen(NS_OK);
+                },
+                [self](nsresult aStatus) {
+                  self->mRequest.Complete();
+                  self->TryInvokeAsyncOpen(aStatus);
+                })
+         ->Track(mRequest);
+
+  return true;
+}
+
+already_AddRefed<GenericPromise>
+HttpChannelParent::WaitForBgParent()
+{
+  LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
+  MOZ_ASSERT(!mBgParent);
+  MOZ_ASSERT(mChannel);
+
+
+  nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
+    do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID);
+  MOZ_ASSERT(registrar);
+  registrar->LinkHttpChannel(mChannel->ChannelId(), this);
+
+  if (mBgParent) {
+    already_AddRefed<GenericPromise> promise = mPromise.Ensure(__func__);
+    // resolve promise immediatedly if bg channel is ready.
+    mPromise.Resolve(true, __func__);
+    return promise;
   }
 
-  return true;
+  return mPromise.Ensure(__func__);;
 }
 
 bool
 HttpChannelParent::ConnectChannel(const uint32_t& registrarId, const bool& shouldIntercept)
 {
   nsresult rv;
 
   LOG(("HttpChannelParent::ConnectChannel: Looking for a registered channel "
@@ -642,16 +811,30 @@ HttpChannelParent::ConnectChannel(const 
   if (mPBOverride != kPBOverride_Unset) {
     // redirected-to channel may not support PB
     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
     if (pbChannel) {
       pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
     }
   }
 
+  MOZ_ASSERT(!mBgParent);
+  MOZ_ASSERT(mPromise.IsEmpty());
+  // Waiting for background channel
+  RefPtr<GenericPromise> promise = WaitForBgParent();
+  RefPtr<HttpChannelParent> self = this;
+  promise->Then(AbstractThread::MainThread(), __func__,
+                [self]() {
+                  self->mRequest.Complete();
+                },
+                [self](const nsresult& aResult) {
+                  NS_ERROR("failed to establish the background channel");
+                  self->mRequest.Complete();
+                })
+         ->Track(mRequest);
   return true;
 }
 
 mozilla::ipc::IPCResult
 HttpChannelParent::RecvSetPriority(const int16_t& priority)
 {
   LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n",
        this, priority));
@@ -798,50 +981,123 @@ HttpChannelParent::RecvRedirect2Verify(c
       nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(newHttpChannel);
       if (appCacheChannel) {
         appCacheChannel->SetChooseApplicationCache(aChooseAppcache);
       }
     }
   }
 
+  // Continue the verification procedure if child has veto the redirection.
+  if (NS_FAILED(result)) {
+    ContinueRedirect2Verify(result);
+    return IPC_OK();
+  }
+
+  // Wait for background channel ready on target channel
+  nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
+    do_GetService(NS_REDIRECTCHANNELREGISTRAR_CONTRACTID);
+  MOZ_ASSERT(redirectReg);
+
+  nsCOMPtr<nsIParentChannel> redirectParentChannel;
+  rv = redirectReg->GetParentChannel(mRedirectRegistrarId,
+                                     getter_AddRefs(redirectParentChannel));
+  MOZ_ASSERT(redirectParentChannel);
+  if (!redirectParentChannel) {
+    ContinueRedirect2Verify(rv);
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
+    do_QueryInterface(redirectParentChannel);
+  if (!redirectedParent) {
+    // Continue verification procedure if redirecting to non-Http protocol
+    ContinueRedirect2Verify(result);
+    return IPC_OK();
+  }
+
+  // Ask redirected channel if verification can proceed.
+  // ContinueRedirect2Verify will be invoked when redirected channel is ready.
+  redirectedParent->ContinueVerification(this);
+
+  return IPC_OK();
+}
+
+// from nsIParentRedirectingChannel
+NS_IMETHODIMP
+HttpChannelParent::ContinueVerification(nsIAsyncVerifyRedirectReadyCallback* aCallback)
+{
+  LOG(("HttpChannelParent::ContinueVerification [this=%p callback=%p]\n",
+       this, aCallback));
+
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aCallback);
+
+  // Continue the verification procedure if background channel is ready.
+  if (mBgParent) {
+    aCallback->ReadyToVerify(NS_OK);
+    return NS_OK;
+  }
+
+  // ConnectChannel must be received before Redirect2Verify.
+  MOZ_ASSERT(!mPromise.IsEmpty());
+
+  // Otherwise, wait for the background channel.
+  RefPtr<GenericPromise> promise = WaitForBgParent();
+  nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
+  promise->Then(AbstractThread::MainThread(), __func__,
+                [callback]() {
+                  callback->ReadyToVerify(NS_OK);
+                },
+                [callback](const nsresult& aResult) {
+                  NS_ERROR("failed to establish the background channel");
+                  callback->ReadyToVerify(aResult);
+                });
+  return NS_OK;
+}
+
+void
+HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult)
+{
+  LOG(("HttpChannelParent::ContinueRedirect2Verify [this=%p result=%" PRIx32 "]\n",
+       this, static_cast<uint32_t>(aResult)));
+
   if (!mRedirectCallback) {
     // This should according the logic never happen, log the situation.
     if (mReceivedRedirect2Verify)
       LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
     if (mSentRedirect1BeginFailed)
       LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
-    if (mSentRedirect1Begin && NS_FAILED(result))
+    if ((mRedirectRegistrarId > 0) && NS_FAILED(aResult))
       LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
-    if (mSentRedirect1Begin && NS_SUCCEEDED(result))
+    if ((mRedirectRegistrarId > 0) && NS_SUCCEEDED(aResult))
       LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
     if (!mRedirectChannel)
       LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
 
     NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
              "mRedirectCallback null");
   }
 
   mReceivedRedirect2Verify = true;
 
   if (mRedirectCallback) {
-    LOG(("HttpChannelParent::RecvRedirect2Verify call OnRedirectVerifyCallback"
+    LOG(("HttpChannelParent::ContinueRedirect2Verify call OnRedirectVerifyCallback"
          " [this=%p result=%" PRIx32 ", mRedirectCallback=%p]\n",
-         this, static_cast<uint32_t>(result), mRedirectCallback.get()));
-    mRedirectCallback->OnRedirectVerifyCallback(result);
+         this, static_cast<uint32_t>(aResult), mRedirectCallback.get()));
+    mRedirectCallback->OnRedirectVerifyCallback(aResult);
     mRedirectCallback = nullptr;
   }
-
-  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 HttpChannelParent::RecvDocumentChannelCleanup()
 {
   // From now on only using mAssociatedContentSecurity.  Free everything else.
+  CleanupBackgroundChannel(); // Background channel can be closed.
   mChannel = nullptr;          // Reclaim some memory sooner.
   mCacheEntry = nullptr;  // Else we'll block other channels reading same URI
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
 {
@@ -1123,16 +1379,17 @@ HttpChannelParent::RecvRemoveCorsPreflig
 // HttpChannelParent::nsIRequestObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
   LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n",
        this, aRequest));
+  MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_RELEASE_ASSERT(!mDivertingFromChild,
     "Cannot call OnStartRequest if diverting is set!");
 
   RefPtr<nsHttpChannel> chan = do_QueryObject(aRequest);
   if (!chan) {
     LOG(("  aRequest is not nsHttpChannel"));
     NS_ERROR("Expecting only nsHttpChannel as aRequest in HttpChannelParent::OnStartRequest");
@@ -1231,26 +1488,38 @@ HttpChannelParent::OnStartRequest(nsIReq
                           redirectCount,
                           cacheKeyValue,
                           altDataType,
                           altDataLen))
   {
     rv = NS_ERROR_UNEXPECTED;
   }
   requestHead->Exit();
+
+  // OnStartRequest is sent to content process successfully.
+  // Notify PHttpBackgroundChannelChild that all following IPC mesasges
+  // should be run after OnStartRequest is handled.
+  if (NS_SUCCEEDED(rv)) {
+    MOZ_ASSERT(mBgParent);
+    if (!mBgParent->OnStartRequestSent()) {
+      rv = NS_ERROR_UNEXPECTED;
+    }
+  }
+
   return rv;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
                                  nsISupports *aContext,
                                  nsresult aStatusCode)
 {
   LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%" PRIx32 "]\n",
        this, aRequest, static_cast<uint32_t>(aStatusCode)));
+  MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_RELEASE_ASSERT(!mDivertingFromChild,
     "Cannot call OnStopRequest if diverting is set!");
   ResourceTimingStruct timing;
   mChannel->GetDomainLookupStart(&timing.domainLookupStart);
   mChannel->GetDomainLookupEnd(&timing.domainLookupEnd);
   mChannel->GetConnectStart(&timing.connectStart);
   mChannel->GetConnectEnd(&timing.connectEnd);
@@ -1264,34 +1533,42 @@ HttpChannelParent::OnStopRequest(nsIRequ
   mChannel->GetEncodedBodySize(&timing.encodedBodySize);
   // decodedBodySize can be computed in the child process so it doesn't need
   // to be passed down.
   mChannel->GetProtocolVersion(timing.protocolVersion);
 
   mChannel->GetCacheReadStart(&timing.cacheReadStart);
   mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
 
-  if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
+  // Either IPC channel is closed or background channel
+  // is ready to send OnStopRequest.
+  MOZ_ASSERT(mIPCClosed || mBgParent);
+
+  if (mIPCClosed ||
+      !mBgParent || !mBgParent->OnStopRequest(aStatusCode, timing)) {
     return NS_ERROR_UNEXPECTED;
+  }
+
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnDataAvailable(nsIRequest *aRequest,
                                    nsISupports *aContext,
                                    nsIInputStream *aInputStream,
                                    uint64_t aOffset,
                                    uint32_t aCount)
 {
-  LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p]\n",
-       this, aRequest));
+  LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p offset=%" PRIu64
+       " count=%" PRIu32 "]\n", this, aRequest, aOffset, aCount));
+  MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_RELEASE_ASSERT(!mDivertingFromChild,
     "Cannot call OnDataAvailable if diverting is set!");
 
   nsresult channelStatus = NS_OK;
   mChannel->GetStatus(&channelStatus);
 
   nsresult transportStatus =
@@ -1308,18 +1585,23 @@ HttpChannelParent::OnDataAvailable(nsIRe
   }
 
   while (aCount) {
     nsresult rv = NS_ReadInputStreamToString(aInputStream, data, toRead);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
-    if (mIPCClosed || !SendOnTransportAndData(channelStatus, transportStatus,
-                                              aOffset, toRead, data)) {
+    // Either IPC channel is closed or background channel
+    // is ready to send OnTransportAndData.
+    MOZ_ASSERT(mIPCClosed || mBgParent);
+
+    if (mIPCClosed || !mBgParent ||
+        !mBgParent->OnTransportAndData(channelStatus, transportStatus,
+                                       aOffset, toRead, data)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     aOffset += toRead;
     aCount -= toRead;
     toRead = std::min<uint32_t>(aCount, kCopyChunkSize);
   }
 
@@ -1331,50 +1613,67 @@ HttpChannelParent::OnDataAvailable(nsIRe
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnProgress(nsIRequest *aRequest,
                               nsISupports *aContext,
                               int64_t aProgress,
                               int64_t aProgressMax)
 {
+  LOG(("HttpChannelParent::OnStatus [this=%p progress=%" PRId64 "max=%" PRId64
+       "]\n", this, aProgress, aProgressMax));
+  MOZ_ASSERT(NS_IsMainThread());
+
   // If it indicates this precedes OnDataAvailable, child can derive the value in ODA.
   if (mIgnoreProgress) {
     mIgnoreProgress = false;
     return NS_OK;
   }
 
+  // Either IPC channel is closed or background channel
+  // is ready to send OnProgress.
+  MOZ_ASSERT(mIPCClosed || mBgParent);
+
   // Send OnProgress events to the child for data upload progress notifications
   // (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
   // LOAD_BACKGROUND set.
-  if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax)) {
+  if (mIPCClosed || !mBgParent
+      || !mBgParent->OnProgress(aProgress, aProgressMax)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::OnStatus(nsIRequest *aRequest,
                             nsISupports *aContext,
                             nsresult aStatus,
                             const char16_t *aStatusArg)
 {
+  LOG(("HttpChannelParent::OnStatus [this=%p status=%" PRIx32 "]\n",
+       this, static_cast<uint32_t>(aStatus)));
+  MOZ_ASSERT(NS_IsMainThread());
+
   // If this precedes OnDataAvailable, transportStatus will be derived in ODA.
   if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
       aStatus == NS_NET_STATUS_READING) {
     // The transport status and progress generated by ODA will be coalesced
     // into one IPC message. Therefore, we can ignore the next OnProgress event
     // since it is generated by ODA as well.
     mIgnoreProgress = true;
     return NS_OK;
   }
 
+  // Either IPC channel is closed or background channel
+  // is ready to send OnStatus.
+  MOZ_ASSERT(mIPCClosed || mBgParent);
+
   // Otherwise, send to child now
-  if (mIPCClosed || !SendOnStatus(aStatus)) {
+  if (mIPCClosed || !mBgParent || !mBgParent->OnStatus(aStatus)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIParentChannel
@@ -1391,42 +1690,45 @@ HttpChannelParent::SetParentListener(Htt
                                "mParentListener is null.");
   mParentListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::NotifyTrackingProtectionDisabled()
 {
-  if (!mIPCClosed)
-    Unused << SendNotifyTrackingProtectionDisabled();
+  LOG(("HttpChannelParent::NotifyTrackingProtectionDisabled [this=%p]\n", this));
+  if (!mIPCClosed) {
+    MOZ_ASSERT(mBgParent);
+    Unused << mBgParent->OnNotifyTrackingProtectionDisabled();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                             const nsACString& aProvider,
                                             const nsACString& aPrefix)
 {
+  LOG(("HttpChannelParent::SetClassifierMatchedInfo [this=%p]\n", this));
   if (!mIPCClosed) {
-    ClassifierInfo info;
-    info.list() = aList;
-    info.prefix() = aPrefix;
-    info.provider() = aProvider;
-
-    Unused << SendSetClassifierMatchedInfo(info);
+    MOZ_ASSERT(mBgParent);
+    Unused << mBgParent->OnSetClassifierMatchedInfo(aList, aProvider, aPrefix);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::NotifyTrackingResource()
 {
-  if (!mIPCClosed)
-    Unused << SendNotifyTrackingResource();
+  LOG(("HttpChannelParent::NotifyTrackingResource [this=%p]\n", this));
+  if (!mIPCClosed) {
+    MOZ_ASSERT(mBgParent);
+    Unused << mBgParent->OnNotifyTrackingResource();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::Delete()
 {
   if (!mIPCClosed)
     Unused << DoSendDeleteSelf();
@@ -1482,17 +1784,19 @@ HttpChannelParent::StartRedirect(uint32_
   }
   if (!result) {
     // Bug 621446 investigation
     mSentRedirect1BeginFailed = true;
     return NS_BINDING_ABORTED;
   }
 
   // Bug 621446 investigation
-  mSentRedirect1Begin = true;
+  // Store registrar Id of the new channel to find the redirect
+  // HttpChannelParent later in verification phase.
+  mRedirectRegistrarId = registrarId;
 
   // Result is handled in RecvRedirect2Verify above
 
   mRedirectChannel = newChannel;
   mRedirectCallback = callback;
   return NS_OK;
 }
 
@@ -1699,24 +2003,21 @@ HttpChannelParent::StartDiversion()
     mDivertListener = converterListener.forget();
   }
 
   // Now mParentListener can be diverted to mDivertListener.
   DebugOnly<nsresult> rvdbg = mParentListener->DivertTo(mDivertListener);
   MOZ_ASSERT(NS_SUCCEEDED(rvdbg));
   mDivertListener = nullptr;
 
-  if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
-    FailDiversion(NS_ERROR_UNEXPECTED);
-    return;
-  }
+  // Either IPC channel is closed or background channel
+  // is ready to send FlushedForDiversion and DivertMessages.
+  MOZ_ASSERT(mIPCClosed || mBgParent);
 
-  // The listener chain should now be setup; tell HttpChannelChild to divert
-  // the OnDataAvailables and OnStopRequest to this HttpChannelParent.
-  if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
+  if (NS_WARN_IF(mIPCClosed || !mBgParent || !mBgParent->OnDiversion())) {
     FailDiversion(NS_ERROR_UNEXPECTED);
     return;
   }
 }
 
 class HTTPFailDiversionEvent : public Runnable
 {
 public:
@@ -1784,22 +2085,26 @@ HttpChannelParent::NotifyDiversionFailed
     mParentListener->OnStartRequest(mChannel, nullptr);
     mChannel->ForcePending(false);
   }
   // If the channel is pending, it will call OnStopRequest itself; otherwise, do
   // it here.
   if (!isPending) {
     mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
   }
-  mParentListener = nullptr;
-  mChannel = nullptr;
 
   if (!mIPCClosed) {
     Unused << DoSendDeleteSelf();
   }
+
+  // DoSendDeleteSelf will need channel Id to remove the strong reference in
+  // BackgroundChannelRegistrar if channel pairing is aborted.
+  // Thus we need to keep mChannel until DoSendDeleteSelf is done.
+  mParentListener = nullptr;
+  mChannel = nullptr;
 }
 
 nsresult
 HttpChannelParent::OpenAlternativeOutputStream(const nsACString & type, nsIOutputStream * *_retval)
 {
   // We need to make sure the child does not call SendDocumentChannelCleanup()
   // before opening the altOutputStream, because that clears mCacheEntry.
   if (!mCacheEntry) {
@@ -1832,16 +2137,19 @@ HttpChannelParent::UpdateAndSerializeSec
   }
 }
 
 bool
 HttpChannelParent::DoSendDeleteSelf()
 {
   bool rv = SendDeleteSelf();
   mIPCClosed = true;
+
+  CleanupBackgroundChannel();
+
   return rv;
 }
 
 mozilla::ipc::IPCResult
 HttpChannelParent::RecvDeletingChannel()
 {
   // We need to ensure that the parent channel will not be sending any more IPC
   // messages after this, as the child is going away. DoSendDeleteSelf will
@@ -1882,16 +2190,32 @@ HttpChannelParent::ReportSecurityMessage
 
 NS_IMETHODIMP
 HttpChannelParent::IssueWarning(uint32_t aWarning, bool aAsError)
 {
   Unused << SendIssueDeprecationWarning(aWarning, aAsError);
   return NS_OK;
 }
 
+//-----------------------------------------------------------------------------
+// nsIAsyncVerifyRedirectReadyCallback
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+HttpChannelParent::ReadyToVerify(nsresult aResult)
+{
+  LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
+       this, static_cast<uint32_t>(aResult)));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ContinueRedirect2Verify(aResult);
+
+  return NS_OK;
+}
+
 void
 HttpChannelParent::DoSendSetPriority(int16_t aValue)
 {
   if (!mIPCClosed) {
     Unused << SendSetPriority(aValue);
   }
 }
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -8,16 +8,17 @@
 #ifndef mozilla_net_HttpChannelParent_h
 #define mozilla_net_HttpChannelParent_h
 
 #include "ADivertableParentChannel.h"
 #include "nsHttp.h"
 #include "mozilla/net/PHttpChannelParent.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/MozPromise.h"
 #include "nsIObserver.h"
 #include "nsIParentRedirectingChannel.h"
 #include "nsIProgressEventSink.h"
 #include "nsHttpChannel.h"
 #include "nsIAuthPromptProvider.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "nsIDeprecationWarner.h"
 
@@ -32,44 +33,47 @@ namespace mozilla {
 
 namespace dom{
 class TabParent;
 class PBrowserOrId;
 } // namespace dom
 
 namespace net {
 
+class HttpBackgroundChannelParent;
 class HttpChannelParentListener;
 class ChannelEventQueue;
 
 // Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
 // works correctly on this object, as it's needed to compute a void* pointing to
 // the beginning of this object.
 
 class HttpChannelParent final : public nsIInterfaceRequestor
                               , public PHttpChannelParent
                               , public nsIParentRedirectingChannel
                               , public nsIProgressEventSink
                               , public ADivertableParentChannel
                               , public nsIAuthPromptProvider
                               , public nsIDeprecationWarner
                               , public HttpChannelSecurityWarningReporter
+                              , public nsIAsyncVerifyRedirectReadyCallback
 {
   virtual ~HttpChannelParent();
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIPARENTCHANNEL
   NS_DECL_NSIPARENTREDIRECTINGCHANNEL
   NS_DECL_NSIPROGRESSEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSIDEPRECATIONWARNER
+  NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
 
   NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)
 
   HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
                     nsILoadContext* aLoadContext,
                     PBOverrideStatus aStatus);
 
   MOZ_MUST_USE bool Init(const HttpChannelCreationArgs& aOpenArgs);
@@ -94,20 +98,32 @@ public:
     if (mChannel) {
       mChannel->SetApplyConversion(aApplyConversion);
     }
   }
 
   MOZ_MUST_USE nsresult OpenAlternativeOutputStream(const nsACString & type,
                                                     nsIOutputStream * *_retval);
 
+  // Callbacks for each asynchronous tasks required in AsyncOpen
+  // procedure, will call InvokeAsyncOpen when all the expected
+  // tasks is finished successfully or when any failure happened.
+  // @see mAsyncOpenBarrier.
+  void TryInvokeAsyncOpen(nsresult aRv);
+
   void InvokeAsyncOpen(nsresult rv);
 
   // Calls SendSetPriority if mIPCClosed is false.
   void DoSendSetPriority(int16_t aValue);
+
+  // Callback while background channel is ready.
+  void OnBackgroundParentReady(HttpBackgroundChannelParent* aBgParent);
+  // Callback while background channel is destroyed.
+  void OnBackgroundParentDestroyed();
+
 protected:
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during redirects.
   MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                    const bool& shouldIntercept);
 
   MOZ_MUST_USE bool
   DoAsyncOpen(const URIParams&           uri,
@@ -212,16 +228,36 @@ private:
   void DivertOnDataAvailable(const nsCString& data,
                              const uint64_t& offset,
                              const uint32_t& count);
   void DivertOnStopRequest(const nsresult& statusCode);
   void DivertComplete();
   void MaybeFlushPendingDiversion();
   void ResponseSynthesized();
 
+  // final step for Redirect2Verify procedure, will be invoked while both
+  // redirecting and redirected channel are ready or any error happened.
+  // OnRedirectVerifyCallback will be invoked for finishing the async
+  // redirect verification procedure.
+  void ContinueRedirect2Verify(const nsresult& aResult);
+
+  void AsyncOpenFailed(nsresult aRv);
+
+  // Request to pair with a HttpBackgroundChannelParent with the same channel
+  // id, a promise will be returned so the caller can append callbacks on it.
+  // If called multiple times before mBgParent is available, the same promise
+  // will be returned and the callbacks will be invoked in order.
+  already_AddRefed<GenericPromise> WaitForBgParent();
+
+  // Remove the association with background channel after main-thread IPC
+  // is about to be destroyed or no further event is going to be sent, i.e.,
+  // DocumentChannelCleanup.
+  void CleanupBackgroundChannel();
+
+  friend class HttpBackgroundChannelParent;
   friend class DivertDataAvailableEvent;
   friend class DivertStopRequestEvent;
   friend class DivertCompleteEvent;
 
   RefPtr<nsHttpChannel>       mChannel;
   nsCOMPtr<nsICacheEntry>       mCacheEntry;
   nsCOMPtr<nsIAssociatedContentSecurity>  mAssociatedContentSecurity;
   bool mIPCClosed;                // PHttpChannel actor has been Closed()
@@ -231,17 +267,16 @@ private:
 
   nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;
 
   // OnStatus is always called before OnProgress.
   // Set true in OnStatus if next OnProgress can be ignored
   // since the information can be recontructed from ODA.
   bool mIgnoreProgress              : 1;
 
-  bool mSentRedirect1Begin          : 1;
   bool mSentRedirect1BeginFailed    : 1;
   bool mReceivedRedirect2Verify     : 1;
 
   PBOverrideStatus mPBOverride;
 
   nsCOMPtr<nsILoadContext> mLoadContext;
   RefPtr<nsHttpHandler>  mHttpHandler;
 
@@ -267,16 +302,30 @@ private:
   // Set if this channel should be suspended after synthesizing a response.
   bool mSuspendAfterSynthesizeResponse;
   // Set if this channel will synthesize its response.
   bool mWillSynthesizeResponse;
 
   dom::TabId mNestedFrameId;
 
   RefPtr<ChannelEventQueue> mEventQ;
+
+  RefPtr<HttpBackgroundChannelParent> mBgParent;
+
+  // Number of events to wait before actually invoking AsyncOpen on the main
+  // channel. For each asynchronous step required before InvokeAsyncOpen, should
+  // increase 1 to mAsyncOpenBarrier and invoke TryInvokeAsyncOpen after
+  // finished. This attribute is main thread only.
+  uint8_t mAsyncOpenBarrier = 0;
+
+  // Corresponding redirect channel registrar Id. 0 means redirection is not started.
+  uint32_t mRedirectRegistrarId = 0;
+
+  MozPromiseHolder<GenericPromise> mPromise;
+  MozPromiseRequestHolder<GenericPromise> mRequest;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
                               HTTP_CHANNEL_PARENT_IID)
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/HttpChannelParentListener.cpp
+++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/Unused.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsIHttpEventSink.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIRedirectChannelRegistrar.h"
 #include "nsIPromptFactory.h"
+#include "nsIWindowWatcher.h"
 #include "nsQueryObject.h"
 
 using mozilla::Unused;
 
 namespace mozilla {
 namespace net {
 
 HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel)
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackground;
+include NeckoChannelParams;
+include PURLClassifierInfo;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
+
+namespace mozilla {
+namespace net {
+
+//-------------------------------------------------------------------
+async protocol PHttpBackgroundChannel
+{
+  manager PBackground;
+
+child:
+  // OnStartRequest is sent over main thread IPC. The following
+  // OnTransportAndData/OnStopRequest/OnProgress/OnStatus/FlushForDiversion/
+  // DivertMessages needs to wait in content process until OnStartRequest
+  // is processed. For synchronizing the event sequence.
+  async OnStartRequestSent();
+
+  // Combines a single OnDataAvailable and its associated OnProgress &
+  // OnStatus calls into one IPDL message
+  async OnTransportAndData(nsresult  channelStatus,
+                           nsresult  transportStatus,
+                           uint64_t  offset,
+                           uint32_t  count,
+                           nsCString data);
+
+  async OnStopRequest(nsresult channelStatus, ResourceTimingStruct timing);
+
+  async OnProgress(int64_t progress, int64_t progressMax);
+
+  async OnStatus(nsresult status);
+
+  // Parent has been suspended for diversion; no more events to be enqueued.
+  async FlushedForDiversion();
+
+  // Child should resume processing the ChannelEventQueue, i.e. diverting any
+  // OnDataAvailable and OnStopRequest messages in the queue back to the parent.
+  async DivertMessages();
+
+  // Tell the child that tracking protection was disabled for this load.
+  async NotifyTrackingProtectionDisabled();
+
+  // Tell the child that the resource being loaded is on the tracking
+  // protection list.
+  async NotifyTrackingResource();
+
+  // Tell the child information of matched URL againts SafeBrowsing list
+  async SetClassifierMatchedInfo(ClassifierInfo info);
+
+  async __delete__();
+
+};
+
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -5,17 +5,16 @@
  * 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 PNecko;
 include InputStreamParams;
 include URIParams;
 include PBackgroundSharedTypes;
 include NeckoChannelParams;
-include PURLClassifierInfo;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
 
 namespace mozilla {
@@ -104,30 +103,16 @@ child:
                        nsCString           securityInfoSerialization,
                        NetAddr             selfAddr,
                        NetAddr             peerAddr,
                        int16_t             redirectCount,
                        uint32_t            cacheKey,
                        nsCString           altDataType,
                        int64_t             altDataLength);
 
-  // Combines a single OnDataAvailable and its associated OnProgress &
-  // OnStatus calls into one IPDL message
-  async OnTransportAndData(nsresult  channelStatus,
-                           nsresult  transportStatus,
-                           uint64_t  offset,
-                           uint32_t  count,
-                           nsCString data);
-
-  async OnStopRequest(nsresult channelStatus, ResourceTimingStruct timing);
-
-  async OnProgress(int64_t progress, int64_t progressMax);
-
-  async OnStatus(nsresult status);
-
   // Used to cancel child channel if we hit errors during creating and
   // AsyncOpen of nsHttpChannel on the parent.
   async FailedAsyncOpen(nsresult status);
 
   // Called to initiate content channel redirect, starts talking to sinks
   // on the content process and reports result via Redirect2Verify above
   async Redirect1Begin(uint32_t           registrarId,
                        URIParams          newUri,
@@ -139,44 +124,27 @@ child:
 
   // Called if redirect successful so that child can complete setup.
   async Redirect3Complete();
 
   // Associate the child with an application ids
   async AssociateApplicationCache(nsCString groupID,
                                   nsCString clientID);
 
-  // Tell the child that tracking protection was disabled for this load.
-  async NotifyTrackingProtectionDisabled();
-
-  // Tell the child that the resource being loaded is on the tracking
-  // protection list.
-  async NotifyTrackingResource();
-
-  // Parent has been suspended for diversion; no more events to be enqueued.
-  async FlushedForDiversion();
-
-  // Child should resume processing the ChannelEventQueue, i.e. diverting any
-  // OnDataAvailable and OnStopRequest messages in the queue back to the parent.
-  async DivertMessages();
-
   // Report a security message to the console associated with this
   // channel.
   async ReportSecurityMessage(nsString messageTag, nsString messageCategory);
 
   // Tell child to delete channel (all IPDL deletes must be done from child to
   // avoid races: see bug 591708).
   async DeleteSelf();
 
   // Tell the child to issue a deprecation warning.
   async IssueDeprecationWarning(uint32_t warning, bool asError);
 
-  // Tell the child information of matched URL againts SafeBrowsing list
-  async SetClassifierMatchedInfo(ClassifierInfo info);
-
 both:
   // After receiving this message, the parent also calls
   // SendFinishInterceptedRedirect, and makes sure not to send any more messages
   // after that. When receiving this message, the child will call
   // Send__delete__() and complete the steps required to finish the redirect.
   async FinishInterceptedRedirect();
 
   async SetPriority(int16_t priority);
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -3,16 +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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Networking: HTTP')
 
 XPIDL_SOURCES += [
+    'nsIBackgroundChannelRegistrar.idl',
     'nsIHstsPrimingCallback.idl',
     'nsIHttpActivityObserver.idl',
     'nsIHttpAuthenticableChannel.idl',
     'nsIHttpAuthenticator.idl',
     'nsIHttpAuthManager.idl',
     'nsIHttpChannel.idl',
     'nsIHttpChannelAuthProvider.idl',
     'nsIHttpChannelChild.idl',
@@ -34,16 +35,18 @@ EXPORTS += [
     'nsHttpRequestHead.h',
     'nsHttpResponseHead.h',
 ]
 
 EXPORTS.mozilla.net += [
     'AltDataOutputStreamChild.h',
     'AltDataOutputStreamParent.h',
     'HttpAuthUtils.h',
+    'HttpBackgroundChannelChild.h',
+    'HttpBackgroundChannelParent.h',
     'HttpBaseChannel.h',
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'NullHttpChannel.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
@@ -53,24 +56,27 @@ SOURCES += [
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
     'AltDataOutputStreamParent.cpp',
     'AlternateServices.cpp',
     'ASpdySession.cpp',
+    'BackgroundChannelRegistrar.cpp',
     'CacheControlParser.cpp',
     'ConnectionDiagnostics.cpp',
     'HSTSPrimerListener.cpp',
     'Http2Compression.cpp',
     'Http2Push.cpp',
     'Http2Session.cpp',
     'Http2Stream.cpp',
     'HttpAuthUtils.cpp',
+    'HttpBackgroundChannelChild.cpp',
+    'HttpBackgroundChannelParent.cpp',
     'HttpBaseChannel.cpp',
     'HttpChannelChild.cpp',
     'HttpChannelParent.cpp',
     'HttpChannelParentListener.cpp',
     'HttpInfo.cpp',
     'InterceptedChannel.cpp',
     'nsCORSListenerProxy.cpp',
     'nsHttp.cpp',
@@ -96,16 +102,17 @@ UNIFIED_SOURCES += [
 
 # These files cannot be built in unified mode because of OS X headers.
 SOURCES += [
     'nsHttpHandler.cpp',
 ]
 
 IPDL_SOURCES += [
     'PAltDataOutputStream.ipdl',
+    'PHttpBackgroundChannel.ipdl',
     'PHttpChannel.ipdl',
 ]
 
 EXTRA_JS_MODULES += [
     'UserAgentOverrides.jsm',
     'UserAgentUpdates.jsm',
 ]
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -59,16 +59,17 @@
 #include "mozilla/Preferences.h"
 #include "nsISSLSocketControl.h"
 #include "sslt.h"
 #include "nsContentUtils.h"
 #include "nsContentSecurityManager.h"
 #include "nsIClassOfService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISSLStatus.h"
 #include "nsISSLStatusProvider.h"
 #include "nsITransportSecurityInfo.h"
 #include "nsIWebProgressListener.h"
 #include "LoadContextInfo.h"
 #include "netCore.h"
 #include "nsHttpTransaction.h"
--- a/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
+++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
@@ -1,20 +1,23 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
+
 #include <errno.h>
 #include "nsHttpChunkedDecoder.h"
 #include <algorithm>
 #include "plstr.h"
 
+#include "mozilla/Unused.h"
+
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // nsHttpChunkedDecoder <public>
 //-----------------------------------------------------------------------------
 
 nsresult
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -28,16 +28,17 @@
 #include "NullHttpTransaction.h"
 #include "nsIDNSRecord.h"
 #include "nsITransport.h"
 #include "nsInterfaceRequestorAgg.h"
 #include "nsIRequestContext.h"
 #include "nsISocketTransportService.h"
 #include <algorithm>
 #include "mozilla/ChaosMode.h"
+#include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Unused.h"
 #include "nsIURI.h"
 
 #include "mozilla/Move.h"
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 namespace net {
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/nsIBackgroundChannelRegistrar.idl
@@ -0,0 +1,63 @@
+/* -*- 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 "nsISupports.idl"
+
+%{ C++
+namespace mozilla {
+namespace net {
+class HttpBackgroundChannelParent;
+class HttpChannelParent;
+}
+}
+%}
+
+[ptr] native HttpChannelParent(mozilla::net::HttpChannelParent);
+[ptr] native HttpBackgroundChannelParent(mozilla::net::HttpBackgroundChannelParent);
+
+/*
+ * Registrar for pairing HttpChannelParent and HttpBackgroundChannelParent via
+ * channel Id. HttpChannelParent::OnBackgroundParentReady and
+ * HttpBackgroundChannelParent::LinkToChannel will be invoked to notify the
+ * existence of associated channel object.
+ */
+[builtinclass, uuid(8acaa9b1-f0c4-4ade-baeb-39b0d4b96e5b)]
+interface nsIBackgroundChannelRegistrar : nsISupports
+{
+  /*
+   * Link the provided channel parent actor with the given channel Id.
+   * callbacks will be invoked immediately when the HttpBackgroundChannelParent
+   * associated with the same channel Id is found. Store the HttpChannelParent
+   * until a matched linkBackgroundChannel is invoked.
+   *
+   * @param aKey the channel Id
+   * @param aChannel the channel parent actor to be paired
+   */
+  [noscript,notxpcom,nostdcall] void linkHttpChannel(in uint64_t aKey,
+                                                     in HttpChannelParent aChannel);
+
+  /*
+   * Link the provided background channel with the given channel Id.
+   * callbacks will be invoked immediately when the HttpChannelParent associated
+   * with the same channel Id is found. Store the HttpBackgroundChannelParent
+   * until a matched linkHttpChannel is invoked.
+   *
+   * @param aKey the channel Id
+   * @param aBgChannel the background channel to be paired
+   */
+  [noscript,notxpcom,nostdcall] void linkBackgroundChannel(in uint64_t aKey,
+                                                           in HttpBackgroundChannelParent aBgChannel);
+
+  /*
+   * Delete previous stored HttpChannelParent or HttpBackgroundChannelParent
+   * if no need to wait for the paired channel object, e.g. background channel
+   * is destroyed before pairing is completed.
+   *
+   * @param aKey the channel Id
+   */
+  [noscript,notxpcom,nostdcall] void deleteChannel(in uint64_t aKey);
+
+};
--- a/toolkit/components/extensions/ext-privacy.js
+++ b/toolkit/components/extensions/ext-privacy.js
@@ -55,16 +55,26 @@ ExtensionPreferencesManager.addSetting("
       "network.http.speculative-parallel-limit": value ? undefined : 0,
       "network.dns.disablePrefetch": !value,
       "network.predictor.enabled": value,
       "network.prefetch-next": value,
     };
   },
 });
 
+ExtensionPreferencesManager.addSetting("network.peerConnectionEnabled", {
+  prefNames: [
+    "media.peerconnection.enabled",
+  ],
+
+  setCallback(value) {
+    return {[this.prefNames[0]]: value};
+  },
+});
+
 ExtensionPreferencesManager.addSetting("network.webRTCIPHandlingPolicy", {
   prefNames: [
     "media.peerconnection.ice.default_address_only",
     "media.peerconnection.ice.no_host",
     "media.peerconnection.ice.proxy_only",
   ],
 
   setCallback(value) {
@@ -114,16 +124,21 @@ this.privacy = class extends ExtensionAP
           networkPredictionEnabled: getAPI(extension,
             "network.networkPredictionEnabled",
             () => {
               return Preferences.get("network.predictor.enabled") &&
                 Preferences.get("network.prefetch-next") &&
                 Preferences.get("network.http.speculative-parallel-limit") > 0 &&
                 !Preferences.get("network.dns.disablePrefetch");
             }),
+          peerConnectionEnabled: getAPI(extension,
+            "network.peerConnectionEnabled",
+            () => {
+              return Preferences.get("media.peerconnection.enabled");
+            }),
           webRTCIPHandlingPolicy: getAPI(extension,
             "network.webRTCIPHandlingPolicy",
             () => {
               if (Preferences.get("media.peerconnection.ice.proxy_only")) {
                 return "disable_non_proxied_udp";
               }
 
               let default_address_only =
--- a/toolkit/components/extensions/schemas/privacy.json
+++ b/toolkit/components/extensions/schemas/privacy.json
@@ -33,16 +33,20 @@
         "description": "The IP handling policy of WebRTC."
       }
     ],
     "properties": {
       "networkPredictionEnabled": {
         "$ref": "types.Setting",
         "description": "If enabled, the browser attempts to speed up your web browsing experience by pre-resolving DNS entries, prerendering sites (<code>&lt;link rel='prefetch' ...&gt;</code>), and preemptively opening TCP and SSL connections to servers.  This preference's value is a boolean, defaulting to <code>true</code>."
       },
+      "peerConnectionEnabled": {
+        "$ref": "types.Setting",
+        "description": "Allow users to enable and disable RTCPeerConnections (aka WebRTC)."
+      },
       "webRTCIPHandlingPolicy": {
         "$ref": "types.Setting",
         "description": "Allow users to specify the media performance/privacy tradeoffs which impacts how WebRTC traffic will be routed and how much local address information is exposed. This preference's value is of type IPHandlingPolicy, defaulting to <code>default</code>."
       }
     }
   },
   {
     "namespace": "privacy.websites",
--- a/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js
@@ -209,94 +209,110 @@ add_task(async function test_privacy() {
 
   for (let extension of testExtensions) {
     await extension.unload();
   }
 
   await promiseShutdownManager();
 });
 
-add_task(async function test_privacy_webRTCIPHandlingPolicy() {
-  // Create a object to hold the default values of all the prefs.
-  const PREF_DEFAULTS = {
-    "media.peerconnection.ice.default_address_only": null,
-    "media.peerconnection.ice.no_host": null,
-    "media.peerconnection.ice.proxy_only": null,
+// This test can be used for any settings that are added which utilize only
+// boolean prefs.
+add_task(async function test_privacy_boolean_prefs() {
+  // Create an object to hold the values to which we will initialize the prefs.
+  const SETTINGS = {
+    "network.webRTCIPHandlingPolicy": {
+      "media.peerconnection.ice.default_address_only": false,
+      "media.peerconnection.ice.no_host": false,
+      "media.peerconnection.ice.proxy_only": false,
+    },
+    "network.peerConnectionEnabled": {
+      "media.peerconnection.enabled": true,
+    },
   };
 
-  // Store the default values of each pref.
-  for (let pref in PREF_DEFAULTS) {
-    PREF_DEFAULTS[pref] = ExtensionPreferencesManager.getDefaultValue(pref);
+  async function background() {
+    browser.test.onMessage.addListener(async (msg, ...args) => {
+      let data = args[0];
+      // The second argument is the end of the api name,
+      // e.g., "network.webRTCIPHandlingPolicy".
+      let apiObj = args[1].split(".").reduce((o, i) => o[i], browser.privacy);
+      let settingData;
+      switch (msg) {
+        case "set":
+          await apiObj.set(data);
+          settingData = await apiObj.get({});
+          browser.test.sendMessage("settingData", settingData);
+          break;
+
+        case "clear":
+          await apiObj.clear(data);
+          settingData = await apiObj.get({});
+          browser.test.sendMessage("settingData", settingData);
+          break;
+      }
+    });
+  }
+
+  // Set prefs to our initial values.
+  for (let setting in SETTINGS) {
+    for (let pref in SETTINGS[setting]) {
+      Preferences.set(pref, SETTINGS[setting][pref]);
+    }
   }
 
   do_register_cleanup(() => {
     // Reset the prefs.
-    for (let pref in PREF_DEFAULTS) {
-      Preferences.reset(pref);
+    for (let setting in SETTINGS) {
+      for (let pref in SETTINGS[setting]) {
+        Preferences.reset(pref);
+      }
     }
   });
 
-  async function background() {
-    browser.test.onMessage.addListener(async (msg, value) => {
-      let rtcData;
-      switch (msg) {
-        case "set":
-          await browser.privacy.network.webRTCIPHandlingPolicy.set({value});
-          rtcData = await browser.privacy.network.webRTCIPHandlingPolicy.get({});
-          browser.test.sendMessage("rtcData", rtcData);
-          break;
-
-        case "clear":
-          await browser.privacy.network.webRTCIPHandlingPolicy.clear({});
-          rtcData = await browser.privacy.network.webRTCIPHandlingPolicy.get({});
-          browser.test.sendMessage("rtcData", rtcData);
-          break;
-
-      }
-    });
-  }
-
   let extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["privacy"],
     },
     useAddonManager: "temporary",
   });
 
   await promiseStartupManager();
   await extension.startup();
 
-  async function testSetting(value, truePrefs) {
-    extension.sendMessage("set", value);
-    let data = await extension.awaitMessage("rtcData");
+  async function testSetting(setting, value, truePrefs) {
+    extension.sendMessage("set", {value: value}, setting);
+    let data = await extension.awaitMessage("settingData");
     equal(data.value, value);
-    for (let pref in PREF_DEFAULTS) {
+    for (let pref in SETTINGS[setting]) {
       let prefValue = Preferences.get(pref);
-      if (truePrefs.includes(pref)) {
-        ok(prefValue, `${pref} set correctly for ${value}`);
-      } else {
-        equal(prefValue, PREF_DEFAULTS[pref], `${pref} contains default value for ${value}`);
-      }
+      equal(prefValue, truePrefs.includes(pref), `${pref} set correctly for ${value}`);
     }
   }
 
   await testSetting(
+    "network.webRTCIPHandlingPolicy",
     "default_public_and_private_interfaces",
     ["media.peerconnection.ice.default_address_only"]);
 
   await testSetting(
+    "network.webRTCIPHandlingPolicy",
     "default_public_interface_only",
     ["media.peerconnection.ice.default_address_only", "media.peerconnection.ice.no_host"]);
 
   await testSetting(
+    "network.webRTCIPHandlingPolicy",
     "disable_non_proxied_udp",
     ["media.peerconnection.ice.proxy_only"]);
 
-  await testSetting("default", []);
+  await testSetting("network.webRTCIPHandlingPolicy", "default", []);
+
+  await testSetting("network.peerConnectionEnabled", false, []);
+  await testSetting("network.peerConnectionEnabled", true, ["media.peerconnection.enabled"]);
 
   await extension.unload();
 
   await promiseShutdownManager();
 });
 
 add_task(async function test_exceptions() {
   async function background() {
--- a/toolkit/components/processsingleton/ContentProcessSingleton.js
+++ b/toolkit/components/processsingleton/ContentProcessSingleton.js
@@ -44,17 +44,16 @@ ContentProcessSingleton.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   observe(subject, topic, data) {
     switch (topic) {
     case "app-startup": {
       Services.obs.addObserver(this, "console-api-log-event");
       Services.obs.addObserver(this, "xpcom-shutdown");
-      cpmm.addMessageListener("DevTools:InitDebuggerServer", this);
       TelemetryController.observe(null, topic, null);
       break;
     }
     case "console-api-log-event": {
       let consoleMsg = subject.wrappedJSObject;
 
       let msgData = {
         level: consoleMsg.level,
@@ -98,24 +97,14 @@ ContentProcessSingleton.prototype = {
 
       cpmm.sendAsyncMessage("Console:Log", msgData);
       break;
     }
 
     case "xpcom-shutdown":
       Services.obs.removeObserver(this, "console-api-log-event");
       Services.obs.removeObserver(this, "xpcom-shutdown");
-      cpmm.removeMessageListener("DevTools:InitDebuggerServer", this);
       break;
     }
   },
-
-  receiveMessage(message) {
-    // load devtools component on-demand
-    // Only reply if we are in a real content process
-    if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-      let {init} = Cu.import("resource://devtools/server/content-server.jsm", {});
-      init(message);
-    }
-  },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentProcessSingleton]);
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -512,16 +512,17 @@ STUB(gtk_window_unmaximize)
 STUB(gdk_device_get_source)
 STUB(gdk_device_manager_get_client_pointer)
 STUB(gdk_disable_multidevice)
 STUB(gdk_device_manager_list_devices)
 STUB(gdk_display_get_device_manager)
 STUB(gdk_error_trap_pop_ignored)
 STUB(gdk_event_get_source_device)
 STUB(gdk_window_get_type)
+STUB(gdk_window_get_window_type)
 STUB(gdk_x11_window_get_xid)
 STUB(gdk_x11_display_get_type)
 STUB(gtk_box_new)
 STUB(gtk_cairo_should_draw_window)
 STUB(gtk_cairo_transform_to_window)
 STUB(gtk_combo_box_text_append)
 STUB(gtk_drag_set_icon_surface)
 STUB(gtk_get_major_version)
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -215,16 +215,18 @@ static void     hierarchy_changed_cb    
                                            GtkWidget *previous_toplevel);
 static gboolean window_state_event_cb     (GtkWidget *widget,
                                            GdkEventWindowState *event);
 static void     theme_changed_cb          (GtkSettings *settings,
                                            GParamSpec *pspec,
                                            nsWindow *data);
 static void     check_resize_cb           (GtkContainer* container,
                                            gpointer user_data);
+static void     composited_changed_cb     (GtkWidget* widget,
+                                           gpointer user_data);
 
 #if (MOZ_WIDGET_GTK == 3)
 static void     scale_changed_cb          (GtkWidget* widget,
                                            GParamSpec* aPSpec,
                                            gpointer aPointer);
 #endif
 #if GTK_CHECK_VERSION(3,4,0)
 static gboolean touch_event_cb            (GtkWidget* aWidget,
@@ -3365,16 +3367,29 @@ nsWindow::OnDPIChanged()
 
 void
 nsWindow::OnCheckResize()
 {
     mPendingConfigures++;
 }
 
 void
+nsWindow::OnCompositedChanged()
+{
+  if (mWidgetListener) {
+    nsIPresShell* presShell = mWidgetListener->GetPresShell();
+    if (presShell) {
+      // Update CSD after the change in alpha visibility
+      presShell->ThemeChanged();
+    }
+  }
+  CleanLayerManagerRecursive();
+}
+
+void
 nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
                             guint aTime)
 {
     WidgetDragEvent event(true, aMsg, this);
 
     InitDragEvent(event);
 
     event.mRefPoint = aRefPoint;
@@ -3599,16 +3614,41 @@ nsWindow::Create(nsIWidget* aParent,
         // (for temporary windows).
         // For long-lived windows, their stacking order is managed by the
         // window manager, as indicated by GTK_WINDOW_TOPLEVEL ...
         GtkWindowType type =
             mWindowType != eWindowType_popup || aInitData->mNoAutoHide ?
               GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
         mShell = gtk_window_new(type);
 
+        bool useAlphaVisual = (mWindowType == eWindowType_popup &&
+                               aInitData->mSupportTranslucency);
+
+        // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false
+        // to allow experimentation
+        if (Preferences::GetBool("mozilla.widget.use-argb-visuals", false))
+            useAlphaVisual = true;
+
+        // We need to select an ARGB visual here instead of in
+        // SetTransparencyMode() because it has to be done before the
+        // widget is realized.  An ARGB visual is only useful if we
+        // are on a compositing window manager.
+        if (useAlphaVisual) {
+            GdkScreen *screen = gtk_widget_get_screen(mShell);
+            if (gdk_screen_is_composited(screen)) {
+#if (MOZ_WIDGET_GTK == 2)
+                GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+                gtk_widget_set_colormap(mShell, colormap);
+#else
+                GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
+                gtk_widget_set_visual(mShell, visual);
+#endif
+            }
+        }
+
         // We only move a general managed toplevel window if someone has
         // actually placed the window somewhere.  If no placement has taken
         // place, we just let the window manager Do The Right Thing.
         NativeResize();
 
         if (mWindowType == eWindowType_dialog) {
             SetDefaultIcon();
             gtk_window_set_wmclass(GTK_WINDOW(mShell), "Dialog",
@@ -3622,33 +3662,16 @@ nsWindow::Create(nsIWidget* aParent,
             // With popup windows, we want to control their position, so don't
             // wait for the window manager to place them (which wouldn't
             // happen with override-redirect windows anyway).
             NativeMove();
 
             gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup",
                                    gdk_get_program_class());
 
-            if (aInitData->mSupportTranslucency) {
-                // We need to select an ARGB visual here instead of in
-                // SetTransparencyMode() because it has to be done before the
-                // widget is realized.  An ARGB visual is only useful if we
-                // are on a compositing window manager.
-                GdkScreen *screen = gtk_widget_get_screen(mShell);
-                if (gdk_screen_is_composited(screen)) {
-#if (MOZ_WIDGET_GTK == 2)
-                    GdkColormap *colormap =
-                        gdk_screen_get_rgba_colormap(screen);
-                    gtk_widget_set_colormap(mShell, colormap);
-#else
-                    GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
-                    gtk_widget_set_visual(mShell, visual);
-#endif
-                }
-            }
             if (aInitData->mNoAutoHide) {
                 // ... but the window manager does not decorate this window,
                 // nor provide a separate taskbar icon.
                 if (mBorderStyle == eBorderStyle_default) {
                   gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
                 }
                 else {
                   bool decorate = mBorderStyle & eBorderStyle_title;
@@ -3824,16 +3847,18 @@ nsWindow::Create(nsIWidget* aParent,
         g_signal_connect(mShell, "configure_event",
                          G_CALLBACK(configure_event_cb), nullptr);
         g_signal_connect(mShell, "delete_event",
                          G_CALLBACK(delete_event_cb), nullptr);
         g_signal_connect(mShell, "window_state_event",
                          G_CALLBACK(window_state_event_cb), nullptr);
         g_signal_connect(mShell, "check-resize",
                          G_CALLBACK(check_resize_cb), nullptr);
+        g_signal_connect(mShell, "composited-changed",
+                         G_CALLBACK(composited_changed_cb), nullptr);
 
         GtkSettings* default_settings = gtk_settings_get_default();
         g_signal_connect_after(default_settings,
                                "notify::gtk-theme-name",
                                G_CALLBACK(theme_changed_cb), this);
         g_signal_connect_after(default_settings,
                                "notify::gtk-font-name",
                                G_CALLBACK(theme_changed_cb), this);
@@ -4340,16 +4365,43 @@ nsWindow::GetTransparencyMode()
         }
 
         return topWindow->GetTransparencyMode();
     }
 
     return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
 }
 
+#if (MOZ_WIDGET_GTK >= 3)
+void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion)
+{
+    // Available as of GTK 3.10+
+    static auto sGdkWindowSetOpaqueRegion =
+        (void (*)(GdkWindow*, cairo_region_t*))
+            dlsym(RTLD_DEFAULT, "gdk_window_set_opaque_region");
+
+    if (sGdkWindowSetOpaqueRegion && mGdkWindow &&
+        gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TOPLEVEL) {
+        if (aOpaqueRegion.IsEmpty()) {
+            (*sGdkWindowSetOpaqueRegion)(mGdkWindow, nullptr);
+        } else {
+            cairo_region_t *region = cairo_region_create();
+            for (auto iter = aOpaqueRegion.RectIter(); !iter.Done();
+                 iter.Next()) {
+                const LayoutDeviceIntRect &r = iter.Get();
+                cairo_rectangle_int_t rect = { r.x, r.y, r.width, r.height };
+                cairo_region_union_rectangle(region, &rect);
+            }
+            (*sGdkWindowSetOpaqueRegion)(mGdkWindow, region);
+            cairo_region_destroy(region);
+        }
+    }
+}
+#endif
+
 nsresult
 nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
 {
     // If this is a remotely updated widget we receive clipping, position, and
     // size information from a source other than our owner. Don't let our parent
     // update this information.
     if (mWindowType == eWindowType_plugin_ipc_chrome) {
       return NS_OK;
@@ -5831,16 +5883,26 @@ check_resize_cb (GtkContainer* container
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
     if (!window) {
       return;
     }
     window->OnCheckResize();
 }
 
+static void
+composited_changed_cb (GtkWidget* widget, gpointer user_data)
+{
+    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+    if (!window) {
+      return;
+    }
+    window->OnCompositedChanged();
+}
+
 #if (MOZ_WIDGET_GTK == 3)
 static void
 scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer)
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
     if (!window) {
       return;
     }
@@ -6431,17 +6493,20 @@ nsWindow::GetLayerManager(PLayerTransact
                           LayersBackend aBackendHint,
                           LayerManagerPersistence aPersistence)
 {
     if (mIsDestroyed) {
       // Prevent external code from triggering the re-creation of the LayerManager/Compositor
       // during shutdown. Just return what we currently have, which is most likely null.
       return mLayerManager;
     }
-    if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
+
+    if (!mLayerManager &&
+        eTransparencyTransparent == GetTransparencyMode())
+    {
         mLayerManager = CreateBasicLayerManager();
     }
 
     return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence);
 }
 
 void
 nsWindow::ClearCachedResources()
@@ -6740,8 +6805,22 @@ void nsWindow::GetCompositorWidgetInitDa
 {
   #ifdef MOZ_X11
   *aInitData = mozilla::widget::CompositorWidgetInitData(
                                   mXWindow,
                                   nsCString(XDisplayString(mXDisplay)),
                                   GetClientSize());
   #endif
 }
+
+bool
+nsWindow::IsComposited() const
+{
+  if (!mGdkWindow) {
+    NS_WARNING("nsWindow::HasARGBVisual called before realization!");
+    return false;
+  }
+
+  GdkScreen* gdkScreen = gdk_screen_get_default();
+  return gdk_screen_is_composited(gdkScreen) &&
+         (gdk_window_get_visual(mGdkWindow)
+            == gdk_screen_get_rgba_visual(gdkScreen));
+}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -246,16 +246,17 @@ private:
     void               ReleaseGrabs (void);
 
     void               UpdateClientOffset();
 
 public:
     void               ThemeChanged(void);
     void               OnDPIChanged(void);
     void               OnCheckResize(void);
+    void               OnCompositedChanged(void);
 
 #ifdef MOZ_X11
     Window             mOldFocusWindow;
 #endif /* MOZ_X11 */
 
     static guint32     sLastButtonPressTime;
 
     virtual MOZ_MUST_USE nsresult
@@ -302,16 +303,19 @@ public:
 
     // These methods are for toplevel windows only.
     void               ResizeTransparencyBitmap();
     void               ApplyTransparencyBitmap();
     void               ClearTransparencyBitmap();
 
    virtual void        SetTransparencyMode(nsTransparencyMode aMode) override;
    virtual nsTransparencyMode GetTransparencyMode() override;
+#if (MOZ_WIDGET_GTK >= 3)
+   virtual void        UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) override;
+#endif
    virtual nsresult    ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override;
    nsresult            UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
                                                             uint8_t* aAlphas, int32_t aStride);
 
 #if (MOZ_WIDGET_GTK == 2)
     static already_AddRefed<DrawTarget> GetDrawTargetForGdkDrawable(GdkDrawable* aDrawable,
                                                                     const mozilla::gfx::IntSize& aSize);
 #endif
@@ -425,16 +429,18 @@ private:
       CheckForRollup(0, 0, false, true);
     }
 
     bool               GetDragInfo(mozilla::WidgetMouseEvent* aMouseEvent,
                                    GdkWindow** aWindow, gint* aButton,
                                    gint* aRootX, gint* aRootY);
     void               ClearCachedResources();
     nsIWidgetListener* GetListener();
+    bool               IsComposited() const;
+
 
     GtkWidget          *mShell;
     MozContainer       *mContainer;
     GdkWindow          *mGdkWindow;
 
     uint32_t            mHasMappedToplevel : 1,
                         mIsFullyObscured : 1,
                         mRetryPointerGrab : 1;