Bug 1260527 - Change HttpChannelChild to open IPDL earlier than AsyncOpen r=kershaw,dragana
authorValentin Gosu <valentin.gosu@gmail.com>
Mon, 26 Nov 2018 17:38:42 +0000
changeset 507219 d15f0ac561d9fc06518cf12851aaecf52eba485e
parent 507218 759ec08eebaaa4418a3a96b20d5942d55a3cafcc
child 507220 f38d34679027acdcb303231cadbc7aef1849af53
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskershaw, dragana
bugs1260527
milestone65.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 1260527 - Change HttpChannelChild to open IPDL earlier than AsyncOpen r=kershaw,dragana Since we need the loadInfo to set up the IPDL connection, we move the logic to do so from HttpChannelChild::AsyncOpen to HttpChannelChild::SetLoadInfo via InitIPCChannel. It would have been nicer to do so in HttpChannelChild::Init, but I ran into issues with view-source channels, which required an ugly hack. Also note that RemoteChannelExists() preserves the existing contract - it is true between asyncOpen and onStopRequest - but the name is slightly off, as the channel has already been open by the time we call asyncOpen. We will fix this in a follow-up. Differential Revision: https://phabricator.services.mozilla.com/D12419
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -69,19 +69,17 @@ void NeckoChild::InitNeckoChild()
       return;
     }
     gNeckoChild = cpc->SendPNeckoConstructor();
     NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
   }
 }
 
 PHttpChannelChild*
-NeckoChild::AllocPHttpChannelChild(const PBrowserOrId& browser,
-                                   const SerializedLoadContext& loadContext,
-                                   const HttpChannelCreationArgs& aOpenArgs)
+NeckoChild::AllocPHttpChannelChild()
 {
   // We don't allocate here: instead we always use IPDL constructor that takes
   // an existing HttpChildChannel
   MOZ_ASSERT_UNREACHABLE("AllocPHttpChannelChild should not be called on "
                          "child");
   return nullptr;
 }
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -20,19 +20,17 @@ class NeckoChild :
 {
 public:
   NeckoChild() = default;
   virtual ~NeckoChild();
 
   static void InitNeckoChild();
 
 protected:
-  virtual PHttpChannelChild*
-    AllocPHttpChannelChild(const PBrowserOrId&, const SerializedLoadContext&,
-                           const HttpChannelCreationArgs& aOpenArgs) override;
+  virtual PHttpChannelChild* AllocPHttpChannelChild() override;
   virtual bool DeallocPHttpChannelChild(PHttpChannelChild*) override;
 
   virtual PStunAddrsRequestChild* AllocPStunAddrsRequestChild() override;
   virtual bool
     DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor) override;
 
   virtual PWebrtcProxyChannelChild* AllocPWebrtcProxyChannelChild(
     const PBrowserOrId& browser) override;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -87,29 +87,31 @@ NeckoParent::NeckoParent()
   static bool registeredBool = false;
   if (!registeredBool) {
     Preferences::AddBoolVarCache(&NeckoCommonInternal::gSecurityDisabled,
                                  "network.disable.ipc.security");
     registeredBool = true;
   }
 }
 
-static PBOverrideStatus
-PBOverrideStatusFromLoadContext(const SerializedLoadContext& aSerialized)
+/* static */ PBOverrideStatus
+NeckoParent::PBOverrideStatusFromLoadContext(
+  const SerializedLoadContext& aSerialized)
 {
   if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
     return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0) ?
       kPBOverride_Private :
       kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
-static already_AddRefed<nsIPrincipal>
-GetRequestingPrincipal(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs)
+/* static */ already_AddRefed<nsIPrincipal>
+NeckoParent::GetRequestingPrincipal(
+  const OptionalLoadInfoArgs& aOptionalLoadInfoArgs)
 {
   if (aOptionalLoadInfoArgs.type() != OptionalLoadInfoArgs::TLoadInfoArgs) {
     return nullptr;
   }
 
   const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs();
   const OptionalPrincipalInfo& optionalPrincipalInfo =
     loadInfoArgs.requestingPrincipalInfo();
@@ -119,29 +121,29 @@ GetRequestingPrincipal(const OptionalLoa
   }
 
   const PrincipalInfo& principalInfo =
     optionalPrincipalInfo.get_PrincipalInfo();
 
   return PrincipalInfoToPrincipal(principalInfo);
 }
 
-static already_AddRefed<nsIPrincipal>
-GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs)
+/* static */ already_AddRefed<nsIPrincipal>
+NeckoParent::GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs)
 {
   if (aArgs.type() != HttpChannelCreationArgs::THttpChannelOpenArgs) {
     return nullptr;
   }
 
   const HttpChannelOpenArgs& args = aArgs.get_HttpChannelOpenArgs();
   return GetRequestingPrincipal(args.loadInfo());
 }
 
-static already_AddRefed<nsIPrincipal>
-GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs)
+/* static */ already_AddRefed<nsIPrincipal>
+NeckoParent::GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs)
 {
   if (aArgs.type() != FTPChannelCreationArgs::TFTPChannelOpenArgs) {
     return nullptr;
   }
 
   const FTPChannelOpenArgs& args = aArgs.get_FTPChannelOpenArgs();
   return GetRequestingPrincipal(args.loadInfo());
 }
@@ -152,17 +154,17 @@ GetRequestingPrincipal(const FTPChannelC
 static MOZ_COLD
 void CrashWithReason(const char * reason)
 {
 #ifndef RELEASE_OR_BETA
   MOZ_CRASH_UNSAFE_OOL(reason);
 #endif
 }
 
-const char*
+/* static */ const char*
 NeckoParent::GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized,
                                           PContentParent* aContent,
                                           nsIPrincipal* aRequestingPrincipal,
                                           OriginAttributes& aAttrs)
 {
   if (!UsingNeckoIPCSecurity()) {
     if (!aSerialized.IsNotNull()) {
       // If serialized is null, we cannot validate anything. We have to assume
@@ -228,17 +230,17 @@ NeckoParent::GetValidatedOriginAttribute
   // Leak the buffer on the heap to make sure that it lives long enough, as
   // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
   // the program.
   char * error = strdup(errorString.BeginReading());
   CrashWithReason(error);
   return "App does not have permission";
 }
 
-const char *
+/* static */ const char *
 NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                                       PContentParent* aContent,
                                       const SerializedLoadContext& aSerialized,
                                       nsIPrincipal* aRequestingPrincipal,
                                       nsCOMPtr<nsILoadContext> &aResult)
 {
   OriginAttributes attrs;
   const char* error = GetValidatedOriginAttributes(aSerialized, aContent,
@@ -279,61 +281,31 @@ NeckoParent::CreateChannelLoadContext(co
 void
 NeckoParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 PHttpChannelParent*
-NeckoParent::AllocPHttpChannelParent(const PBrowserOrId& aBrowser,
-                                     const SerializedLoadContext& aSerialized,
-                                     const HttpChannelCreationArgs& aOpenArgs)
+NeckoParent::AllocPHttpChannelParent()
 {
-  nsCOMPtr<nsIPrincipal> requestingPrincipal =
-    GetRequestingPrincipal(aOpenArgs);
-
-  nsCOMPtr<nsILoadContext> loadContext;
-  const char *error = CreateChannelLoadContext(aBrowser, Manager(),
-                                               aSerialized, requestingPrincipal,
-                                               loadContext);
-  if (error) {
-    printf_stderr("NeckoParent::AllocPHttpChannelParent: "
-                  "FATAL error: %s: KILLING CHILD PROCESS\n",
-                  error);
-    return nullptr;
-  }
-  PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(aSerialized);
-  HttpChannelParent *p = new HttpChannelParent(aBrowser, loadContext, overrideStatus);
+  HttpChannelParent* p = new HttpChannelParent();
   p->AddRef();
   return p;
 }
 
 bool
 NeckoParent::DeallocPHttpChannelParent(PHttpChannelParent* channel)
 {
   HttpChannelParent *p = static_cast<HttpChannelParent *>(channel);
   p->Release();
   return true;
 }
 
-mozilla::ipc::IPCResult
-NeckoParent::RecvPHttpChannelConstructor(
-                      PHttpChannelParent* aActor,
-                      const PBrowserOrId& aBrowser,
-                      const SerializedLoadContext& aSerialized,
-                      const HttpChannelCreationArgs& aOpenArgs)
-{
-  HttpChannelParent* p = static_cast<HttpChannelParent*>(aActor);
-  if (!p->Init(aOpenArgs)) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-  return IPC_OK();
-}
-
 PStunAddrsRequestParent*
 NeckoParent::AllocPStunAddrsRequestParent()
 {
 #ifdef MOZ_WEBRTC
   StunAddrsRequestParent* p = new StunAddrsRequestParent();
   p->AddRef();
   return p;
 #else
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -28,16 +28,25 @@ enum PBOverrideStatus {
 // Header file contents
 class NeckoParent
   : public PNeckoParent
 {
 public:
   NeckoParent();
   virtual ~NeckoParent() = default;
 
+  static PBOverrideStatus PBOverrideStatusFromLoadContext(
+    const SerializedLoadContext& aSerialized);
+  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+    const OptionalLoadInfoArgs& aOptionalLoadInfoArgs);
+  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+    const FTPChannelCreationArgs& aArgs);
+  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
+    const HttpChannelCreationArgs& aArgs);
+
   MOZ_MUST_USE
   static const char *
   GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized,
                                PContentParent* aBrowser,
                                nsIPrincipal* aRequestingPrincipal,
                                mozilla::OriginAttributes& aAttrs);
 
   /*
@@ -88,25 +97,17 @@ public:
                                nsIAuthInformation* aInfo, nsICancelable**) override;
 
   protected:
     PNeckoParent* mNeckoParent;
     TabId mNestedFrameId;
   };
 
 protected:
-  virtual PHttpChannelParent*
-    AllocPHttpChannelParent(const PBrowserOrId&, const SerializedLoadContext&,
-                            const HttpChannelCreationArgs& aOpenArgs) override;
-  virtual mozilla::ipc::IPCResult
-    RecvPHttpChannelConstructor(
-      PHttpChannelParent* aActor,
-      const PBrowserOrId& aBrowser,
-      const SerializedLoadContext& aSerialized,
-      const HttpChannelCreationArgs& aOpenArgs) override;
+  virtual PHttpChannelParent* AllocPHttpChannelParent() override;
   virtual bool DeallocPHttpChannelParent(PHttpChannelParent*) override;
 
   virtual PStunAddrsRequestParent* AllocPStunAddrsRequestParent() override;
   virtual bool
     DeallocPStunAddrsRequestParent(PStunAddrsRequestParent* aActor) override;
 
   virtual PWebrtcProxyChannelParent* AllocPWebrtcProxyChannelParent(
     const PBrowserOrId& aBrowser) override;
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -67,19 +67,17 @@ nested(upto inside_cpow) sync protocol P
   manages PStunAddrsRequest;
   manages PTrackingDummyChannel;
   manages PWebrtcProxyChannel;
 
 parent:
   async __delete__();
 
   nested(inside_cpow) async PCookieService();
-  async PHttpChannel(PBrowserOrId browser,
-                     SerializedLoadContext loadContext,
-                     HttpChannelCreationArgs args);
+  async PHttpChannel();
   async PWyciwygChannel();
   async PFTPChannel(PBrowserOrId browser, SerializedLoadContext loadContext,
                     FTPChannelCreationArgs args);
 
   async PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext,
                    uint32_t aSerialID);
   async PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers);
   async PUDPSocket(Principal principal, nsCString filter);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -188,16 +188,17 @@ HttpChannelChild::HttpChannelChild()
   , mShouldInterceptSubsequentRedirect(false)
   , mRedirectingForSubsequentSynthesizedResponse(false)
   , mPostRedirectChannelShouldIntercept(false)
   , mPostRedirectChannelShouldUpgrade(false)
   , mShouldParentIntercept(false)
   , mSuspendParentAfterSynthesizeResponse(false)
   , mCacheNeedToReportBytesReadInitialized(false)
   , mNeedToReportBytesRead(true)
+  , mSentAsyncOpen(false)
 {
   LOG(("Creating HttpChannelChild @%p\n", this));
 
   mChannelCreationTime = PR_Now();
   mChannelCreationTimestamp = TimeStamp::Now();
   mLastStatusReported = mChannelCreationTimestamp; // in case we enable the profiler after Init()
   mAsyncOpenTime = TimeStamp::Now();
   mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
@@ -261,17 +262,17 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
   nsrefcnt count = --mRefCnt;
   MOZ_ASSERT(int32_t(count) >= 0, "dup release");
   NS_LOG_RELEASE(this, count, "HttpChannelChild");
 
   // Normally we Send_delete in OnStopRequest, but when we need to retain the
   // remote channel for security info IPDL itself holds 1 reference, so we
   // Send_delete when refCnt==1.  But if !mIPCOpen, then there's nobody to send
   // to, so we fall through.
-  if (mKeptAlive && count == 1 && mIPCOpen) {
+  if ((mKeptAlive || !mSentAsyncOpen) && count == 1 && mIPCOpen) {
     mKeptAlive = false;
     // We send a message to the parent, which calls SendDelete, and then the
     // child calling Send__delete__() to finally drop the refcount to 0.
     TrySendDeletingChannel();
     return 1;
   }
 
   if (count == 0) {
@@ -1702,17 +1703,18 @@ HttpChannelChild::RecvFinishInterceptedR
 void
 HttpChannelChild::DeleteSelf()
 {
   Send__delete__(this);
 }
 
 void HttpChannelChild::FinishInterceptedRedirect()
 {
-  nsresult rv;
+  nsresult rv = InitIPCChannel(); // reinitializes IPC channel
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
     MOZ_ASSERT(!mInterceptedRedirectContext, "the context should be null!");
     rv = AsyncOpen2(mInterceptedRedirectListener);
   } else {
     rv = AsyncOpen(mInterceptedRedirectListener, mInterceptedRedirectContext);
   }
   mInterceptedRedirectListener = nullptr;
   mInterceptedRedirectContext = nullptr;
@@ -2259,34 +2261,23 @@ HttpChannelChild::ConnectParent(uint32_t
 
   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
-  // The socket transport in the chrome process now holds a logical ref to us
-  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
-  AddIPDLReference();
-
-  // This must happen before the constructor message is sent. Otherwise messages
-  // from the parent could arrive quickly and be delivered to the wrong event
-  // target.
-  SetEventTarget();
-
   HttpChannelConnectArgs connectArgs(registrarId, mShouldParentIntercept);
   PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
                          ->GetBrowserOrId(tabChild);
-  if (!gNeckoChild->
-        SendPHttpChannelConstructor(this, browser,
-                                    IPC::SerializedLoadContext(this),
-                                    connectArgs)) {
+  if (!SendAsyncOpen(browser, IPC::SerializedLoadContext(this), connectArgs)) {
     return NS_ERROR_FAILURE;
   }
+  mSentAsyncOpen = true;
 
   {
     MutexAutoLock lock(mBgChildMutex);
 
     MOZ_ASSERT(!mBgChild);
     MOZ_ASSERT(!mBgInitFailCallback);
 
     mBgInitFailCallback = NewRunnableMethod<nsresult>(
@@ -2783,16 +2774,52 @@ HttpChannelChild::AsyncOpen2(nsIStreamLi
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ReleaseListeners();
     return rv;
   }
   return AsyncOpen(listener, nullptr);
 }
 
+NS_IMETHODIMP
+HttpChannelChild::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+  LOG(("HttpChannelChild::SetLoadInfo [this=%p, aLoadInfo=%p]",
+       this, aLoadInfo));
+  MOZ_ALWAYS_SUCCEEDS(HttpBaseChannel::SetLoadInfo(aLoadInfo));
+
+  // IPC channel already open
+  if (NS_WARN_IF(mIPCOpen)) {
+    return NS_OK;
+  }
+
+  return InitIPCChannel();
+}
+
+nsresult
+HttpChannelChild::InitIPCChannel()
+{
+  MOZ_ASSERT(!mIPCOpen);
+
+  // This must happen before the constructor message is sent. Otherwise messages
+  // from the parent could arrive quickly and be delivered to the wrong event
+  // target.
+  SetEventTarget();
+
+  LOG(("  Calling SendPHttpChannelConstructor"));
+  if (!gNeckoChild->SendPHttpChannelConstructor(this)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // The socket transport in the chrome process now holds a logical ref to us
+  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
+  AddIPDLReference();
+  return NS_OK;
+}
+
 // Assigns an nsIEventTarget to our IPDL actor so that IPC messages are sent to
 // the correct DocGroup/TabGroup.
 void
 HttpChannelChild::SetEventTarget()
 {
   nsCOMPtr<nsILoadInfo> loadInfo;
   GetLoadInfo(getter_AddRefs(loadInfo));
 
@@ -2996,31 +3023,22 @@ HttpChannelChild::ContinueAsyncOpen()
   openArgs.dispatchFetchEventEnd()    = mDispatchFetchEventEnd;
   openArgs.handleFetchEventStart()    = mHandleFetchEventStart;
   openArgs.handleFetchEventEnd()      = mHandleFetchEventEnd;
 
   openArgs.forceMainDocumentChannel() = mForceMainDocumentChannel;
 
   openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
 
-  // This must happen before the constructor message is sent. Otherwise messages
-  // from the parent could arrive quickly and be delivered to the wrong event
-  // target.
-  SetEventTarget();
-
-  // The socket transport in the chrome process now holds a logical ref to us
-  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
-  AddIPDLReference();
-
+  LOG(("HttpChannelChild::ContinueAsyncOpen - SendAsyncOpen [this=%p]", this));
   PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
-  if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
-                                                IPC::SerializedLoadContext(this),
-                                                openArgs)) {
+  if (!SendAsyncOpen(browser, IPC::SerializedLoadContext(this), openArgs)) {
     return NS_ERROR_FAILURE;
   }
+  mSentAsyncOpen = true;
 
   {
     MutexAutoLock lock(mBgChildMutex);
 
     MOZ_RELEASE_ASSERT(gSocketTransportService);
 
     // Service worker might use the same HttpChannelChild to do async open
     // twice. Need to disconnect with previous background channel before
@@ -3787,16 +3805,24 @@ HttpChannelChild::ResetInterception()
     mLoadFlags |= LOAD_BYPASS_SERVICE_WORKER;
   }
 
   // If the channel has already been aborted or canceled, just stop.
   if (NS_FAILED(mStatus)) {
     return;
   }
 
+  if (!mIPCOpen) {
+    // The IPC channel was closed. See RecvFinishInterceptedRedirect.
+    // We now want to reuse it, so we call InitIPCChannel again.
+    DebugOnly<nsresult> rv = InitIPCChannel();
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+  MOZ_ASSERT(mLoadInfo && mIPCOpen);
+
   // Continue with the original cross-process request
   nsresult rv = ContinueAsyncOpen();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Unused << Cancel(rv);
   }
 }
 
 void
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -86,16 +86,17 @@ public:
   // nsIRequest
   NS_IMETHOD Cancel(nsresult status) override;
   NS_IMETHOD Suspend() override;
   NS_IMETHOD Resume() override;
   // nsIChannel
   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
   NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
   NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
+  NS_IMETHOD SetLoadInfo(nsILoadInfo* aLoadInfo) override;
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue,
                               bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
@@ -201,16 +202,17 @@ protected:
   };
 
   // Get event target for processing network events.
   already_AddRefed<nsIEventTarget> GetNeckoTarget() override;
 
   virtual mozilla::ipc::IPCResult RecvLogBlockedCORSRequest(const nsString& aMessage, const nsCString& aCategory) override;
   NS_IMETHOD LogBlockedCORSRequest(const nsAString & aMessage, const nsACString& aCategory) override;
 
+  nsresult InitIPCChannel();
 private:
   nsresult
   AsyncCallImpl(void (HttpChannelChild::*funcPtr)(),
                 nsRunnableMethod<HttpChannelChild> **retval);
 
   class OverrideRunnable : public Runnable {
   public:
     OverrideRunnable(HttpChannelChild* aChannel,
@@ -427,21 +429,32 @@ private:
   uint8_t mSuspendParentAfterSynthesizeResponse : 1;
 
   // Set if we get the result and cache |mNeedToReportBytesRead|
   uint8_t mCacheNeedToReportBytesReadInitialized : 1;
 
   // True if we need to tell the parent the size of unreported received data
   uint8_t mNeedToReportBytesRead : 1;
 
+  // True after SendAsyncOpen is called.
+  uint8_t mSentAsyncOpen : 1;
+
   void FinishInterceptedRedirect();
   void CleanupRedirectingChannel(nsresult rv);
 
   // true after successful AsyncOpen until OnStopRequest completes.
-  bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }
+  // XXX valentin: technically the channel exists after SetLoadInfo, but it
+  // used to be created at AsyncOpen, and before that the parent actually
+  // doesn't have all the info for it to be safe to call any method.
+  // We should rename it in the future and allow some methods to be called
+  // before asyncOpen.
+  bool RemoteChannelExists()
+  {
+    return mIPCOpen && !mKeptAlive && mSentAsyncOpen;
+  }
 
   void AssociateApplicationCache(const nsCString &groupID,
                                  const nsCString &clientID);
   void OnStartRequest(const nsresult& channelStatus,
                       const nsHttpResponseHead& responseHead,
                       const bool& useResponseHead,
                       const nsHttpHeaderArray& requestHeaders,
                       const ParentLoadInfoForwarderArgs& loadInfoForwarder,
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -57,23 +57,21 @@
 
 using mozilla::BasePrincipal;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
-HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
-                                     nsILoadContext* aLoadContext,
-                                     PBOverrideStatus aOverrideStatus)
-  : mLoadContext(aLoadContext)
+HttpChannelParent::HttpChannelParent()
+  : mLoadContext(nullptr)
   , mNestedFrameId(0)
   , mIPCClosed(false)
-  , mPBOverride(aOverrideStatus)
+  , mPBOverride(kPBOverride_Unset)
   , mStatus(NS_OK)
   , mIgnoreProgress(false)
   , mSentRedirect1BeginFailed(false)
   , mReceivedRedirect2Verify(false)
   , mHasSuspendedByBackPressure(false)
   , mPendingDiversion(false)
   , mDivertingFromChild(false)
   , mDivertedOnStartRequest(false)
@@ -89,24 +87,16 @@ HttpChannelParent::HttpChannelParent(con
 
   // Ensure gHttpHandler is initialized: we need the atom table up and running.
   nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
     do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
 
   MOZ_ASSERT(gHttpHandler);
   mHttpHandler = gHttpHandler;
 
-  if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
-    mTabParent = static_cast<dom::TabParent*>(iframeEmbedding.get_PBrowserParent());
-  } else {
-    mNestedFrameId = iframeEmbedding.get_TabId();
-  }
-
-  mSendWindowSize = gHttpHandler->SendWindowSize();
-
   mEventQ = new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
 }
 
 HttpChannelParent::~HttpChannelParent()
 {
   LOG(("Destroying HttpChannelParent [this=%p]\n", this));
   CleanupBackgroundChannel();
 }
@@ -170,16 +160,53 @@ HttpChannelParent::Init(const HttpChanne
     return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
   }
   default:
     MOZ_ASSERT_UNREACHABLE("unknown open type");
     return false;
   }
 }
 
+mozilla::ipc::IPCResult
+HttpChannelParent::RecvAsyncOpen(const PBrowserOrId& aBrowser,
+                                 const SerializedLoadContext& aSerialized,
+                                 const HttpChannelCreationArgs& aOpenArgs)
+{
+  LOG(("HttpChannelParent::RecvAsyncOpen [this=%p]\n", this));
+
+  nsCOMPtr<nsIPrincipal> requestingPrincipal =
+    NeckoParent::GetRequestingPrincipal(aOpenArgs);
+
+  nsCOMPtr<nsILoadContext> loadContext;
+  const char* error =
+    NeckoParent::CreateChannelLoadContext(aBrowser,
+                                          Manager()->Manager(),
+                                          aSerialized,
+                                          requestingPrincipal,
+                                          loadContext);
+  if (error) {
+    return IPC_FAIL(this, "Error in NeckoParent::CreateChannelLoadContext");
+  }
+  mPBOverride = NeckoParent::PBOverrideStatusFromLoadContext(aSerialized);
+  mLoadContext = loadContext;
+
+  if (aBrowser.type() == PBrowserOrId::TPBrowserParent) {
+    mTabParent = static_cast<dom::TabParent*>(aBrowser.get_PBrowserParent());
+  } else {
+    mNestedFrameId = aBrowser.get_TabId();
+  }
+
+  mSendWindowSize = gHttpHandler->SendWindowSize();
+
+  if (!Init(aOpenArgs)) {
+    return IPC_FAIL(this, "Error in HttpChannelParent::Init");
+  }
+  return IPC_OK();
+}
+
 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.
@@ -473,18 +500,22 @@ HttpChannelParent::DoAsyncOpen(  const U
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
   nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
   nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
   nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
   nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
 
-  LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64 " topwinid=%" PRIx64 "]\n",
-       this, uri->GetSpecOrDefault().get(), aChannelId, aTopLevelOuterContentWindowId));
+  LOG(("HttpChannelParent DoAsyncOpen [this=%p uri=%s, gid=%" PRIu64
+       " topwinid=%" PRIx64 "]\n",
+       this,
+       uri->GetSpecOrDefault().get(),
+       aChannelId,
+       aTopLevelOuterContentWindowId));
 
   nsresult rv;
 
   nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
   nsCOMPtr<nsILoadInfo> loadInfo;
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -66,19 +66,17 @@ public:
   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);
+  HttpChannelParent();
 
   MOZ_MUST_USE bool Init(const HttpChannelCreationArgs& aOpenArgs);
 
   // ADivertableParentChannel functions.
   void DivertTo(nsIStreamListener *aListener) override;
   MOZ_MUST_USE nsresult SuspendForDiversion() override;
   MOZ_MUST_USE nsresult SuspendMessageDiversion() override;
   MOZ_MUST_USE nsresult ResumeMessageDiversion() override;
@@ -228,16 +226,20 @@ protected:
   nsresult LogBlockedCORSRequest(const nsAString& aMessage, const nsACString& aCategory) override;
 
   // Calls SendDeleteSelf and sets mIPCClosed to true because we should not
   // send any more messages after that. Bug 1274886
   MOZ_MUST_USE bool DoSendDeleteSelf();
   // Called to notify the parent channel to not send any more IPC messages.
   virtual mozilla::ipc::IPCResult RecvDeletingChannel() override;
   virtual mozilla::ipc::IPCResult RecvFinishInterceptedRedirect() override;
+  virtual mozilla::ipc::IPCResult RecvAsyncOpen(
+    const PBrowserOrId& aBrowser,
+    const SerializedLoadContext& aSerialized,
+    const HttpChannelCreationArgs& aOpenArgs) override;
 
 private:
   void UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut);
 
   void DivertOnDataAvailable(const nsCString& data,
                              const uint64_t& offset,
                              const uint32_t& count);
   void DivertOnStopRequest(const nsresult& statusCode);
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -8,34 +8,37 @@
 include protocol PNecko;
 include protocol PStreamFilter;
 include InputStreamParams;
 include URIParams;
 include PBackgroundSharedTypes;
 include NeckoChannelParams;
 include IPCServiceWorkerDescriptor;
 include IPCStream;
+include PBrowserOrId;
 
 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";
+using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
 
 parent:
-  // Note: channels are opened during construction, so no open method here:
-  // see PNecko.ipdl
+  async AsyncOpen(PBrowserOrId browser,
+                  SerializedLoadContext loadContext,
+                  HttpChannelCreationArgs args);
 
   async SetClassOfService(uint32_t cos);
 
   async SetCacheTokenCachedCharset(nsCString charset);
 
   async Suspend();
   async Resume();