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 357695 e684bf18e5d9ff115fe123d47de966a87291d067
parent 357694 1ee1b3c2df133672d920267794eb6ca3f2e07894
child 357696 464a1a1307774742e2f6bc9f5837586c1d80f87b
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1299040
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;
     }