Bug 1263991 part 6: Add e10s support for incoming websocket connections to FlyWebPublishedServer. r=baku,michal
authorJonas Sicking <jonas@sicking.cc>
Tue, 07 Jun 2016 02:46:03 -0700
changeset 300996 c5ac946a987f9c48d5ce2c54dc8507717fc5d29a
parent 300995 33bbb14d3adc41156873970c2c6fefa60ccc6d8d
child 300997 1b8f7b8f94c48e6da002d67df1df06b48daa74fe
push id19599
push usercbook@mozilla.com
push dateWed, 08 Jun 2016 10:16:21 +0000
treeherderfx-team@81f4cc3f6f4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, michal
bugs1263991
milestone50.0a1
Bug 1263991 part 6: Add e10s support for incoming websocket connections to FlyWebPublishedServer. r=baku,michal
dom/flyweb/FlyWebPublishedServer.cpp
dom/flyweb/FlyWebPublishedServerIPC.h
dom/flyweb/HttpServer.cpp
dom/flyweb/HttpServer.h
dom/flyweb/PFlyWebPublishedServer.ipdl
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/websocket/IPCTransportProvider.cpp
netwerk/protocol/websocket/IPCTransportProvider.h
netwerk/protocol/websocket/PTransportProvider.ipdl
netwerk/protocol/websocket/PWebSocket.ipdl
netwerk/protocol/websocket/WebSocketChannelChild.cpp
netwerk/protocol/websocket/WebSocketChannelParent.cpp
netwerk/protocol/websocket/WebSocketChannelParent.h
netwerk/protocol/websocket/moz.build
netwerk/protocol/websocket/nsITransportProvider.idl
--- a/dom/flyweb/FlyWebPublishedServer.cpp
+++ b/dom/flyweb/FlyWebPublishedServer.cpp
@@ -8,19 +8,22 @@
 #include "mozilla/dom/FlyWebPublishBinding.h"
 #include "mozilla/dom/FlyWebService.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/FlyWebServerEvents.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
+#include "nsCharSeparatedTokenizer.h"
 #include "nsGlobalWindow.h"
 #include "WebSocketChannel.h"
 
 namespace mozilla {
 namespace dom {
 
 static LazyLogModule gFlyWebPublishedServerLog("FlyWebPublishedServer");
 #undef LOG_I
@@ -290,16 +293,36 @@ FlyWebPublishedServerChild::RecvFetchReq
 
   RefPtr<InternalRequest> request = new InternalRequest(aRequest);
   mPendingRequests.Put(request, aRequestId);
   FireFetchEvent(request);
 
   return true;
 }
 
+bool
+FlyWebPublishedServerChild::RecvWebSocketRequest(const IPCInternalRequest& aRequest,
+                                                 const uint64_t& aRequestId,
+                                                 PTransportProviderChild* aProvider)
+{
+  LOG_I("FlyWebPublishedServerChild::RecvWebSocketRequest(%p)", this);
+
+  RefPtr<InternalRequest> request = new InternalRequest(aRequest);
+  mPendingRequests.Put(request, aRequestId);
+
+  // Not addreffing here. The addref was already done when the
+  // PTransportProvider child constructor original ran.
+  mPendingTransportProviders.Put(aRequestId,
+    dont_AddRef(static_cast<TransportProviderChild*>(aProvider)));
+
+  FireWebsocketEvent(request);
+
+  return true;
+}
+
 void
 FlyWebPublishedServerChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOG_I("FlyWebPublishedServerChild::ActorDestroy(%p)", this);
 
   mActorDestroyed = true;
 }
 
@@ -323,31 +346,80 @@ FlyWebPublishedServerChild::OnFetchRespo
   aResponse->ToIPC(&ipcResp, Manager(), autoStream);
   Unused << SendFetchResponse(ipcResp, id);
   if (autoStream) {
     autoStream->TakeOptionalValue();
   }
 }
 
 already_AddRefed<nsITransportProvider>
-FlyWebPublishedServerChild::OnWebSocketAcceptInternal(InternalRequest* aConnectRequest,
+FlyWebPublishedServerChild::OnWebSocketAcceptInternal(InternalRequest* aRequest,
                                                       const Optional<nsAString>& aProtocol,
                                                       ErrorResult& aRv)
 {
   LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p)", this);
 
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  if (mActorDestroyed) {
+    LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p) - No actor!", this);
+    return nullptr;
+  }
+
+  uint64_t id = mPendingRequests.Get(aRequest);
+  MOZ_ASSERT(id);
+  mPendingRequests.Remove(aRequest);
+
+  RefPtr<TransportProviderChild> provider;
+  mPendingTransportProviders.Remove(id, getter_AddRefs(provider));
+
+  nsString protocol;
+  if (aProtocol.WasPassed()) {
+    protocol = aProtocol.Value();
+
+    nsAutoCString reqProtocols;
+    aRequest->Headers()->
+      Get(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv);
+    if (!ContainsToken(reqProtocols, NS_ConvertUTF16toUTF8(protocol))) {
+      // Should throw a better error here
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+  } else {
+    protocol.SetIsVoid(true);
+  }
+
+  Unused << SendWebSocketAccept(protocol, id);
+
+  return provider.forget();
 }
 
 void
-FlyWebPublishedServerChild::OnWebSocketResponse(InternalRequest* aConnectRequest,
+FlyWebPublishedServerChild::OnWebSocketResponse(InternalRequest* aRequest,
                                                 InternalResponse* aResponse)
 {
-  // Send ipdl message to parent
+  LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p)", this);
+
+  if (mActorDestroyed) {
+    LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p) - No actor!", this);
+    return;
+  }
+
+  uint64_t id = mPendingRequests.Get(aRequest);
+  MOZ_ASSERT(id);
+  mPendingRequests.Remove(aRequest);
+
+  mPendingTransportProviders.Remove(id);
+
+  IPCInternalResponse ipcResp;
+  UniquePtr<mozilla::ipc::AutoIPCStream> autoStream;
+  aResponse->ToIPC(&ipcResp, Manager(), autoStream);
+
+  Unused << SendWebSocketResponse(ipcResp, id);
+  if (autoStream) {
+    autoStream->TakeOptionalValue();
+  }
 }
 
 void
 FlyWebPublishedServerChild::Close()
 {
   LOG_I("FlyWebPublishedServerChild::Close(%p)", this);
 
   FlyWebPublishedServer::Close();
@@ -392,16 +464,18 @@ FlyWebPublishedServerParent::FlyWebPubli
       mPublishedServer = static_cast<FlyWebPublishedServerImpl*>(aServer);
       if (mActorDestroyed) {
         mPublishedServer->Close();
         return;
       }
 
       mPublishedServer->AddEventListener(NS_LITERAL_STRING("fetch"),
                                          this, false, false, 2);
+      mPublishedServer->AddEventListener(NS_LITERAL_STRING("websocket"),
+                                         this, false, false, 2);
       mPublishedServer->AddEventListener(NS_LITERAL_STRING("close"),
                                          this, false, false, 2);
       Unused << SendServerReady(NS_OK);
     },
     [this, self] (nsresult aStatus) {
       MOZ_ASSERT(NS_FAILED(aStatus));
       if (!mActorDestroyed) {
         Unused << SendServerReady(aStatus);
@@ -430,55 +504,127 @@ FlyWebPublishedServerParent::HandleEvent
     mPendingRequests.Put(id, request);
 
     IPCInternalRequest ipcReq;
     request->ToIPC(&ipcReq);
     Unused << SendFetchRequest(ipcReq, id);
     return NS_OK;
   }
 
+  if (type.EqualsLiteral("websocket")) {
+    RefPtr<InternalRequest> request =
+      static_cast<FlyWebWebSocketEvent*>(aEvent)->Request()->GetInternalRequest();
+    uint64_t id = mNextRequestId++;
+    mPendingRequests.Put(id, request);
+
+    RefPtr<TransportProviderParent> provider =
+      static_cast<TransportProviderParent*>(
+        mozilla::net::gNeckoParent->SendPTransportProviderConstructor());
+
+    IPCInternalRequest ipcReq;
+    request->ToIPC(&ipcReq);
+    Unused << SendWebSocketRequest(ipcReq, id, provider);
+
+    mPendingTransportProviders.Put(id, provider.forget());
+    return NS_OK;
+  }
+
   MOZ_CRASH("Unknown event type");
 
   return NS_OK;
 }
 
 bool
 FlyWebPublishedServerParent::RecvFetchResponse(const IPCInternalResponse& aResponse,
                                                const uint64_t& aRequestId)
 {
-  RefPtr<InternalRequest> request = mPendingRequests.GetWeak(aRequestId);
-  mPendingRequests.Remove(aRequestId);
+  RefPtr<InternalRequest> request;
+  mPendingRequests.Remove(aRequestId, getter_AddRefs(request));
   if (!request) {
      static_cast<ContentParent*>(Manager())->KillHard("unknown request id");
      return false;
   }
 
   RefPtr<InternalResponse> response = InternalResponse::FromIPC(aResponse);
 
   mPublishedServer->OnFetchResponse(request, response);
 
   return true;
 }
 
+bool
+FlyWebPublishedServerParent::RecvWebSocketResponse(const IPCInternalResponse& aResponse,
+                                                   const uint64_t& aRequestId)
+{
+  mPendingTransportProviders.Remove(aRequestId);
+
+  RefPtr<InternalRequest> request;
+  mPendingRequests.Remove(aRequestId, getter_AddRefs(request));
+  if (!request) {
+     static_cast<ContentParent*>(Manager())->KillHard("unknown websocket request id");
+     return false;
+  }
+
+  RefPtr<InternalResponse> response = InternalResponse::FromIPC(aResponse);
+
+  mPublishedServer->OnWebSocketResponse(request, response);
+
+  return true;
+}
+
+bool
+FlyWebPublishedServerParent::RecvWebSocketAccept(const nsString& aProtocol,
+                                                 const uint64_t& aRequestId)
+{
+  RefPtr<TransportProviderParent> providerIPC;
+  mPendingTransportProviders.Remove(aRequestId, getter_AddRefs(providerIPC));
+
+  RefPtr<InternalRequest> request;
+  mPendingRequests.Remove(aRequestId, getter_AddRefs(request));
+
+  if (!request || !providerIPC) {
+     static_cast<ContentParent*>(Manager())->KillHard("unknown websocket request id");
+     return false;
+  }
+
+  Optional<nsAString> protocol;
+  if (!aProtocol.IsVoid()) {
+    protocol = &aProtocol;
+  }
+
+  ErrorResult result;
+  nsCOMPtr<nsITransportProvider> providerServer =
+    mPublishedServer->OnWebSocketAcceptInternal(request, protocol, result);
+  if (result.Failed()) {
+    return false;
+  }
+
+  providerServer->SetListener(providerIPC);
+
+  return true;
+}
+
 void
 FlyWebPublishedServerParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOG_I("FlyWebPublishedServerParent::ActorDestroy(%p)", this);
 
   mActorDestroyed = true;
 }
 
 bool
 FlyWebPublishedServerParent::Recv__delete__()
 {
   LOG_I("FlyWebPublishedServerParent::Recv__delete__(%p)", this);
 
   if (mPublishedServer) {
     mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("fetch"),
                                           this, false);
+    mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("websocket"),
+                                          this, false);
     mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("close"),
                                           this, false);
     mPublishedServer->Close();
     mPublishedServer = nullptr;
   }
   return true;
 }
 
--- a/dom/flyweb/FlyWebPublishedServerIPC.h
+++ b/dom/flyweb/FlyWebPublishedServerIPC.h
@@ -14,16 +14,21 @@
 #include "mozilla/MozPromise.h"
 #include "nsICancelable.h"
 #include "nsIDOMEventListener.h"
 #include "nsISupportsImpl.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
+namespace net {
+class TransportProviderParent;
+class TransportProviderChild;
+}
+
 namespace dom {
 
 class FlyWebPublishedServerParent;
 
 class FlyWebPublishedServerImpl final : public FlyWebPublishedServer
                                       , public HttpServerListener
 {
 public:
@@ -92,16 +97,19 @@ public:
   FlyWebPublishedServerChild(nsPIDOMWindowInner* aOwner,
                              const nsAString& aName,
                              const FlyWebPublishOptions& aOptions);
 
   virtual bool RecvServerReady(const nsresult& aStatus) override;
   virtual bool RecvServerClose() override;
   virtual bool RecvFetchRequest(const IPCInternalRequest& aRequest,
                                 const uint64_t& aRequestId) override;
+  virtual bool RecvWebSocketRequest(const IPCInternalRequest& aRequest,
+                                    const uint64_t& aRequestId,
+                                    PTransportProviderChild* aProvider) override;
 
   virtual void OnFetchResponse(InternalRequest* aRequest,
                                InternalResponse* aResponse) override;
   virtual void OnWebSocketResponse(InternalRequest* aConnectRequest,
                                    InternalResponse* aResponse) override;
   virtual already_AddRefed<nsITransportProvider>
     OnWebSocketAcceptInternal(InternalRequest* aConnectRequest,
                               const Optional<nsAString>& aProtocol,
@@ -110,16 +118,18 @@ public:
   virtual void Close() override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   ~FlyWebPublishedServerChild() {}
 
   nsDataHashtable<nsRefPtrHashKey<InternalRequest>, uint64_t> mPendingRequests;
+  nsRefPtrHashtable<nsUint64HashKey, TransportProviderChild>
+    mPendingTransportProviders;
   bool mActorDestroyed;
 };
 
 class FlyWebPublishedServerParent final : public PFlyWebPublishedServerParent
                                         , public nsIDOMEventListener
 {
 public:
   FlyWebPublishedServerParent(const nsAString& aName,
@@ -132,21 +142,29 @@ private:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool
   Recv__delete__() override;
   virtual bool
   RecvFetchResponse(const IPCInternalResponse& aResponse,
                     const uint64_t& aRequestId) override;
+  virtual bool
+  RecvWebSocketResponse(const IPCInternalResponse& aResponse,
+                        const uint64_t& aRequestId) override;
+  virtual bool
+  RecvWebSocketAccept(const nsString& aProtocol,
+                      const uint64_t& aRequestId) override;
 
   ~FlyWebPublishedServerParent() {}
 
   bool mActorDestroyed;
   uint64_t mNextRequestId;
   nsRefPtrHashtable<nsUint64HashKey, InternalRequest> mPendingRequests;
+  nsRefPtrHashtable<nsUint64HashKey, TransportProviderParent>
+    mPendingTransportProviders;
   RefPtr<FlyWebPublishedServerImpl> mPublishedServer;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FlyWebPublishedServerIPC_h
--- a/dom/flyweb/HttpServer.cpp
+++ b/dom/flyweb/HttpServer.cpp
@@ -255,16 +255,23 @@ HttpServer::TransportProvider::SetListen
 
   mListener = aListener;
 
   MaybeNotify();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP_(PTransportProviderChild*)
+HttpServer::TransportProvider::GetIPCChild()
+{
+  MOZ_CRASH("Don't call this in parent process");
+  return nullptr;
+}
+
 void
 HttpServer::TransportProvider::SetTransport(nsISocketTransport* aTransport,
                                             nsIAsyncInputStream* aInput,
                                             nsIAsyncOutputStream* aOutput)
 {
   MOZ_ASSERT(!mTransport);
   MOZ_ASSERT(aTransport && aInput && aOutput);
 
@@ -487,17 +494,17 @@ HttpServer::Connection::ConsumeInput(con
       mCurrentRequestBody = nullptr;
       mState = eRequestLine;
     }
   }
 
   return NS_OK;
 }
 
-static bool
+bool
 ContainsToken(const nsCString& aList, const nsCString& aToken)
 {
   nsCCharSeparatedTokenizer tokens(aList, ',');
   bool found = false;
   while (!found && tokens.hasMoreTokens()) {
     found = tokens.nextToken().Equals(aToken);
   }
   return found;
--- a/dom/flyweb/HttpServer.h
+++ b/dom/flyweb/HttpServer.h
@@ -18,16 +18,19 @@
 #include "nsITransportProvider.h"
 #include "nsILocalCertService.h"
 
 class nsIX509Cert;
 
 namespace mozilla {
 namespace dom {
 
+extern bool
+ContainsToken(const nsCString& aList, const nsCString& aToken);
+
 class InternalRequest;
 class InternalResponse;
 
 class HttpServerListener
 {
 public:
   // switch to NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING when that lands
   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
--- a/dom/flyweb/PFlyWebPublishedServer.ipdl
+++ b/dom/flyweb/PFlyWebPublishedServer.ipdl
@@ -2,32 +2,37 @@
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include protocol PSendStream;
 include protocol PFileDescriptorSet;
+include protocol PTransportProvider;
 include FetchTypes;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 namespace mozilla {
 namespace dom {
 
 async protocol PFlyWebPublishedServer
 {
   manager PContent;
 
 child:
   async ServerReady(nsresult aStatus);
   async FetchRequest(IPCInternalRequest aRequest, uint64_t aRequestId);
+  async WebSocketRequest(IPCInternalRequest aRequest, uint64_t aRequestId,
+                         PTransportProvider aProvider);
   async ServerClose();
 
 parent:
   async __delete__();
 
   async FetchResponse(IPCInternalResponse aResponse, uint64_t aRequestId);
+  async WebSocketResponse(IPCInternalResponse aResponse, uint64_t aRequestId);
+  async WebSocketAccept(nsString aProtocol, uint64_t aRequestId);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
 #include "mozilla/net/WebSocketEventListenerChild.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/RemoteOpenFileChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
+#include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerChild.h"
 #include "mozilla/net/RtspChannelChild.h"
 #endif
 #include "SerializedLoadContext.h"
@@ -322,16 +323,32 @@ NeckoChild::AllocPChannelDiverterChild(c
 
 bool
 NeckoChild::DeallocPChannelDiverterChild(PChannelDiverterChild* child)
 {
   delete static_cast<ChannelDiverterChild*>(child);
   return true;
 }
 
+PTransportProviderChild*
+NeckoChild::AllocPTransportProviderChild()
+{
+  // This refcount is transferred to the receiver of the message that
+  // includes the PTransportProviderChild actor.
+  RefPtr<TransportProviderChild> res = new TransportProviderChild();
+
+  return res.forget().take();
+}
+
+bool
+NeckoChild::DeallocPTransportProviderChild(PTransportProviderChild* aActor)
+{
+  return true;
+}
+
 bool
 NeckoChild::RecvAsyncAuthPromptForNestedFrame(const TabId& aNestedFrameId,
                                               const nsCString& aUri,
                                               const nsString& aRealm,
                                               const uint64_t& aCallbackId)
 {
   RefPtr<dom::TabChild> tabChild = dom::TabChild::FindTabChild(aNestedFrameId);
   if (!tabChild) {
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -70,16 +70,20 @@ protected:
   virtual PRtspChannelChild*
     AllocPRtspChannelChild(const RtspChannelConnectArgs& aArgs)
                            override;
   virtual bool DeallocPRtspChannelChild(PRtspChannelChild*) override;
   virtual PChannelDiverterChild*
   AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) override;
   virtual bool
   DeallocPChannelDiverterChild(PChannelDiverterChild* actor) override;
+  virtual PTransportProviderChild*
+  AllocPTransportProviderChild() override;
+  virtual bool
+  DeallocPTransportProviderChild(PTransportProviderChild* aActor) override;
   virtual bool RecvAsyncAuthPromptForNestedFrame(const TabId& aNestedFrameId,
                                                  const nsCString& aUri,
                                                  const nsString& aRealm,
                                                  const uint64_t& aCallbackId) override;
   virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) override;
   virtual PWebSocketEventListenerChild*
     AllocPWebSocketEventListenerChild(const uint64_t& aInnerWindowID) override;
   virtual bool DeallocPWebSocketEventListenerChild(PWebSocketEventListenerChild*) override;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/net/DataChannelParent.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerParent.h"
 #include "mozilla/net/RtspChannelParent.h"
 #endif
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/RemoteOpenFileParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
+#include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
 #include "mozilla/dom/network/TCPServerSocketParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/LoadContext.h"
@@ -781,16 +782,31 @@ NeckoParent::RecvPChannelDiverterConstru
 
 bool
 NeckoParent::DeallocPChannelDiverterParent(PChannelDiverterParent* parent)
 {
   delete static_cast<ChannelDiverterParent*>(parent);
   return true;
 }
 
+PTransportProviderParent*
+NeckoParent::AllocPTransportProviderParent()
+{
+  RefPtr<TransportProviderParent> res = new TransportProviderParent();
+  return res.forget().take();
+}
+
+bool
+NeckoParent::DeallocPTransportProviderParent(PTransportProviderParent* aActor)
+{
+  RefPtr<TransportProviderParent> provider =
+    dont_AddRef(static_cast<TransportProviderParent*>(aActor));
+  return true;
+}
+
 void
 NeckoParent::CloneManagees(ProtocolBase* aSource,
                          mozilla::ipc::ProtocolCloneContext* aCtx)
 {
   aCtx->SetNeckoParent(this); // For cloning protocols managed by this.
   PNeckoParent::CloneManagees(aSource, aCtx);
 }
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -202,16 +202,20 @@ protected:
 
   virtual PChannelDiverterParent*
   AllocPChannelDiverterParent(const ChannelDiverterArgs& channel) override;
   virtual bool
   RecvPChannelDiverterConstructor(PChannelDiverterParent* actor,
                                   const ChannelDiverterArgs& channel) override;
   virtual bool DeallocPChannelDiverterParent(PChannelDiverterParent* actor)
                                                                 override;
+  virtual PTransportProviderParent*
+  AllocPTransportProviderParent() override;
+  virtual bool
+  DeallocPTransportProviderParent(PTransportProviderParent* aActor) override;
 
   virtual bool RecvOnAuthAvailable(const uint64_t& aCallbackId,
                                    const nsString& aUser,
                                    const nsString& aPassword,
                                    const nsString& aDomain) override;
   virtual bool RecvOnAuthCancelled(const uint64_t& aCallbackId,
                                    const bool& aUserCancel) override;
 
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -17,16 +17,17 @@ include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PUDPSocket;
 include protocol PRemoteOpenFile;
 include protocol PDNSRequest;
 include protocol PChannelDiverter;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet;
 include protocol PDataChannel;
+include protocol PTransportProvider;
 
 include protocol PRtspController;
 include protocol PRtspChannel;
 include URIParams;
 include InputStreamParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 
@@ -51,16 +52,17 @@ prio(normal upto urgent) sync protocol P
   manages PTCPServerSocket;
   manages PUDPSocket;
   manages PDNSRequest;
   manages PRemoteOpenFile;
   manages PDataChannel;
   manages PRtspController;
   manages PRtspChannel;
   manages PChannelDiverter;
+  manages PTransportProvider;
 
 parent:
   async __delete__();
 
   prio(urgent) async PCookieService();
   async PHttpChannel(PBrowserOrId browser,
                      SerializedLoadContext loadContext,
                      HttpChannelCreationArgs args);
@@ -127,16 +129,18 @@ child:
 
   /* Predictor Methods */
   async PredOnPredictPrefetch(URIParams uri, uint32_t httpStatus);
   async PredOnPredictPreconnect(URIParams uri);
   async PredOnPredictDNS(URIParams uri);
 
   async SpeculativeConnectRequest(nsCString notificationData);
 
+  async PTransportProvider();
+
 both:
   // Actually we need PTCPSocket() for parent. But ipdl disallows us having different
   // signatures on parent and child. So when constructing the parent side object, we just 
   // leave host/port unused.
   async PTCPSocket(nsString host, uint16_t port);
 };
 
 
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/IPCTransportProvider.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/IPCTransportProvider.h"
+
+#include "nsISocketTransport.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(TransportProviderParent,
+                  nsITransportProvider,
+                  nsIHttpUpgradeListener)
+
+TransportProviderParent::TransportProviderParent()
+{
+  MOZ_COUNT_CTOR(TransportProviderParent);
+}
+
+TransportProviderParent::~TransportProviderParent()
+{
+  MOZ_COUNT_DTOR(TransportProviderParent);
+}
+
+NS_IMETHODIMP
+TransportProviderParent::SetListener(nsIHttpUpgradeListener* aListener)
+{
+  MOZ_ASSERT(aListener);
+  mListener = aListener;
+
+  MaybeNotify();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(mozilla::net::PTransportProviderChild*)
+TransportProviderParent::GetIPCChild()
+{
+  MOZ_CRASH("Don't call this in parent process");
+  return nullptr;
+}
+
+NS_IMETHODIMP
+TransportProviderParent::OnTransportAvailable(nsISocketTransport* aTransport,
+                                              nsIAsyncInputStream* aSocketIn,
+                                              nsIAsyncOutputStream* aSocketOut)
+{
+  MOZ_ASSERT(aTransport && aSocketOut && aSocketOut);
+  mTransport = aTransport;
+  mSocketIn = aSocketIn;
+  mSocketOut = aSocketOut;
+
+  MaybeNotify();
+
+  return NS_OK;
+}
+
+void
+TransportProviderParent::MaybeNotify()
+{
+  if (!mListener || !mTransport) {
+    return;
+  }
+
+  mListener->OnTransportAvailable(mTransport, mSocketIn, mSocketOut);
+}
+
+
+NS_IMPL_ISUPPORTS(TransportProviderChild,
+                  nsITransportProvider)
+
+TransportProviderChild::TransportProviderChild()
+{
+  MOZ_COUNT_CTOR(TransportProviderChild);
+}
+
+TransportProviderChild::~TransportProviderChild()
+{
+  MOZ_COUNT_DTOR(TransportProviderChild);
+  Send__delete__(this);
+}
+
+NS_IMETHODIMP
+TransportProviderChild::SetListener(nsIHttpUpgradeListener* aListener)
+{
+  MOZ_CRASH("Don't call this in child process");
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(mozilla::net::PTransportProviderChild*)
+TransportProviderChild::GetIPCChild()
+{
+  return this;
+}
+
+} // net
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/IPCTransportProvider.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_IPCTransportProvider_h
+#define mozilla_net_IPCTransportProvider_h
+
+#include "nsISupportsImpl.h"
+#include "mozilla/net/PTransportProviderParent.h"
+#include "mozilla/net/PTransportProviderChild.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsITransportProvider.h"
+
+/*
+ * No, the ownership model for TransportProvider is that the child object is
+ * refcounted "normally". I.e. ipdl code doesn't hold a strong reference to
+ * TransportProviderChild.
+ *
+ * When TransportProviderChild goes away, it sends a __delete__ message to the
+ * parent.
+ *
+ * On the parent side, ipdl holds a strong reference to TransportProviderParent.
+ * When the actor is deallocatde it releases the reference to the
+ * TransportProviderParent.
+ *
+ * So effectively the child holds a strong reference to the parent, and are
+ * otherwise normally refcounted and have their lifetime determined by that
+ * refcount.
+ *
+ * The only other caveat is that the creation happens from the parent.
+ * So to create a TransportProvider, a constructor is sent from the parent to
+ * the child. At this time the child gets its first addref.
+ *
+ * A reference to the TransportProvider is then sent as part of some other
+ * message from the parent to the child. For example in the
+ * PFlyWebPublishedServer.WebSocketRequest message.
+ *
+ * The receiver of that message can then grab the TransportProviderChild and
+ * without addreffing it, effectively using the refcount that the
+ * TransportProviderChild got on creation.
+ */
+
+class nsISocketTransport;
+class nsIAsyncInputStream;
+class nsIAsyncOutputStream;
+
+namespace mozilla {
+namespace net {
+
+class TransportProviderParent final : public PTransportProviderParent
+                                    , public nsITransportProvider
+                                    , public nsIHttpUpgradeListener
+{
+public:
+  TransportProviderParent();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITRANSPORTPROVIDER
+  NS_DECL_NSIHTTPUPGRADELISTENER
+
+  void ActorDestroy(ActorDestroyReason aWhy) override {};
+
+private:
+  ~TransportProviderParent();
+
+  void MaybeNotify();
+
+  nsCOMPtr<nsIHttpUpgradeListener> mListener;
+  nsCOMPtr<nsISocketTransport> mTransport;
+  nsCOMPtr<nsIAsyncInputStream> mSocketIn;
+  nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
+};
+
+class TransportProviderChild final : public PTransportProviderChild
+                                   , public nsITransportProvider
+{
+public:
+  TransportProviderChild();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITRANSPORTPROVIDER
+
+private:
+  ~TransportProviderChild();
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/PTransportProvider.ipdl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+/*
+ * The only thing this protocol manages is used for is passing a
+ * PTransportProvider object from parent to child and then back to the parent
+ * again. Hence there's no need for any messages on the protocol itself.
+ */
+
+async protocol PTransportProvider
+{
+  manager PNecko;
+
+parent:
+  async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -2,44 +2,54 @@
 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include protocol PBrowser;
+include protocol PTransportProvider;
 include InputStreamParams;
 include URIParams;
 include NeckoChannelParams;
 
 include protocol PBlob; //FIXME: bug #792908
 
 using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
+union OptionalTransportProvider
+{
+  PTransportProvider;
+  void_t;
+};
+
 async protocol PWebSocket
 {
   manager PNecko;
 
 parent:
   // Forwarded methods corresponding to methods on nsIWebSocketChannel
-  async AsyncOpen(URIParams aURI,
+  async AsyncOpen(OptionalURIParams aURI,
                   nsCString aOrigin,
                   uint64_t aInnerWindowID,
                   nsCString aProtocol,
                   bool aSecure,
                   // ping values only meaningful if client set them
                   uint32_t aPingInterval,
                   bool aClientSetPingInterval,
                   uint32_t aPingTimeout,
                   bool aClientSetPingTimeout,
-                  OptionalLoadInfoArgs aLoadInfoArgs);
+                  OptionalLoadInfoArgs aLoadInfoArgs,
+                  OptionalTransportProvider aProvider,
+                  nsCString aNegotiatedExtensions);
   async Close(uint16_t code, nsCString reason);
   async SendMsg(nsCString aMsg);
   async SendBinaryMsg(nsCString aMsg);
   async SendBinaryStream(InputStreamParams aStream, uint32_t aLength);
 
   async DeleteSelf();
 
 child:
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -431,50 +431,69 @@ WebSocketChannelChild::AsyncOpen(nsIURI 
                                  const nsACString &aOrigin,
                                  uint64_t aInnerWindowID,
                                  nsIWebSocketListener *aListener,
                                  nsISupports *aContext)
 {
   LOG(("WebSocketChannelChild::AsyncOpen() %p\n", this));
 
   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
-  MOZ_ASSERT(aURI && aListener && !mListenerMT,
+  MOZ_ASSERT((aURI && !mIsServerSide) || (!aURI && mIsServerSide),
+             "Invalid aURI for WebSocketChannelChild::AsyncOpen");
+  MOZ_ASSERT(aListener && !mListenerMT,
              "Invalid state for WebSocketChannelChild::AsyncOpen");
 
   mozilla::dom::TabChild* tabChild = nullptr;
   nsCOMPtr<nsITabChild> iTabChild;
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsITabChild),
                                 getter_AddRefs(iTabChild));
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
   if (MissingRequiredTabChild(tabChild, "websocket")) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  URIParams uri;
-  SerializeURI(aURI, uri);
-
   // Corresponding release in DeallocPWebSocket
   AddIPDLReference();
 
+  OptionalURIParams uri;
   OptionalLoadInfoArgs loadInfoArgs;
-  nsresult rv = LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs);
-  NS_ENSURE_SUCCESS(rv, rv);
+  OptionalTransportProvider transportProvider;
+
+  if (!mIsServerSide) {
+    uri = URIParams();
+    SerializeURI(aURI, uri.get_URIParams());
+    nsresult rv = LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    transportProvider = void_t();
+  } else {
+    uri = void_t();
+    loadInfoArgs = void_t();
+
+    MOZ_ASSERT(mServerTransportProvider);
+    transportProvider = mServerTransportProvider->GetIPCChild();
+  }
 
   gNeckoChild->SendPWebSocketConstructor(this, tabChild,
                                          IPC::SerializedLoadContext(this),
                                          mSerial);
   if (!SendAsyncOpen(uri, nsCString(aOrigin), aInnerWindowID, mProtocol,
                      mEncrypted, mPingInterval, mClientSetPingInterval,
-                     mPingResponseTimeout, mClientSetPingTimeout, loadInfoArgs)) {
+                     mPingResponseTimeout, mClientSetPingTimeout, loadInfoArgs,
+                     transportProvider, mNegotiatedExtensions)) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  if (mIsServerSide) {
+    mServerTransportProvider = nullptr;
+  }
+
   mOriginalURI = aURI;
   mURI = mOriginalURI;
   mListenerMT = new ListenerAndContextContainer(aListener, aContext);
   mOrigin = aOrigin;
   mWasOpened = 1;
 
   return NS_OK;
 }
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -53,26 +53,28 @@ WebSocketChannelParent::RecvDeleteSelf()
 {
   LOG(("WebSocketChannelParent::RecvDeleteSelf() %p\n", this));
   mChannel = nullptr;
   mAuthProvider = nullptr;
   return mIPCOpen ? Send__delete__(this) : true;
 }
 
 bool
-WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
+WebSocketChannelParent::RecvAsyncOpen(const OptionalURIParams& aURI,
                                       const nsCString& aOrigin,
                                       const uint64_t& aInnerWindowID,
                                       const nsCString& aProtocol,
                                       const bool& aSecure,
                                       const uint32_t& aPingInterval,
                                       const bool& aClientSetPingInterval,
                                       const uint32_t& aPingTimeout,
                                       const bool& aClientSetPingTimeout,
-                                      const OptionalLoadInfoArgs& aLoadInfoArgs)
+                                      const OptionalLoadInfoArgs& aLoadInfoArgs,
+                                      const OptionalTransportProvider& aTransportProvider,
+                                      const nsCString& aNegotiatedExtensions)
 {
   LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this));
 
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsILoadInfo> loadInfo;
 
   bool appOffline = false;
@@ -112,20 +114,30 @@ WebSocketChannelParent::RecvAsyncOpen(co
   rv = mChannel->SetNotificationCallbacks(this);
   if (NS_FAILED(rv))
     goto fail;
 
   rv = mChannel->SetProtocol(aProtocol);
   if (NS_FAILED(rv))
     goto fail;
 
-  uri = DeserializeURI(aURI);
-  if (!uri) {
-    rv = NS_ERROR_FAILURE;
-    goto fail;
+  if (aTransportProvider.type() != OptionalTransportProvider::Tvoid_t) {
+    RefPtr<TransportProviderParent> provider =
+      static_cast<TransportProviderParent*>(
+        aTransportProvider.get_PTransportProviderParent());
+    rv = mChannel->SetServerParameters(provider, aNegotiatedExtensions);
+    if (NS_FAILED(rv)) {
+      goto fail;
+    }
+  } else {
+    uri = DeserializeURI(aURI);
+    if (!uri) {
+      rv = NS_ERROR_FAILURE;
+      goto fail;
+    }
   }
 
   // only use ping values from child if they were overridden by client code.
   if (aClientSetPingInterval) {
     // IDL allows setting in seconds, so must be multiple of 1000 ms
     MOZ_ASSERT(aPingInterval >= 1000 && !(aPingInterval % 1000));
     mChannel->SetPingInterval(aPingInterval / 1000);
   }
--- a/netwerk/protocol/websocket/WebSocketChannelParent.h
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.h
@@ -34,26 +34,28 @@ class WebSocketChannelParent : public PW
   NS_DECL_NSIINTERFACEREQUESTOR
 
   WebSocketChannelParent(nsIAuthPromptProvider* aAuthProvider,
                          nsILoadContext* aLoadContext,
                          PBOverrideStatus aOverrideStatus,
                          uint32_t aSerial);
 
  private:
-  bool RecvAsyncOpen(const URIParams& aURI,
+  bool RecvAsyncOpen(const OptionalURIParams& aURI,
                      const nsCString& aOrigin,
                      const uint64_t& aInnerWindowID,
                      const nsCString& aProtocol,
                      const bool& aSecure,
                      const uint32_t& aPingInterval,
                      const bool& aClientSetPingInterval,
                      const uint32_t& aPingTimeout,
                      const bool& aClientSetPingTimeout,
-                     const OptionalLoadInfoArgs& aLoadInfoArgs) override;
+                     const OptionalLoadInfoArgs& aLoadInfoArgs,
+                     const OptionalTransportProvider& aTransportProvider,
+                     const nsCString& aNegotiatedExtensions) override;
   bool RecvClose(const uint16_t & code, const nsCString & reason) override;
   bool RecvSendMsg(const nsCString& aMsg) override;
   bool RecvSendBinaryMsg(const nsCString& aMsg) override;
   bool RecvSendBinaryStream(const InputStreamParams& aStream,
                             const uint32_t& aLength) override;
   bool RecvDeleteSelf() override;
 
   void ActorDestroy(ActorDestroyReason why) override;
--- a/netwerk/protocol/websocket/moz.build
+++ b/netwerk/protocol/websocket/moz.build
@@ -10,37 +10,40 @@ XPIDL_SOURCES += [
     'nsIWebSocketEventService.idl',
     'nsIWebSocketListener.idl',
 ]
 
 XPIDL_MODULE = 'necko_websocket'
 
 EXPORTS.mozilla.net += [
     'BaseWebSocketChannel.h',
+    'IPCTransportProvider.h',
     'WebSocketChannel.h',
     'WebSocketChannelChild.h',
     'WebSocketChannelParent.h',
     'WebSocketEventListenerChild.h',
     'WebSocketEventListenerParent.h',
     'WebSocketEventService.h',
     'WebSocketFrame.h',
 ]
 
 UNIFIED_SOURCES += [
     'BaseWebSocketChannel.cpp',
+    'IPCTransportProvider.cpp',
     'WebSocketChannel.cpp',
     'WebSocketChannelChild.cpp',
     'WebSocketChannelParent.cpp',
     'WebSocketEventListenerChild.cpp',
     'WebSocketEventListenerParent.cpp',
     'WebSocketEventService.cpp',
     'WebSocketFrame.cpp',
 ]
 
 IPDL_SOURCES += [
+    'PTransportProvider.ipdl',
     'PWebSocket.ipdl',
     'PWebSocketEventListener.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/netwerk/protocol/websocket/nsITransportProvider.idl
+++ b/netwerk/protocol/websocket/nsITransportProvider.idl
@@ -3,17 +3,34 @@
 /* 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/. */
 
 interface nsIHttpUpgradeListener;
 
 #include "nsISupports.idl"
 
+%{C++
+namespace mozilla {
+namespace net {
+class PTransportProviderChild;
+}
+}
+%}
+
+[ptr] native PTransportProviderChild(mozilla::net::PTransportProviderChild);
+
 /**
  * An interface which can be used to asynchronously request a nsITransport
  * together with the input and output streams that go together with it.
  */
-[scriptable, uuid(6fcec704-cfd2-46ef-a394-a64d5cb1475c)]
+[uuid(6fcec704-cfd2-46ef-a394-a64d5cb1475c)]
 interface nsITransportProvider : nsISupports
 {
+    // This must not be called in a child process since transport
+    // objects are not accessible there. Call getIPCChild instead.
     void setListener(in nsIHttpUpgradeListener listener);
+
+    // This must be implemented by nsITransportProvider objects running
+    // in the child process. It must return null when called in the parent
+    // process.
+    [noscript, notxpcom] PTransportProviderChild getIPCChild();
 };