Bug 1015466 - Part 3, PHttpBackgroundChannel lifecycle management. r=mayhemer
authorShih-Chiang Chien <schien@mozilla.com>
Mon, 24 Apr 2017 11:09:35 +0800
changeset 409884 3814e17ae85efaad6d9740bc2e979179c2a4e99e
parent 409883 272dbc46892e0b6bfc602792bc6747927d93a333
child 409885 7ea4def6d4f08a443473c9ee659a2a1c704000a8
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)
reviewersmayhemer
bugs1015466
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
Bug 1015466 - Part 3, PHttpBackgroundChannel lifecycle management. r=mayhemer PHttpBackgroundChannel is created by content process because PBackground IPDL can only be initiated from content process. The background channel deletion is controlled by chrome process while PHttpChannel is going to be closed or canceled. BackgroundChannelRegistrar is introduced for pairing HttpChannelParent and HttpBackgroundChannelParent since they are created over different IPDL asynchronously. nsIParentRedirectingChannel.continueVerification is introduced to asynchronously wait for background channel to be established on the new channel during the Redirect2Verify phase. MozReview-Commit-ID: 41l8ivan8iA
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundParentImpl.cpp
netwerk/base/nsIParentRedirectingChannel.idl
netwerk/build/nsNetCID.h
netwerk/build/nsNetModule.cpp
netwerk/protocol/http/BackgroundChannelRegistrar.cpp
netwerk/protocol/http/BackgroundChannelRegistrar.h
netwerk/protocol/http/HttpBackgroundChannelChild.cpp
netwerk/protocol/http/HttpBackgroundChannelChild.h
netwerk/protocol/http/HttpBackgroundChannelParent.cpp
netwerk/protocol/http/HttpBackgroundChannelParent.h
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsIBackgroundChannelRegistrar.idl
--- 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 {
 
@@ -564,18 +565,21 @@ BackgroundChildImpl::AllocPHttpBackgroun
 {
   MOZ_CRASH("PHttpBackgroundChannelChild actor should be manually constructed!");
   return nullptr;
 }
 
 bool
 BackgroundChildImpl::DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelChild* aActor)
 {
-  MOZ_ASSERT(aActor);
-  // TODO
+  // 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)
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -874,41 +874,54 @@ BackgroundParentImpl::DeallocPWebAuthnTr
 }
 
 net::PHttpBackgroundChannelParent*
 BackgroundParentImpl::AllocPHttpBackgroundChannelParent(const uint64_t& aChannelId)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
-  return new net::HttpBackgroundChannelParent();
+  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();
 
-  //TODO
+  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();
 
-  //TODO
+  // 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)
--- 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/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/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -1,30 +1,153 @@
 /* -*- 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/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::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);
+}
+
+// PHttpBackgroundChannelChild
 IPCResult
 HttpBackgroundChannelChild::RecvOnStartRequestSent()
 {
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvOnTransportAndData(
@@ -68,12 +191,19 @@ IPCResult
 HttpBackgroundChannelChild::RecvDivertMessages()
 {
   return IPC_OK();
 }
 
 void
 HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy)
 {
+  LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mChannelChild) {
+    RefPtr<HttpChannelChild> channelChild = mChannelChild.forget();
+    channelChild->OnBackgroundChildDestroyed();
+  }
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -10,22 +10,36 @@
 
 #include "mozilla/net/PHttpBackgroundChannelChild.h"
 
 using mozilla::ipc::IPCResult;
 
 namespace mozilla {
 namespace net {
 
+class HttpChannelChild;
+
 class HttpBackgroundChannelChild final : public PHttpBackgroundChannelChild
 {
+  friend class BackgroundChannelCreateCallback;
 public:
   explicit HttpBackgroundChannelChild();
 
-  virtual ~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 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;
 
@@ -39,14 +53,27 @@ protected:
 
   IPCResult RecvFlushedForDiversion() override;
 
   IPCResult RecvDivertMessages() override;
 
   IPCResult RecvOnStartRequestSent() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  virtual ~HttpBackgroundChannelChild();
+
+  // Initiate the creation of the PBckground IPC channel.
+  // Return false if failed.
+  bool CreateBackgroundChannel();
+
+  // 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;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpBackgroundChannelChild_h
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -1,29 +1,164 @@
 /* -*- 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/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));
 }
 
 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
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -4,27 +4,56 @@
 /* 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();
 
-  virtual ~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();
 
 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.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
@@ -16,16 +16,17 @@
 #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"
@@ -267,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)
@@ -1025,16 +1046,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) {
@@ -1247,35 +1270,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]\n", this));
+
+  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;
   }
@@ -1830,16 +1885,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));
@@ -2458,16 +2527,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
@@ -112,16 +113,21 @@ public:
 
   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,
@@ -307,16 +313,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 +410,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
@@ -5,26 +5,31 @@
  * 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"
@@ -33,38 +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)
@@ -89,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:
@@ -149,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
@@ -249,16 +352,20 @@ HttpChannelParent::AsyncOpenFailed(nsres
   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)) {
     AsyncOpenFailed(rv);
     return;
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
   if (NS_FAILED(rv)) {
@@ -287,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)
@@ -454,25 +561,25 @@ HttpChannelParent::DoAsyncOpen(  const U
 
   httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
 
   if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
     const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
     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))) {
@@ -621,21 +728,57 @@ HttpChannelParent::DoAsyncOpen(  const U
   // 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 "
@@ -668,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));
@@ -824,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()
 {
@@ -1292,16 +1522,17 @@ HttpChannelParent::OnStopRequest(nsIRequ
   // to be passed down.
   mChannel->GetProtocolVersion(timing.protocolVersion);
 
   mChannel->GetCacheReadStart(&timing.cacheReadStart);
   mChannel->GetCacheReadEnd(&timing.cacheReadEnd);
 
   if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
     return NS_ERROR_UNEXPECTED;
+
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -1508,17 +1739,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;
 }
 
@@ -1810,22 +2043,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) {
@@ -1858,16 +2095,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
@@ -1908,16 +2148,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,18 +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()
@@ -233,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;
 
@@ -269,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/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',
@@ -55,16 +56,17 @@ 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',
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);
+
+};