Bug 558623 - e10s HTTP: combine PHttpChannel constructor with SendAsyncOpen/SendRedirect1Begin r=jdm
authorJason Duell <jduell.mcbugs@gmail.com>
Sat, 22 Jun 2013 09:09:19 -0700
changeset 148541 f22c958c618733ca14247e5cf2fa75037f0618c3
parent 148540 312d1be1a95aa24e24cdd67cc7d48510ee8d3a41
child 148542 6e43d49fc668b24590868785fd6e8f3ba63f8307
push id368
push userbbajaj@mozilla.com
push dateMon, 09 Sep 2013 22:57:58 +0000
treeherdermozilla-release@5a4f47ae1217 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs558623
milestone24.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 558623 - e10s HTTP: combine PHttpChannel constructor with SendAsyncOpen/SendRedirect1Begin r=jdm
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/ipc/ipdl.mk
netwerk/protocol/ftp/FTPChannelChild.cpp
netwerk/protocol/ftp/FTPChannelParent.cpp
netwerk/protocol/ftp/FTPChannelParent.h
netwerk/protocol/ftp/PFTPChannel.ipdl
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/HttpLog.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/PHttpChannelParams.h
netwerk/protocol/http/nsHttpResponseHead.h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=c: */
+
+/* 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 URIParams;
+include InputStreamParams;
+include "mozilla/net/PHttpChannelParams.h";
+
+using mozilla::void_t;
+using RequestHeaderTuples;
+using nsHttpAtom;
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// HTTP IPDL structs
+//-----------------------------------------------------------------------------
+
+struct HttpChannelOpenArgs
+{
+  URIParams                   uri;
+  // - TODO: bug 571161: unclear if any HTTP channel clients ever
+  // set originalURI != uri (about:credits?); also not clear if
+  // chrome channel would ever need to know.  Get rid of next arg?
+  OptionalURIParams           original;
+  OptionalURIParams           doc;
+  OptionalURIParams           referrer;
+  OptionalURIParams           apiRedirectTo;
+  uint32_t                    loadFlags;
+  RequestHeaderTuples         requestHeaders;
+  nsHttpAtom                  requestMethod;
+  OptionalInputStreamParams   uploadStream;
+  bool                        uploadStreamHasHeaders;
+  uint16_t                    priority;
+  uint8_t                     redirectionLimit;
+  bool                        allowPipelining;
+  bool                        forceAllowThirdPartyCookie;
+  bool                        resumeAt;
+  uint64_t                    startPos;
+  nsCString                   entityID;
+  bool                        chooseApplicationCache;
+  nsCString                   appCacheClientID;
+  bool                        allowSpdy;
+};
+
+struct HttpChannelConnectArgs
+{
+  uint32_t channelId;
+};
+
+union HttpChannelCreationArgs
+{
+  HttpChannelOpenArgs;      // For AsyncOpen: the common case.
+  HttpChannelConnectArgs;   // Used for redirected-to channels
+};
+
+//-----------------------------------------------------------------------------
+// FTP IPDL structs
+//-----------------------------------------------------------------------------
+
+struct FTPChannelOpenArgs
+{
+  URIParams uri;
+  uint64_t startPos;
+  nsCString entityID;
+  OptionalInputStreamParams uploadStream;
+};
+
+struct FTPChannelConnectArgs
+{
+  uint32_t channelId;
+};
+
+union FTPChannelCreationArgs
+{
+  FTPChannelOpenArgs;      // For AsyncOpen: the common case.
+  FTPChannelConnectArgs;   // Used for redirected-to channels
+};
+
+} // namespace ipc
+} // namespace mozilla
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -57,17 +57,18 @@ void NeckoChild::DestroyNeckoChild()
     Send__delete__(gNeckoChild); 
     gNeckoChild = nullptr;
     alreadyDestroyed = true;
   }
 }
 
 PHttpChannelChild*
 NeckoChild::AllocPHttpChannel(PBrowserChild* browser,
-                              const SerializedLoadContext& loadContext)
+                              const SerializedLoadContext& loadContext,
+                              const HttpChannelCreationArgs& aOpenArgs)
 {
   // We don't allocate here: instead we always use IPDL constructor that takes
   // an existing HttpChildChannel
   NS_NOTREACHED("AllocPHttpChannel should not be called on child");
   return nullptr;
 }
 
 bool 
@@ -77,17 +78,18 @@ NeckoChild::DeallocPHttpChannel(PHttpCha
 
   HttpChannelChild* child = static_cast<HttpChannelChild*>(channel);
   child->ReleaseIPDLReference();
   return true;
 }
 
 PFTPChannelChild*
 NeckoChild::AllocPFTPChannel(PBrowserChild* aBrowser,
-                             const SerializedLoadContext& aSerialized)
+                             const SerializedLoadContext& aSerialized,
+                             const FTPChannelCreationArgs& aOpenArgs)
 {
   // We don't allocate here: see FTPChannelChild::AsyncOpen()
   NS_RUNTIMEABORT("AllocPFTPChannel should not be called");
   return nullptr;
 }
 
 bool
 NeckoChild::DeallocPFTPChannel(PFTPChannelChild* channel)
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -21,25 +21,28 @@ class NeckoChild :
 public:
   NeckoChild();
   virtual ~NeckoChild();
 
   static void InitNeckoChild();
   static void DestroyNeckoChild();
 
 protected:
-  virtual PHttpChannelChild* AllocPHttpChannel(PBrowserChild*,
-                                              const SerializedLoadContext&);
+  virtual PHttpChannelChild*
+    AllocPHttpChannel(PBrowserChild*, const SerializedLoadContext&,
+                      const HttpChannelCreationArgs& aOpenArgs);
   virtual bool DeallocPHttpChannel(PHttpChannelChild*);
   virtual PCookieServiceChild* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceChild*);
   virtual PWyciwygChannelChild* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelChild*);
-  virtual PFTPChannelChild* AllocPFTPChannel(PBrowserChild* aBrowser,
-                                             const SerializedLoadContext& aSerialized);
+  virtual PFTPChannelChild*
+    AllocPFTPChannel(PBrowserChild* aBrowser,
+                     const SerializedLoadContext& aSerialized,
+                     const FTPChannelCreationArgs& aOpenArgs);
   virtual bool DeallocPFTPChannel(PFTPChannelChild*);
   virtual PWebSocketChild* AllocPWebSocket(PBrowserChild*, const SerializedLoadContext&);
   virtual bool DeallocPWebSocket(PWebSocketChild*);
   virtual PTCPSocketChild* AllocPTCPSocket(const nsString& aHost,
                                            const uint16_t& aPort,
                                            const bool& useSSL,
                                            const nsString& aBinaryType,
                                            PBrowserChild* aBrowser);
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -30,16 +30,22 @@ using mozilla::dom::TCPSocketParent;
 using IPC::SerializedLoadContext;
 
 namespace mozilla {
 namespace net {
 
 // C++ file contents
 NeckoParent::NeckoParent()
 {
+  // Init HTTP protocol handler now since we need atomTable up and running very
+  // early (IPDL argument handling for PHttpChannel constructor needs it) so
+  // normal init (during 1st Http channel request) isn't early enough.
+  nsCOMPtr<nsIProtocolHandler> proto =
+    do_GetService("@mozilla.org/network/protocol;1?name=http");
+
   if (UsingNeckoIPCSecurity()) {
     // cache values for core/packaged apps basepaths
     nsAutoString corePath, webPath;
     nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
     if (appsService) {
       appsService->GetCoreAppsBasePath(corePath);
       appsService->GetWebAppsBasePath(webPath);
     }
@@ -148,17 +154,18 @@ NeckoParent::CreateChannelLoadContext(PB
     aResult = new LoadContext(aSerialized, topFrameElement, appId, inBrowser);
   }
 
   return nullptr;
 }
 
 PHttpChannelParent*
 NeckoParent::AllocPHttpChannel(PBrowserParent* aBrowser,
-                               const SerializedLoadContext& aSerialized)
+                               const SerializedLoadContext& aSerialized,
+                               const HttpChannelCreationArgs& aOpenArgs)
 {
   nsCOMPtr<nsILoadContext> loadContext;
   const char *error = CreateChannelLoadContext(aBrowser, aSerialized,
                                                loadContext);
   if (error) {
     printf_stderr("NeckoParent::AllocPHttpChannel: "
                   "FATAL error: %s: KILLING CHILD PROCESS\n",
                   error);
@@ -173,19 +180,31 @@ NeckoParent::AllocPHttpChannel(PBrowserP
 bool
 NeckoParent::DeallocPHttpChannel(PHttpChannelParent* channel)
 {
   HttpChannelParent *p = static_cast<HttpChannelParent *>(channel);
   p->Release();
   return true;
 }
 
+bool
+NeckoParent::RecvPHttpChannelConstructor(
+                      PHttpChannelParent* aActor,
+                      PBrowserParent* aBrowser,
+                      const SerializedLoadContext& aSerialized,
+                      const HttpChannelCreationArgs& aOpenArgs)
+{
+  HttpChannelParent* p = static_cast<HttpChannelParent*>(aActor);
+  return p->Init(aOpenArgs);
+}
+
 PFTPChannelParent*
 NeckoParent::AllocPFTPChannel(PBrowserParent* aBrowser,
-                              const SerializedLoadContext& aSerialized)
+                              const SerializedLoadContext& aSerialized,
+                              const FTPChannelCreationArgs& aOpenArgs)
 {
   nsCOMPtr<nsILoadContext> loadContext;
   const char *error = CreateChannelLoadContext(aBrowser, aSerialized,
                                                loadContext);
   if (error) {
     printf_stderr("NeckoParent::AllocPFTPChannel: "
                   "FATAL error: %s: KILLING CHILD PROCESS\n",
                   error);
@@ -200,16 +219,27 @@ NeckoParent::AllocPFTPChannel(PBrowserPa
 bool
 NeckoParent::DeallocPFTPChannel(PFTPChannelParent* channel)
 {
   FTPChannelParent *p = static_cast<FTPChannelParent *>(channel);
   p->Release();
   return true;
 }
 
+bool
+NeckoParent::RecvPFTPChannelConstructor(
+                      PFTPChannelParent* aActor,
+                      PBrowserParent* aBrowser,
+                      const SerializedLoadContext& aSerialized,
+                      const FTPChannelCreationArgs& aOpenArgs)
+{
+  FTPChannelParent* p = static_cast<FTPChannelParent*>(aActor);
+  return p->Init(aOpenArgs);
+}
+
 PCookieServiceParent* 
 NeckoParent::AllocPCookieService()
 {
   return new CookieServiceParent();
 }
 
 bool 
 NeckoParent::DeallocPCookieService(PCookieServiceParent* cs)
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -46,25 +46,40 @@ public:
    */
   MOZ_WARN_UNUSED_RESULT
   static const char*
   CreateChannelLoadContext(PBrowserParent* aBrowser,
                            const SerializedLoadContext& aSerialized,
                            nsCOMPtr<nsILoadContext> &aResult);
 
 protected:
-  virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent*,
-                                                const SerializedLoadContext&);
+  virtual PHttpChannelParent*
+    AllocPHttpChannel(PBrowserParent*, const SerializedLoadContext&,
+                      const HttpChannelCreationArgs& aOpenArgs);
+  virtual bool
+    RecvPHttpChannelConstructor(
+                      PHttpChannelParent* aActor,
+                      PBrowserParent* aBrowser,
+                      const SerializedLoadContext& aSerialized,
+                      const HttpChannelCreationArgs& aOpenArgs);
   virtual bool DeallocPHttpChannel(PHttpChannelParent*);
   virtual PCookieServiceParent* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceParent*);
   virtual PWyciwygChannelParent* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelParent*);
-  virtual PFTPChannelParent* AllocPFTPChannel(PBrowserParent* aBrowser,
-                                              const SerializedLoadContext& aSerialized);
+  virtual PFTPChannelParent*
+    AllocPFTPChannel(PBrowserParent* aBrowser,
+                     const SerializedLoadContext& aSerialized,
+                     const FTPChannelCreationArgs& aOpenArgs);
+  virtual bool
+    RecvPFTPChannelConstructor(
+                      PFTPChannelParent* aActor,
+                      PBrowserParent* aBrowser,
+                      const SerializedLoadContext& aSerialized,
+                      const FTPChannelCreationArgs& aOpenArgs);
   virtual bool DeallocPFTPChannel(PFTPChannelParent*);
   virtual PWebSocketParent* AllocPWebSocket(PBrowserParent* browser,
                                             const SerializedLoadContext& aSerialized);
   virtual bool DeallocPWebSocket(PWebSocketParent*);
   virtual PTCPSocketParent* AllocPTCPSocket(const nsString& aHost,
                                             const uint16_t& aPort,
                                             const bool& useSSL,
                                             const nsString& aBinaryType,
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -9,26 +9,29 @@ include protocol PContent;
 include protocol PHttpChannel;
 include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
 include protocol PTCPSocket;
 include protocol PRemoteOpenFile;
+include protocol PBlob; //FIXME: bug #792908
+
 include URIParams;
+include InputStreamParams;
+include NeckoChannelParams;
 
 include "SerializedLoadContext.h";
 
 using IPC::SerializedLoadContext;
 
 namespace mozilla {
 namespace net {
 
-
 //-------------------------------------------------------------------
 sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
@@ -36,19 +39,22 @@ sync protocol PNecko
   manages PTCPSocket;
   manages PRemoteOpenFile;
 
 parent:
   __delete__();
 
   PCookieService();
   PHttpChannel(nullable PBrowser browser,
-               SerializedLoadContext loadContext);
+               SerializedLoadContext loadContext,
+               HttpChannelCreationArgs args);
   PWyciwygChannel();
-  PFTPChannel(PBrowser browser, SerializedLoadContext loadContext);
+  PFTPChannel(PBrowser browser, SerializedLoadContext loadContext,
+              FTPChannelCreationArgs args);
+
   PWebSocket(PBrowser browser, SerializedLoadContext loadContext);
   PTCPSocket(nsString host, uint16_t port, bool useSSL, nsString binaryType,
              nullable PBrowser browser);
 
   // Request that the parent open a file.
   PRemoteOpenFile(URIParams fileuri, nullable PBrowser browser);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
--- a/netwerk/ipc/ipdl.mk
+++ b/netwerk/ipc/ipdl.mk
@@ -1,9 +1,10 @@
 # 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/.
 
 IPDLSRCS =          \
   PNecko.ipdl       \
   PRemoteOpenFile.ipdl \
+  NeckoChannelParams.ipdlh \
   $(NULL)
 
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -164,32 +164,35 @@ FTPChannelChild::AsyncOpen(::nsIStreamLi
   GetCallback(iTabChild);
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
   if (MissingRequiredTabChild(tabChild, "ftp")) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  // FIXME: like bug 558623, merge constructor+SendAsyncOpen into 1 IPC msg
-  gNeckoChild->SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this));
   mListener = listener;
   mListenerContext = aContext;
 
   // add ourselves to the load group. 
   if (mLoadGroup)
     mLoadGroup->AddRequest(this, nullptr);
 
-  URIParams uri;
-  SerializeURI(nsBaseChannel::URI(), uri);
-
   OptionalInputStreamParams uploadStream;
   SerializeInputStream(mUploadStream, uploadStream);
 
-  SendAsyncOpen(uri, mStartPos, mEntityID, uploadStream);
+  FTPChannelOpenArgs openArgs;
+  SerializeURI(nsBaseChannel::URI(), openArgs.uri());
+  openArgs.startPos() = mStartPos;
+  openArgs.entityID() = mEntityID;
+  openArgs.uploadStream() = uploadStream;
+
+  gNeckoChild->
+    SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this),
+                               openArgs);
 
   // The socket transport layer in the chrome process now has a logical ref to
   // us until OnStopRequest is called.
   AddIPDLReference();
 
   mIsPending = true;
   mWasOpened = true;
 
@@ -512,22 +515,23 @@ FTPChannelChild::ConnectParent(uint32_t 
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
 
   // 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();
 
+  FTPChannelConnectArgs connectArgs(id);
+
   if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild,
-                                               IPC::SerializedLoadContext(this)))
+                                               IPC::SerializedLoadContext(this),
+                                               connectArgs)) {
     return NS_ERROR_FAILURE;
-
-  if (!SendConnectChannel(id))
-    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
                                        nsISupports *aContext)
 {
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -55,30 +55,54 @@ NS_IMPL_ISUPPORTS4(FTPChannelParent,
                    nsIParentChannel,
                    nsIInterfaceRequestor,
                    nsIRequestObserver)
 
 //-----------------------------------------------------------------------------
 // FTPChannelParent::PFTPChannelParent
 //-----------------------------------------------------------------------------
 
+//-----------------------------------------------------------------------------
+// FTPChannelParent methods
+//-----------------------------------------------------------------------------
+
 bool
-FTPChannelParent::RecvAsyncOpen(const URIParams& aURI,
-                                const uint64_t& aStartPos,
-                                const nsCString& aEntityID,
-                                const OptionalInputStreamParams& aUploadStream)
+FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
+{
+  switch (aArgs.type()) {
+  case FTPChannelCreationArgs::TFTPChannelOpenArgs:
+  {
+    const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
+    return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream());
+  }
+  case FTPChannelCreationArgs::TFTPChannelConnectArgs:
+  {
+    const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
+    return ConnectChannel(cArgs.channelId());
+  }
+  default:
+    NS_NOTREACHED("unknown open type");
+    return false;
+  }
+}
+
+bool
+FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
+                              const uint64_t& aStartPos,
+                              const nsCString& aEntityID,
+                              const OptionalInputStreamParams& aUploadStream)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri)
       return false;
 
 #ifdef DEBUG
   nsCString uriSpec;
   uri->GetSpec(uriSpec);
-  LOG(("FTPChannelParent RecvAsyncOpen [this=%p uri=%s]\n",
+  LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
        this, uriSpec.get()));
 #endif
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
@@ -112,17 +136,17 @@ FTPChannelParent::RecvAsyncOpen(const UR
   rv = mChannel->AsyncOpen(this, nullptr);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
   
   return true;
 }
 
 bool
-FTPChannelParent::RecvConnectChannel(const uint32_t& channelId)
+FTPChannelParent::ConnectChannel(const uint32_t& channelId)
 {
   nsresult rv;
 
   LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
   if (NS_SUCCEEDED(rv))
--- a/netwerk/protocol/ftp/FTPChannelParent.h
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -29,22 +29,27 @@ public:
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIPARENTCHANNEL
   NS_DECL_NSIINTERFACEREQUESTOR
 
   FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatus aOverrideStatus);
   virtual ~FTPChannelParent();
 
+  bool Init(const FTPChannelCreationArgs& aOpenArgs);
+
 protected:
-  virtual bool RecvAsyncOpen(const URIParams& uri,
-                             const uint64_t& startPos,
-                             const nsCString& entityID,
-                             const OptionalInputStreamParams& uploadStream) MOZ_OVERRIDE;
-  virtual bool RecvConnectChannel(const uint32_t& channelId) MOZ_OVERRIDE;
+  bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
+                   const nsCString& aEntityID,
+                   const OptionalInputStreamParams& aUploadStream);
+
+  // used to connect redirected-to channel in parent with just created
+  // ChildChannel.  Used during HTTP->FTP redirects.
+  bool ConnectChannel(const uint32_t& channelId);
+
   virtual bool RecvCancel(const nsresult& status) MOZ_OVERRIDE;
   virtual bool RecvSuspend() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   nsRefPtr<nsFtpChannel> mChannel;
 
--- a/netwerk/protocol/ftp/PFTPChannel.ipdl
+++ b/netwerk/protocol/ftp/PFTPChannel.ipdl
@@ -4,38 +4,37 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include InputStreamParams;
 include URIParams;
 
+//FIXME: bug #792908 (NeckoChannelParams already included by PNecko)
+include NeckoChannelParams;
 include protocol PBlob; //FIXME: bug #792908
 
 include "SerializedLoadContext.h";
 
 using PRTime;
 
 namespace mozilla {
 namespace net {
 
 async protocol PFTPChannel
 {
   manager PNecko;
 
 parent:
+  // Note: channels are opened during construction, so no open method here:
+  // see PNecko.ipdl
+
   __delete__();
 
-  AsyncOpen(URIParams uri,
-            uint64_t startPos,
-            nsCString entityID,
-            OptionalInputStreamParams uploadStream);
-
-  ConnectChannel(uint32_t channelId);
   Cancel(nsresult status);
   Suspend();
   Resume();
 
 child:
   OnStartRequest(int64_t aContentLength, nsCString aContentType,
                  PRTime aLastModified, nsCString aEntityID, URIParams aURI);
   OnDataAvailable(nsCString data, uint64_t offset, uint32_t count);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -812,24 +812,24 @@ HttpChannelChild::ConnectParent(uint32_t
   if (MissingRequiredTabChild(tabChild, "http")) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   // 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();
 
-  if (!gNeckoChild->SendPHttpChannelConstructor(
-                      this, tabChild, IPC::SerializedLoadContext(this))) {
+  HttpChannelConnectArgs connectArgs(id);
+  if (!gNeckoChild->
+        SendPHttpChannelConstructor(this, tabChild,
+                                    IPC::SerializedLoadContext(this),
+                                    connectArgs)) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!SendConnectChannel(id))
-    return NS_ERROR_FAILURE;
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
                                         nsISupports *aContext)
 {
   LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this));
@@ -1043,53 +1043,57 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
       }
     }
   }
 
   //
   // Send request to the chrome process...
   //
 
-  // FIXME: bug 558623: Combine constructor and SendAsyncOpen into one IPC msg
-
   mozilla::dom::TabChild* tabChild = nullptr;
   nsCOMPtr<nsITabChild> iTabChild;
   GetCallback(iTabChild);
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
   if (MissingRequiredTabChild(tabChild, "http")) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
+  HttpChannelOpenArgs openArgs;
+  // No access to HttpChannelOpenArgs members, but they each have a
+  // function with the struct name that returns a ref.
+  SerializeURI(mURI, openArgs.uri());
+  SerializeURI(mOriginalURI, openArgs.original());
+  SerializeURI(mDocumentURI, openArgs.doc());
+  SerializeURI(mReferrer, openArgs.referrer());
+  SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
+  openArgs.loadFlags() = mLoadFlags;
+  openArgs.requestHeaders() = mClientSetRequestHeaders;
+  openArgs.requestMethod() = mRequestHead.Method();
+  SerializeInputStream(mUploadStream, openArgs.uploadStream());
+  openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders;
+  openArgs.priority() = mPriority;
+  openArgs.redirectionLimit() = mRedirectionLimit;
+  openArgs.allowPipelining() = mAllowPipelining;
+  openArgs.forceAllowThirdPartyCookie() = mForceAllowThirdPartyCookie;
+  openArgs.resumeAt() = mSendResumeAt;
+  openArgs.startPos() = mStartPos;
+  openArgs.entityID() = mEntityID;
+  openArgs.chooseApplicationCache() = mChooseApplicationCache;
+  openArgs.appCacheClientID() = appCacheClientId;
+  openArgs.allowSpdy() = mAllowSpdy;
+
   // 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();
 
   gNeckoChild->SendPHttpChannelConstructor(this, tabChild,
-                                           IPC::SerializedLoadContext(this));
-
-  URIParams uri;
-  SerializeURI(mURI, uri);
-
-  OptionalURIParams originalURI, documentURI, referrer, redirectURI;
-  SerializeURI(mOriginalURI, originalURI);
-  SerializeURI(mDocumentURI, documentURI);
-  SerializeURI(mReferrer, referrer);
-  SerializeURI(mAPIRedirectToURI, redirectURI);
-
-  OptionalInputStreamParams uploadStream;
-  SerializeInputStream(mUploadStream, uploadStream);
-
-  SendAsyncOpen(uri, originalURI, documentURI, referrer, redirectURI, mLoadFlags,
-                mClientSetRequestHeaders, mRequestHead.Method(), uploadStream,
-                mUploadStreamHasHeaders, mPriority, mRedirectionLimit,
-                mAllowPipelining, mForceAllowThirdPartyCookie, mSendResumeAt,
-                mStartPos, mEntityID, mChooseApplicationCache,
-                appCacheClientId, mAllowSpdy);
+                                           IPC::SerializedLoadContext(this),
+                                           openArgs);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -64,16 +64,43 @@ 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;
 }
 
+bool
+HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
+{
+  switch (aArgs.type()) {
+  case HttpChannelCreationArgs::THttpChannelOpenArgs:
+  {
+    const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
+    return DoAsyncOpen(a.uri(), a.original(), a.doc(), a.referrer(),
+                       a.apiRedirectTo(), a.loadFlags(), a.requestHeaders(),
+                       a.requestMethod(), a.uploadStream(),
+                       a.uploadStreamHasHeaders(), a.priority(),
+                       a.redirectionLimit(), a.allowPipelining(),
+                       a.forceAllowThirdPartyCookie(), a.resumeAt(),
+                       a.startPos(), a.entityID(), a.chooseApplicationCache(),
+                       a.appCacheClientID(), a.allowSpdy());
+  }
+  case HttpChannelCreationArgs::THttpChannelConnectArgs:
+  {
+    const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
+    return ConnectChannel(cArgs.channelId());
+  }
+  default:
+    NS_NOTREACHED("unknown open type");
+    return false;
+  }
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS6(HttpChannelParent,
                    nsIInterfaceRequestor,
                    nsIProgressEventSink,
                    nsIRequestObserver,
@@ -106,17 +133,17 @@ HttpChannelParent::GetInterface(const ns
   return QueryInterface(aIID, result);
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::PHttpChannelParent
 //-----------------------------------------------------------------------------
 
 bool
-HttpChannelParent::RecvAsyncOpen(const URIParams&           aURI,
+HttpChannelParent::DoAsyncOpen(  const URIParams&           aURI,
                                  const OptionalURIParams&   aOriginalURI,
                                  const OptionalURIParams&   aDocURI,
                                  const OptionalURIParams&   aReferrerURI,
                                  const OptionalURIParams&   aAPIRedirectToURI,
                                  const uint32_t&            loadFlags,
                                  const RequestHeaderTuples& requestHeaders,
                                  const nsHttpAtom&          requestMethod,
                                  const OptionalInputStreamParams& uploadStream,
@@ -235,17 +262,17 @@ HttpChannelParent::RecvAsyncOpen(const U
   rv = httpChan->AsyncOpen(channelListener, nullptr);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
   return true;
 }
 
 bool
-HttpChannelParent::RecvConnectChannel(const uint32_t& channelId)
+HttpChannelParent::ConnectChannel(const uint32_t& channelId)
 {
   nsresult rv;
 
   LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
   rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(mChannel));
   LOG(("  found channel %p, rv=%08x", mChannel.get(), rv));
 
   if (mPBOverride != kPBOverride_Unset) {
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -44,39 +44,44 @@ public:
   NS_DECL_NSIPROGRESSEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
 
   HttpChannelParent(mozilla::dom::PBrowserParent* iframeEmbedding,
                     nsILoadContext* aLoadContext,
                     PBOverrideStatus aStatus);
   virtual ~HttpChannelParent();
 
+  bool Init(const HttpChannelCreationArgs& aOpenArgs);
+
 protected:
-  virtual bool RecvAsyncOpen(const URIParams&           uri,
-                             const OptionalURIParams&   originalUri,
-                             const OptionalURIParams&   docUri,
-                             const OptionalURIParams&   referrerUri,
-                             const OptionalURIParams&   internalRedirectUri,
-                             const uint32_t&            loadFlags,
-                             const RequestHeaderTuples& requestHeaders,
-                             const nsHttpAtom&          requestMethod,
-                             const OptionalInputStreamParams& uploadStream,
-                             const bool&              uploadStreamHasHeaders,
-                             const uint16_t&            priority,
-                             const uint8_t&             redirectionLimit,
-                             const bool&              allowPipelining,
-                             const bool&              forceAllowThirdPartyCookie,
-                             const bool&                doResumeAt,
-                             const uint64_t&            startPos,
-                             const nsCString&           entityID,
-                             const bool&                chooseApplicationCache,
-                             const nsCString&           appCacheClientID,
-                             const bool&                allowSpdy) MOZ_OVERRIDE;
+  // used to connect redirected-to channel in parent with just created
+  // ChildChannel.  Used during redirects.
+  bool ConnectChannel(const uint32_t& channelId);
 
-  virtual bool RecvConnectChannel(const uint32_t& channelId);
+  bool DoAsyncOpen(const URIParams&           uri,
+                   const OptionalURIParams&   originalUri,
+                   const OptionalURIParams&   docUri,
+                   const OptionalURIParams&   referrerUri,
+                   const OptionalURIParams&   internalRedirectUri,
+                   const uint32_t&            loadFlags,
+                   const RequestHeaderTuples& requestHeaders,
+                   const nsHttpAtom&          requestMethod,
+                   const OptionalInputStreamParams& uploadStream,
+                   const bool&                uploadStreamHasHeaders,
+                   const uint16_t&            priority,
+                   const uint8_t&             redirectionLimit,
+                   const bool&                allowPipelining,
+                   const bool&                forceAllowThirdPartyCookie,
+                   const bool&                doResumeAt,
+                   const uint64_t&            startPos,
+                   const nsCString&           entityID,
+                   const bool&                chooseApplicationCache,
+                   const nsCString&           appCacheClientID,
+                   const bool&                allowSpdy);
+
   virtual bool RecvSetPriority(const uint16_t& priority);
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
   virtual bool RecvSuspend();
   virtual bool RecvResume();
   virtual bool RecvCancel(const nsresult& status);
   virtual bool RecvRedirect2Verify(const nsresult& result,
                                    const RequestHeaderTuples& changedHeaders,
                                    const OptionalURIParams&   apiRedirectUri);
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -26,43 +26,18 @@ namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
 
 parent:
-  AsyncOpen(URIParams           uri,
-            // - TODO: bug 571161: unclear if any HTTP channel clients ever
-            // set originalURI != uri (about:credits?); also not clear if
-            // chrome channel would ever need to know.  Get rid of next arg?
-            OptionalURIParams   original,
-            OptionalURIParams   doc,
-            OptionalURIParams   referrer,
-            OptionalURIParams   apiRedirectTo,
-            uint32_t            loadFlags,
-            RequestHeaderTuples requestHeaders,
-            nsHttpAtom          requestMethod,
-            OptionalInputStreamParams uploadStream,
-            bool                uploadStreamHasHeaders,
-            uint16_t            priority,
-            uint8_t             redirectionLimit,
-            bool                allowPipelining,
-            bool                forceAllowThirdPartyCookie,
-            bool                resumeAt,
-            uint64_t            startPos,
-            nsCString           entityID,
-            bool                chooseApplicationCache,
-            nsCString           appCacheClientID,
-            bool                allowSpdy);
-
-  // Used to connect redirected-to channel on the parent with redirected-to
-  // channel on the child.
-  ConnectChannel(uint32_t channelId);
+  // Note: channels are opened during construction, so no open method here:
+  // see PNecko.ipdl
 
   SetPriority(uint16_t priority);
 
   SetCacheTokenCachedCharset(nsCString charset);
 
   UpdateAssociatedContentSecurity(int32_t broken,
                                   int32_t no);
   Suspend();
--- a/netwerk/protocol/http/PHttpChannelParams.h
+++ b/netwerk/protocol/http/PHttpChannelParams.h
@@ -20,16 +20,22 @@
 
 namespace mozilla {
 namespace net {
 
 struct RequestHeaderTuple {
   nsCString mHeader;
   nsCString mValue;
   bool      mMerge;
+
+  bool operator ==(const RequestHeaderTuple &other) const {
+    return mHeader.Equals(other.mHeader) &&
+           mValue.Equals(other.mValue) &&
+           mMerge == other.mMerge;
+  }
 };
 
 typedef nsTArray<RequestHeaderTuple> RequestHeaderTuples;
 
 } // namespace net
 } // namespace mozilla
 
 namespace IPC {
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -23,16 +23,18 @@ public:
                          , mContentLength(UINT64_MAX)
                          , mCacheControlNoStore(false)
                          , mCacheControlNoCache(false)
                          , mPragmaNoCache(false) {}
 
     const nsHttpHeaderArray & Headers()   const { return mHeaders; }
     nsHttpHeaderArray    &Headers()             { return mHeaders; }
     nsHttpVersion         Version()       const { return mVersion; }
+// X11's Xlib.h #defines 'Status' to 'int' on some systems!
+#undef Status
     uint16_t              Status()        const { return mStatus; }
     const nsAFlatCString &StatusText()    const { return mStatusText; }
     int64_t               ContentLength() const { return mContentLength; }
     const nsAFlatCString &ContentType()   const { return mContentType; }
     const nsAFlatCString &ContentCharset() const { return mContentCharset; }
     bool                  NoStore() const { return mCacheControlNoStore; }
     bool                  NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
     /**