Bug 1299040 - Implement transport builder constructor. r=smaug
☠☠ backed out by 38a6519fd70b ☠ ☠
authorKershaw Chang <kechang@mozilla.com>
Thu, 15 Sep 2016 07:09:00 -0400
changeset 314192 e684bf18e5d9ff115fe123d47de966a87291d067
parent 314191 1ee1b3c2df133672d920267794eb6ca3f2e07894
child 314193 464a1a1307774742e2f6bc9f5837586c1d80f87b
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs1299040
milestone51.0a1
Bug 1299040 - Implement transport builder constructor. r=smaug
dom/presentation/PresentationCallbacks.cpp
dom/presentation/PresentationDataChannelSessionTransport.js
dom/presentation/PresentationRequest.cpp
dom/presentation/PresentationService.cpp
dom/presentation/PresentationService.h
dom/presentation/PresentationSessionInfo.cpp
dom/presentation/PresentationSessionInfo.h
dom/presentation/PresentationTransportBuilderConstructor.cpp
dom/presentation/PresentationTransportBuilderConstructor.h
dom/presentation/interfaces/nsIPresentationService.idl
dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
dom/presentation/ipc/PresentationIPCService.cpp
dom/presentation/ipc/PresentationParent.cpp
dom/presentation/moz.build
dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -5,20 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 #include "nsIDocShell.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPresentationService.h"
 #include "nsIWebProgress.h"
 #include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
 #include "PresentationCallbacks.h"
 #include "PresentationRequest.h"
 #include "PresentationConnection.h"
-#include "nsThreadUtils.h"
+#include "PresentationTransportBuilderConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /*
  * Implementation of PresentationRequesterCallback
  */
 
@@ -196,17 +197,21 @@ PresentationResponderLoadingCallback::No
   uint64_t windowId = window->GetCurrentInnerWindow()->WindowID();
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return service->NotifyReceiverReady(mSessionId, windowId, aIsLoading);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    PresentationTransportBuilderConstructor::Create();
+  return service->NotifyReceiverReady(mSessionId,
+                                      windowId,aIsLoading,
+                                      constructor);
 }
 
 // nsIWebProgressListener
 NS_IMETHODIMP
 PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress,
                                                     nsIRequest* aRequest,
                                                     uint32_t aStateFlags,
                                                     nsresult aStatus)
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -43,17 +43,18 @@ PresentationDataChannelDescription.proto
 function PresentationTransportBuilder() {
   log("PresentationTransportBuilder construct");
   this._isControlChannelNeeded = true;
 }
 
 PresentationTransportBuilder.prototype = {
   classID: PRESENTATIONTRANSPORTBUILDER_CID,
   contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDataChannelSessionTransportBuilder,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder,
+                                         Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsITimerCallback]),
 
   buildDataChannelTransport: function(aRole, aWindow, aListener) {
     if (!aRole || !aWindow || !aListener) {
       log("buildDataChannelTransport with illegal parameters");
       throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
 
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -21,16 +21,17 @@
 #include "nsIURI.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 #include "nsSandboxFlags.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationAvailability.h"
 #include "PresentationCallbacks.h"
 #include "PresentationLog.h"
+#include "PresentationTransportBuilderConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationRequest)
@@ -202,17 +203,26 @@ PresentationRequest::StartWithDevice(con
   // Get xul:browser element in parent process or nsWindowRoot object in child
   // process. If it's in child process, the corresponding xul:browser element
   // will be obtained at PresentationRequestParent::DoRequest in its parent
   // process.
   nsCOMPtr<nsIDOMEventTarget> handler =
     do_QueryInterface(GetOwner()->GetChromeEventHandler());
   nsCOMPtr<nsIPresentationServiceCallback> callback =
     new PresentationRequesterCallback(this, id, promise);
-  rv = service->StartSession(mUrls, id, origin, aDeviceId, GetOwner()->WindowID(), handler, callback);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    PresentationTransportBuilderConstructor::Create();
+  rv = service->StartSession(mUrls,
+                             id,
+                             origin,
+                             aDeviceId,
+                             GetOwner()->WindowID(),
+                             handler,
+                             callback,
+                             constructor);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -102,61 +102,67 @@ ConvertURLArrayHelper(const nsTArray<nsS
  */
 
 class PresentationDeviceRequest final : public nsIPresentationDeviceRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONDEVICEREQUEST
 
-  PresentationDeviceRequest(const nsTArray<nsString>& aUrls,
-                            const nsAString& aId,
-                            const nsAString& aOrigin,
-                            uint64_t aWindowId,
-                            nsIDOMEventTarget* aEventTarget,
-                            nsIPresentationServiceCallback* aCallback);
+  PresentationDeviceRequest(
+              const nsTArray<nsString>& aUrls,
+              const nsAString& aId,
+              const nsAString& aOrigin,
+              uint64_t aWindowId,
+              nsIDOMEventTarget* aEventTarget,
+              nsIPresentationServiceCallback* aCallback,
+              nsIPresentationTransportBuilderConstructor* aBuilderConstructor);
 
 private:
   virtual ~PresentationDeviceRequest() = default;
   nsresult CreateSessionInfo(nsIPresentationDevice* aDevice,
                              const nsAString& aSelectedRequestUrl);
 
   nsTArray<nsString> mRequestUrls;
   nsString mId;
   nsString mOrigin;
   uint64_t mWindowId;
   nsWeakPtr mChromeEventHandler;
   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
 };
 
 LazyLogModule gPresentationLog("Presentation");
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest)
 
 PresentationDeviceRequest::PresentationDeviceRequest(
-                                      const nsTArray<nsString>& aUrls,
-                                      const nsAString& aId,
-                                      const nsAString& aOrigin,
-                                      uint64_t aWindowId,
-                                      nsIDOMEventTarget* aEventTarget,
-                                      nsIPresentationServiceCallback* aCallback)
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aId,
+               const nsAString& aOrigin,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
   : mRequestUrls(aUrls)
   , mId(aId)
   , mOrigin(aOrigin)
   , mWindowId(aWindowId)
   , mChromeEventHandler(do_GetWeakReference(aEventTarget))
   , mCallback(aCallback)
+  , mBuilderConstructor(aBuilderConstructor)
 {
   MOZ_ASSERT(!mRequestUrls.IsEmpty());
   MOZ_ASSERT(!mId.IsEmpty());
   MOZ_ASSERT(!mOrigin.IsEmpty());
   MOZ_ASSERT(mCallback);
+  MOZ_ASSERT(mBuilderConstructor);
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
@@ -236,16 +242,17 @@ PresentationDeviceRequest::CreateSession
   }
 
   // Initialize the session info with the control channel.
   rv = info->Init(ctrlChannel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
+  info->SetTransportBuilderConstructor(mBuilderConstructor);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::Cancel(nsresult aReason)
 {
   return mCallback->NotifyError(aReason);
 }
@@ -637,38 +644,41 @@ PresentationService::IsAppInstalled(nsIU
   if (NS_WARN_IF(!app)) {
     return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP
-PresentationService::StartSession(const nsTArray<nsString>& aUrls,
-                                  const nsAString& aSessionId,
-                                  const nsAString& aOrigin,
-                                  const nsAString& aDeviceId,
-                                  uint64_t aWindowId,
-                                  nsIDOMEventTarget* aEventTarget,
-                                  nsIPresentationServiceCallback* aCallback)
+PresentationService::StartSession(
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aSessionId,
+               const nsAString& aOrigin,
+               const nsAString& aDeviceId,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!aSessionId.IsEmpty());
   MOZ_ASSERT(!aUrls.IsEmpty());
 
   nsCOMPtr<nsIPresentationDeviceRequest> request =
     new PresentationDeviceRequest(aUrls,
                                   aSessionId,
                                   aOrigin,
                                   aWindowId,
                                   aEventTarget,
-                                  aCallback);
+                                  aCallback,
+                                  aBuilderConstructor);
 
   if (aDeviceId.IsVoid()) {
     // Pop up a prompt and ask user to select a device.
     nsCOMPtr<nsIPresentationDevicePrompt> prompt =
       do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
     if (NS_WARN_IF(!prompt)) {
       return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
@@ -940,38 +950,16 @@ PresentationService::UnregisterSessionLi
     // When content side decide not handling this session anymore, simply
     // close the connection. Session info is kept for reconnection.
     Unused << NS_WARN_IF(NS_FAILED(info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED)));
     return info->SetListener(nullptr);
   }
   return NS_OK;
 }
 
-nsresult
-PresentationService::RegisterTransportBuilder(const nsAString& aSessionId,
-                                              uint8_t aRole,
-                                              nsIPresentationSessionTransportBuilder* aBuilder)
-{
-  PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
-             NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
-
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aBuilder);
-  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
-             aRole == nsIPresentationService::ROLE_RECEIVER);
-
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
-  if (NS_WARN_IF(!info)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  info->SetBuilder(aBuilder);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 PresentationService::RegisterRespondingListener(
   uint64_t aWindowId,
   nsIPresentationRespondingListener* aListener)
 {
   PRES_DEBUG("%s:windowId[%lld]\n", __func__, aWindowId);
 
   MOZ_ASSERT(NS_IsMainThread());
@@ -1004,19 +992,21 @@ PresentationService::UnregisterRespondin
 
   MOZ_ASSERT(NS_IsMainThread());
 
   mRespondingListeners.Remove(aWindowId);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationService::NotifyReceiverReady(const nsAString& aSessionId,
-                                         uint64_t aWindowId,
-                                         bool aIsLoading)
+PresentationService::NotifyReceiverReady(
+               const nsAString& aSessionId,
+               uint64_t aWindowId,
+               bool aIsLoading,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   PRES_DEBUG("%s:id[%s], windowId[%lld], loading[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId, aIsLoading);
 
   RefPtr<PresentationSessionInfo> info =
     GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -1034,16 +1024,17 @@ PresentationService::NotifyReceiverReady
   nsCOMPtr<nsIPresentationRespondingListener> listener;
   if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
     nsresult rv = listener->NotifySessionConnect(aWindowId, aSessionId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
+  info->SetTransportBuilderConstructor(aBuilderConstructor);
   return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderReady();
 }
 
 nsresult
 PresentationService::NotifyTransportClosed(const nsAString& aSessionId,
                                            uint8_t aRole,
                                            nsresult aReason)
 {
--- a/dom/presentation/PresentationService.h
+++ b/dom/presentation/PresentationService.h
@@ -51,20 +51,16 @@ public:
              info.forget() : nullptr;
     }
   }
 
   bool IsSessionAccessible(const nsAString& aSessionId,
                            const uint8_t aRole,
                            base::ProcessId aProcessId);
 
-  nsresult RegisterTransportBuilder(const nsAString& aSessionId,
-                                    uint8_t aRole,
-                                    nsIPresentationSessionTransportBuilder* aBuilder);
-
 private:
   friend class PresentationDeviceRequest;
 
   virtual ~PresentationService();
   void HandleShutdown();
   nsresult HandleDeviceChange();
   nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
   nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest);
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -236,17 +236,17 @@ PresentationSessionInfo::Shutdown(nsresu
   if (mTransport) {
     // |mIsTransportReady| will be unset once |NotifyTransportClosed| is called.
     Unused << NS_WARN_IF(NS_FAILED(mTransport->Close(aReason)));
   }
 
   mIsResponderReady = false;
   mIsOnTerminating = false;
 
-  SetBuilder(nullptr);
+  ResetBuilder();
 }
 
 nsresult
 PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
 {
   if (mListener && aListener) {
     Unused << NS_WARN_IF(NS_FAILED(mListener->NotifyReplaced()));
   }
@@ -374,17 +374,22 @@ PresentationSessionInfo::GetWindow()
   }
   uint64_t windowId = 0;
   if (NS_WARN_IF(NS_FAILED(service->GetWindowIdBySessionId(mSessionId,
                                                            mRole,
                                                            &windowId)))) {
     return nullptr;
   }
 
-  return nsGlobalWindow::GetInnerWindowWithId(windowId)->AsInner();
+  auto window = nsGlobalWindow::GetInnerWindowWithId(windowId);
+  if (!window) {
+    return nullptr;
+  }
+
+  return window->AsInner();
 }
 
 /* virtual */ bool
 PresentationSessionInfo::IsAccessible(base::ProcessId aProcessId)
 {
   // No restriction by default.
   return true;
 }
@@ -487,17 +492,17 @@ PresentationSessionInfo::NotifyData(cons
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
 PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
 {
   PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
 
-  SetBuilder(nullptr);
+  ResetBuilder();
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
     return NS_ERROR_FAILURE;
   }
 
   // The session transport is managed by content process
   if (!transport) {
     return NS_OK;
@@ -518,17 +523,17 @@ PresentationSessionInfo::OnSessionTransp
 }
 
 NS_IMETHODIMP
 PresentationSessionInfo::OnError(nsresult aReason)
 {
   PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole);
 
-  SetBuilder(nullptr);
+  ResetBuilder();
   return ReplyError(aReason);
 }
 
 NS_IMETHODIMP
 PresentationSessionInfo::SendOffer(nsIPresentationChannelDescription* aOffer)
 {
   return mControlChannel->SendOffer(aOffer);
 }
@@ -822,62 +827,62 @@ nsresult
 PresentationControllingInfo::BuildTransport()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
     return NS_OK;
   }
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
     // Build TCP session transport
     return GetAddress();
   }
-
-  nsPIDOMWindowInner* window = nullptr;
   /**
    * Generally transport is maintained by the chrome process. However, data
    * channel should be live with the DOM , which implies RTCDataChannel in an OOP
    * page should be establish in the content process.
    *
-   * In OOP data channel transport case, |mBuilder| is hooked when the content
-   * process is ready to build a data channel transport, trigger by:
-   * 1. PresentationIPCService::StartSession (sender)
-   * 2. PresentationIPCService::NotifyReceiverReady (receiver).
+   * |mBuilderConstructor| is responsible for creating a builder, which is for
+   * building a data channel transport.
    *
-   * In this case, |mBuilder| would be an object of |PresentationBuilderParent|
-   * and set previously. Therefore, |BuildDataChannelTransport| triggers an IPC
-   * call to make content process establish a RTCDataChannel transport.
+   * In the OOP case, |mBuilderConstructor| would create a builder which is
+   * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport|
+   * triggers an IPC call to make content process establish a RTCDataChannel
+   * transport.
    */
-  // in-process case
-  if (!mBuilder) {
-    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> builder =
-      do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
-    if (NS_WARN_IF(!builder)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-    SetBuilder(builder);
-    // OOP window would be set from content process
-    window = GetWindow();
+
+  mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
+  if (NS_WARN_IF(NS_FAILED(
+    mBuilderConstructor->CreateTransportBuilder(mTransportType,
+                                                getter_AddRefs(mBuilder))))) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
-  // OOP case
-  mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
 
   nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
     dataChannelBuilder(do_QueryInterface(mBuilder));
   if (NS_WARN_IF(!dataChannelBuilder)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+
+  // OOP window would be set from content process
+  nsPIDOMWindowInner* window = GetWindow();
+
   nsresult rv = dataChannelBuilder->
          BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
                                    window,
                                    this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationControllingInfo::NotifyDisconnected(nsresult aReason)
 {
   PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole);
@@ -918,19 +923,28 @@ PresentationControllingInfo::OnSocketAcc
   int32_t port;
   nsresult rv = aTransport->GetPort(&port);
   if (!NS_WARN_IF(NS_FAILED(rv))) {
     PRES_DEBUG("%s:receive from port[%d]\n",__func__, port);
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
   // Initialize session transport builder and use |this| as the callback.
-  nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder =
-    do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder;
+  if (NS_SUCCEEDED(mBuilderConstructor->CreateTransportBuilder(
+                     nsIPresentationChannelDescription::TYPE_TCP,
+                     getter_AddRefs(mBuilder)))) {
+    builder = do_QueryInterface(mBuilder);
+  }
+
   if (NS_WARN_IF(!builder)) {
     return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   mTransportType = nsIPresentationChannelDescription::TYPE_TCP;
   return builder->BuildTCPSenderTransport(aTransport, this);
 }
 
@@ -1130,16 +1144,17 @@ PresentationPresentingInfo::Shutdown(nsr
   if (mDevice) {
     mDevice->Disconnect();
   }
   mDevice = nullptr;
   mLoadingCallback = nullptr;
   mRequesterDescription = nullptr;
   mPendingCandidates.Clear();
   mPromise = nullptr;
+  mHasFlushPendingEvents = false;
 }
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
 PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
 {
   nsresult rv = PresentationSessionInfo::OnSessionTransport(transport);
 
@@ -1207,84 +1222,80 @@ PresentationPresentingInfo::InitTranspor
   MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CONNECTING);
 
   uint8_t type = 0;
   nsresult rv = mRequesterDescription->GetType(&type);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(
+    mBuilderConstructor->CreateTransportBuilder(type,
+                                                getter_AddRefs(mBuilder))))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   if (type == nsIPresentationChannelDescription::TYPE_TCP) {
     // Establish a data transport channel |mTransport| to the sender and use
     // |this| as the callback.
     nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder =
-      do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+      do_QueryInterface(mBuilder);
     if (NS_WARN_IF(!builder)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
-    SetBuilder(builder);
     mTransportType = nsIPresentationChannelDescription::TYPE_TCP;
     return builder->BuildTCPReceiverTransport(mRequesterDescription, this);
   }
 
   if (type == nsIPresentationChannelDescription::TYPE_DATACHANNEL) {
     if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
       return NS_ERROR_NOT_IMPLEMENTED;
     }
-    nsPIDOMWindowInner* window = nullptr;
-
     /**
      * Generally transport is maintained by the chrome process. However, data
      * channel should be live with the DOM , which implies RTCDataChannel in an OOP
      * page should be establish in the content process.
      *
-     * In OOP data channel transport case, |mBuilder| is hooked when the content
-     * process is ready to build a data channel transport, trigger by:
-     * 1. PresentationIPCService::StartSession (sender)
-     * 2. PresentationIPCService::NotifyReceiverReady (receiver).
+     * |mBuilderConstructor| is responsible for creating a builder, which is for
+     * building a data channel transport.
      *
-     * In this case, |mBuilder| would be an object of |PresentationBuilderParent|
-     * and set previously. Therefore, |BuildDataChannelTransport| triggers an IPC
-     * call to make content process establish a RTCDataChannel transport.
+     * In the OOP case, |mBuilderConstructor| would create a builder which is
+     * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport|
+     * triggers an IPC call to make content process establish a RTCDataChannel
+     * transport.
      */
-    // in-process case
-    if (!mBuilder) {
-      nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> builder =
-        do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
 
-      if (NS_WARN_IF(!builder)) {
-        return NS_ERROR_NOT_AVAILABLE;
-      }
-
-      SetBuilder(builder);
-
-      // OOP window would be set from content process
-      window = GetWindow();
-    }
     mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
 
-    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
-      dataChannelBuilder(do_QueryInterface(mBuilder));
+    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> dataChannelBuilder =
+      do_QueryInterface(mBuilder);
     if (NS_WARN_IF(!dataChannelBuilder)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
+
+    nsPIDOMWindowInner* window = GetWindow();
+
     rv = dataChannelBuilder->
            BuildDataChannelTransport(nsIPresentationService::ROLE_RECEIVER,
                                      window,
                                      this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-
-    rv = this->FlushPendingEvents(dataChannelBuilder);
+    rv = FlushPendingEvents(dataChannelBuilder);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
+
     return NS_OK;
   }
 
   MOZ_ASSERT(false, "Unknown nsIPresentationChannelDescription type!");
   return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -74,21 +74,16 @@ public:
 
   nsresult SetListener(nsIPresentationSessionListener* aListener);
 
   void SetDevice(nsIPresentationDevice* aDevice)
   {
     mDevice = aDevice;
   }
 
-  void SetBuilder(nsIPresentationSessionTransportBuilder* aBuilder)
-  {
-    mBuilder = aBuilder;
-  }
-
   already_AddRefed<nsIPresentationDevice> GetDevice() const
   {
     nsCOMPtr<nsIPresentationDevice> device = mDevice;
     return device.forget();
   }
 
   void SetControlChannel(nsIPresentationControlChannel* aControlChannel)
   {
@@ -108,16 +103,22 @@ public:
                  uint32_t aState);
 
   nsresult OnTerminate(nsIPresentationControlChannel* aControlChannel);
 
   nsresult ReplyError(nsresult aReason);
 
   virtual bool IsAccessible(base::ProcessId aProcessId);
 
+  void SetTransportBuilderConstructor(
+    nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
+  {
+    mBuilderConstructor = aBuilderConstructor;
+  }
+
 protected:
   virtual ~PresentationSessionInfo()
   {
     Shutdown(NS_OK);
   }
 
   virtual void Shutdown(nsresult aReason);
 
@@ -144,16 +145,21 @@ protected:
       DebugOnly<nsresult> rv =
         mListener->NotifyStateChange(mSessionId, mState, aReason);
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyStateChanged");
     }
   }
 
   void ContinueTermination();
 
+  void ResetBuilder()
+  {
+    mBuilder = nullptr;
+  }
+
   // Should be nsIPresentationChannelDescription::TYPE_TCP/TYPE_DATACHANNEL
   uint8_t mTransportType = 0;
 
   nsPIDOMWindowInner* GetWindow();
 
   nsString mUrl;
   nsString mSessionId;
   // mRole should be nsIPresentationService::ROLE_CONTROLLER
@@ -164,16 +170,17 @@ protected:
   bool mIsOnTerminating = false;
   uint32_t mState; // CONNECTED, CLOSED, TERMINATED
   nsresult mReason;
   nsCOMPtr<nsIPresentationSessionListener> mListener;
   nsCOMPtr<nsIPresentationDevice> mDevice;
   nsCOMPtr<nsIPresentationSessionTransport> mTransport;
   nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
   nsCOMPtr<nsIPresentationSessionTransportBuilder> mBuilder;
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
 };
 
 // Session info with controlling browsing context (sender side) behaviors.
 class PresentationControllingInfo final : public PresentationSessionInfo
                                         , public nsIServerSocketListener
                                         , public nsIListNetworkAddressesListener
 {
 public:
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationTransportBuilderConstructor.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PresentationTransportBuilderConstructor.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsIPresentationControlChannel.h"
+#include "nsIPresentationSessionTransport.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(DummyPresentationTransportBuilderConstructor,
+                  nsIPresentationTransportBuilderConstructor)
+
+NS_IMETHODIMP
+DummyPresentationTransportBuilderConstructor::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  MOZ_ASSERT(false, "Unexpected to be invoked.");
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(PresentationTransportBuilderConstructor,
+                             DummyPresentationTransportBuilderConstructor)
+
+/* static */ already_AddRefed<nsIPresentationTransportBuilderConstructor>
+PresentationTransportBuilderConstructor::Create()
+{
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor;
+  if (XRE_IsContentProcess()) {
+    constructor = new DummyPresentationTransportBuilderConstructor();
+  } else {
+    constructor = new PresentationTransportBuilderConstructor();
+  }
+
+  return constructor.forget();
+}
+
+NS_IMETHODIMP
+PresentationTransportBuilderConstructor::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  if (NS_WARN_IF(!aRetval)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aRetval = nullptr;
+
+  if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
+                 aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (XRE_IsContentProcess()) {
+    MOZ_ASSERT(false,
+               "CreateTransportBuilder can only be invoked in parent process.");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
+  if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
+    builder =
+      do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  } else {
+    builder =
+      do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
+  }
+
+  if (NS_WARN_IF(!builder)) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  builder.forget(aRetval);
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationTransportBuilderConstructor.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PresentationTransportBuilderConstructor_h
+#define mozilla_dom_PresentationTransportBuilderConstructor_h
+
+#include "nsCOMPtr.h"
+#include "nsIPresentationSessionTransportBuilder.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class DummyPresentationTransportBuilderConstructor :
+  public nsIPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  DummyPresentationTransportBuilderConstructor() = default;
+
+protected:
+  virtual ~DummyPresentationTransportBuilderConstructor() = default;
+};
+
+class PresentationTransportBuilderConstructor final :
+  public DummyPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  static already_AddRefed<nsIPresentationTransportBuilderConstructor>
+  Create();
+
+private:
+  PresentationTransportBuilderConstructor() = default;
+  virtual ~PresentationTransportBuilderConstructor() = default;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationTransportBuilderConstructor_h
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -4,16 +4,17 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMEventTarget;
 interface nsIInputStream;
 interface nsIPresentationAvailabilityListener;
 interface nsIPresentationRespondingListener;
 interface nsIPresentationSessionListener;
+interface nsIPresentationTransportBuilderConstructor;
 
 %{C++
 #define PRESENTATION_SERVICE_CID \
   { 0x1d9bb10c, 0xc0ab, 0x4fe8, \
     { 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } }
 #define PRESENTATION_SERVICE_CONTRACTID \
   "@mozilla.org/presentation/presentationservice;1"
 
@@ -67,24 +68,26 @@ interface nsIPresentationService : nsISu
    *                  located in different process from this service)
    * @param eventTarget: The chrome event handler, in particular XUL browser
    *                     element in parent process, that the request was
    *                     originated in.
    * @param callback: Invoke the callback when the operation is completed.
    *                  NotifySuccess() is called with |id| if a session is
    *                  established successfully with the selected device.
    *                  Otherwise, NotifyError() is called with a error message.
+   * @param constructor: The constructor for creating a transport builder.
    */
   [noscript] void startSession(in URLArrayRef urls,
                                in DOMString sessionId,
                                in DOMString origin,
                                in DOMString deviceId,
                                in unsigned long long windowId,
                                in nsIDOMEventTarget eventTarget,
-                               in nsIPresentationServiceCallback callback);
+                               in nsIPresentationServiceCallback callback,
+                               in nsIPresentationTransportBuilderConstructor constructor);
 
   /*
    * Send the message to the session.
    *
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param data: the message being sent out.
    */
@@ -176,20 +179,22 @@ interface nsIPresentationService : nsISu
 
   /*
    * Notify the receiver page is ready for presentation use.
    *
    * @param sessionId An ID to identify presentation session.
    * @param windowId  The inner window ID associated with the presentation
    *                  session.
    * @param isLoading true if receiver page is loading successfully.
+   * @param constructor: The constructor for creating a transport builder.
    */
   void notifyReceiverReady(in DOMString sessionId,
                            in unsigned long long windowId,
-                           in boolean isLoading);
+                           in boolean isLoading,
+                           in nsIPresentationTransportBuilderConstructor constructor);
 
   /*
    * Notify the transport is closed
    *
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param reason: the error message. NS_OK indicates it is closed normally.
    */
--- a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
+++ b/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
@@ -24,16 +24,25 @@ interface nsIPresentationSessionTranspor
 };
 
 [scriptable, uuid(2fdbe67d-80f9-48dc-8237-5bef8fa19801)]
 interface nsIPresentationSessionTransportBuilder : nsISupports
 {
 };
 
 /**
+ * The constructor for creating a transport builder.
+ */
+[scriptable, uuid(706482b2-1b51-4bed-a21d-785a9cfcfac7)]
+interface nsIPresentationTransportBuilderConstructor : nsISupports
+{
+  nsIPresentationSessionTransportBuilder createTransportBuilder(in uint8_t type);
+};
+
+/**
  * Builder for TCP session transport
  */
 [scriptable, uuid(cde36d6e-f471-4262-a70d-f932a26b21d9)]
 interface nsIPresentationTCPSessionTransportBuilder : nsIPresentationSessionTransportBuilder
 {
   /**
    * The following creation functions will trigger |listener.onSessionTransport|
    * if the session transport is successfully built, |listener.onError| if some
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -49,23 +49,25 @@ PresentationIPCService::~PresentationIPC
 
   mAvailabilityListeners.Clear();
   mSessionListeners.Clear();
   mSessionInfos.Clear();
   sPresentationChild = nullptr;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::StartSession(const nsTArray<nsString>& aUrls,
-                                     const nsAString& aSessionId,
-                                     const nsAString& aOrigin,
-                                     const nsAString& aDeviceId,
-                                     uint64_t aWindowId,
-                                     nsIDOMEventTarget* aEventTarget,
-                                     nsIPresentationServiceCallback* aCallback)
+PresentationIPCService::StartSession(
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aSessionId,
+               const nsAString& aOrigin,
+               const nsAString& aDeviceId,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   if (aWindowId != 0) {
     AddRespondingSessionId(aWindowId,
                            aSessionId,
                            nsIPresentationService::ROLE_CONTROLLER);
   }
 
   nsPIDOMWindowInner* window =
@@ -373,19 +375,21 @@ PresentationIPCService::NotifyAvailableC
     nsIPresentationAvailabilityListener* listener = iter.GetNext();
     Unused << NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aAvailable)));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId,
-                                            uint64_t aWindowId,
-                                            bool aIsLoading)
+PresentationIPCService::NotifyReceiverReady(
+               const nsAString& aSessionId,
+               uint64_t aWindowId,
+               bool aIsLoading,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // No actual window uses 0 as its ID.
   if (NS_WARN_IF(aWindowId == 0)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -4,23 +4,88 @@
  * 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 "DCPresentationChannelDescription.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/Unused.h"
 #include "nsIPresentationDeviceManager.h"
+#include "nsIPresentationSessionTransport.h"
+#include "nsIPresentationSessionTransportBuilder.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationBuilderParent.h"
 #include "PresentationParent.h"
 #include "PresentationService.h"
 #include "PresentationSessionInfo.h"
 
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+class PresentationTransportBuilderConstructorIPC final :
+  public nsIPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  PresentationTransportBuilderConstructorIPC(PresentationParent* aParent)
+    : mParent(aParent)
+  {
+  }
+
+private:
+  virtual ~PresentationTransportBuilderConstructorIPC() = default;
+
+  RefPtr<PresentationParent> mParent;
+};
+
+NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,
+                  nsIPresentationTransportBuilderConstructor)
+
+NS_IMETHODIMP
+PresentationTransportBuilderConstructorIPC::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  if (NS_WARN_IF(!aRetval)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aRetval = nullptr;
+
+  if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
+                 aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (XRE_IsContentProcess()) {
+    MOZ_ASSERT(false,
+               "CreateTransportBuilder can only be invoked in parent process.");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
+  if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
+    builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  } else {
+    builder = new PresentationBuilderParent(mParent);
+  }
+
+  if (NS_WARN_IF(!builder)) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  builder.forget(aRetval);
+  return NS_OK;
+}
+
+} // anonymous namespace
 
 /*
  * Implementation of PresentationParent
  */
 
 NS_IMPL_ISUPPORTS(PresentationParent,
                   nsIPresentationAvailabilityListener,
                   nsIPresentationSessionListener,
@@ -212,29 +277,16 @@ PresentationParent::RecvRegisterRespondi
 PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId)
 {
   MOZ_ASSERT(mService);
   mWindowIds.RemoveElement(aWindowId);
   Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
   return true;
 }
 
-bool
-PresentationParent::RegisterTransportBuilder(const nsString& aSessionId,
-                                             const uint8_t& aRole)
-{
-  MOZ_ASSERT(mService);
-
-  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder =
-    new PresentationBuilderParent(this);
-  Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationService*>(mService.get())->
-                         RegisterTransportBuilder(aSessionId, aRole, builder)));
-  return true;
-}
-
 NS_IMETHODIMP
 PresentationParent::NotifyAvailableChange(bool aAvailable)
 {
   if (NS_WARN_IF(mActorDestroyed || !SendNotifyAvailableChange(aAvailable))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
@@ -285,20 +337,22 @@ PresentationParent::NotifySessionConnect
 
 bool
 PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId,
                                             const uint64_t& aWindowId,
                                             const bool& aIsLoading)
 {
   MOZ_ASSERT(mService);
 
-  RegisterTransportBuilder(aSessionId, nsIPresentationService::ROLE_RECEIVER);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    new PresentationTransportBuilderConstructorIPC(this);
   Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId,
                                                                aWindowId,
-                                                               aIsLoading)));
+                                                               aIsLoading,
+                                                               constructor)));
   return true;
 }
 
 bool
 PresentationParent::RecvNotifyTransportClosed(const nsString& aSessionId,
                                               const uint8_t& aRole,
                                               const nsresult& aReason)
 {
@@ -333,30 +387,33 @@ PresentationRequestParent::ActorDestroy(
   mActorDestroyed = true;
   mService = nullptr;
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const StartSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
-  mNeedRegisterBuilder = true;
+
   mSessionId = aRequest.sessionId();
 
   nsCOMPtr<nsIDOMEventTarget> eventTarget;
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   RefPtr<TabParent> tp =
     cpm->GetTopLevelTabParentByProcessAndTabId(mChildId, aRequest.tabId());
   if (tp) {
     eventTarget = do_QueryInterface(tp->GetOwnerElement());
   }
-  
+
+  RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    new PresentationTransportBuilderConstructorIPC(parent);
   return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
                                 aRequest.origin(), aRequest.deviceId(),
-                                aRequest.windowId(), eventTarget, this);
+                                aRequest.windowId(), eventTarget, this, constructor);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
@@ -425,17 +482,16 @@ PresentationRequestParent::DoRequest(con
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
     IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
 
     // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
     // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
     return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
-  mNeedRegisterBuilder = true;
   mSessionId = aRequest.sessionId();
   return mService->ReconnectSession(aRequest.urls(),
                                     aRequest.sessionId(),
                                     aRequest.role(),
                                     this);
 }
 
 nsresult
@@ -455,23 +511,16 @@ PresentationRequestParent::DoRequest(con
     return SendResponse(rv);
   }
   return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifySuccess(const nsAString& aUrl)
 {
-  if (mNeedRegisterBuilder) {
-    RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
-    Unused << NS_WARN_IF(!parent->RegisterTransportBuilder(
-                                      mSessionId,
-                                      nsIPresentationService::ROLE_CONTROLLER));
-  }
-
   Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
   return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifyError(nsresult aError)
 {
   return SendResponse(aError);
@@ -481,8 +530,11 @@ nsresult
 PresentationRequestParent::SendResponse(nsresult aResult)
 {
   if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/presentation/moz.build
+++ b/dom/presentation/moz.build
@@ -50,16 +50,17 @@ UNIFIED_SOURCES += [
     'PresentationReceiver.cpp',
     'PresentationRequest.cpp',
     'PresentationService.cpp',
     'PresentationServiceBase.cpp',
     'PresentationSessionInfo.cpp',
     'PresentationSessionRequest.cpp',
     'PresentationTCPSessionTransport.cpp',
     'PresentationTerminateRequest.cpp',
+    'PresentationTransportBuilderConstructor.cpp'
 ]
 
 EXTRA_COMPONENTS += [
     'PresentationDataChannelSessionTransport.js',
     'PresentationDataChannelSessionTransport.manifest',
     'PresentationDeviceInfoManager.js',
     'PresentationDeviceInfoManager.manifest',
 ]
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -215,16 +215,17 @@ const mockedDevicePrompt = {
   },
   simulateCancel: function(result) {
     this._request.cancel(result);
   }
 };
 
 const mockedSessionTransport = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport,
+                                         Ci.nsIPresentationSessionTransportBuilder,
                                          Ci.nsIPresentationTCPSessionTransportBuilder,
                                          Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsIPresentationControlChannelListener,
                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     }