Bug 1301259 - Part2: Close the connected/connecting connection before starting reconnecting, r=smaug
authorKershaw Chang <kechang@mozilla.com>
Wed, 28 Sep 2016 23:35:00 +0200
changeset 315822 f8797c70734845bba0e918977e276f6b76e9769e
parent 315821 c572919bea3524d38e1a3364a4acba332910b84a
child 315823 f205f28cbcf3723aee0e302ea2ce7b71a00f4210
push id30757
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:02:43 +0000
treeherdermozilla-central@5ffed033557e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1301259
milestone52.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 1301259 - Part2: Close the connected/connecting connection before starting reconnecting, r=smaug
dom/presentation/PresentationCallbacks.cpp
dom/presentation/PresentationConnection.cpp
dom/presentation/PresentationSessionInfo.cpp
dom/presentation/PresentationSessionInfo.h
dom/presentation/interfaces/nsIPresentationListener.idl
dom/presentation/ipc/PPresentation.ipdl
dom/presentation/ipc/PresentationBuilderParent.cpp
dom/presentation/ipc/PresentationBuilderParent.h
dom/presentation/ipc/PresentationChild.cpp
dom/presentation/ipc/PresentationChild.h
dom/presentation/ipc/PresentationIPCService.cpp
dom/presentation/ipc/PresentationIPCService.h
dom/presentation/ipc/PresentationParent.cpp
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -105,16 +105,20 @@ PresentationReconnectCallback::NotifySuc
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsresult rv = NS_OK;
   // We found a matched connection with the same window ID, URL, and
   // the session ID. Resolve the promise with this connection and dispatch
   // the event.
   if (mConnection) {
+    mConnection->NotifyStateChange(
+      mSessionId,
+      nsIPresentationSessionListener::STATE_CONNECTING,
+      NS_OK);
     mPromise->MaybeResolve(mConnection);
     rv = mRequest->DispatchConnectionAvailableEvent(mConnection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     // Use |PresentationRequesterCallback::NotifySuccess| to create a new
     // connection since we don't find one that can be reused.
@@ -137,16 +141,22 @@ PresentationReconnectCallback::NotifySuc
              service->BuildTransport(sessionId,
                                      nsIPresentationService::ROLE_CONTROLLER);
            }));
 }
 
 NS_IMETHODIMP
 PresentationReconnectCallback::NotifyError(nsresult aError)
 {
+  if (mConnection) {
+    mConnection->NotifyStateChange(
+      mSessionId,
+      nsIPresentationSessionListener::STATE_CLOSED,
+      aError);
+  }
   return PresentationRequesterCallback::NotifyError(aError);
 }
 
 NS_IMPL_ISUPPORTS(PresentationResponderLoadingCallback,
                   nsIWebProgressListener,
                   nsISupportsWeakReference)
 
 PresentationResponderLoadingCallback::PresentationResponderLoadingCallback(const nsAString& aSessionId)
--- a/dom/presentation/PresentationConnection.cpp
+++ b/dom/presentation/PresentationConnection.cpp
@@ -357,16 +357,21 @@ PresentationConnection::NotifyStateChang
   PRES_DEBUG("connection state change:id[%s], state[%x], reason[%x], role[%d]\n",
              NS_ConvertUTF16toUTF8(aSessionId).get(), aState,
              aReason, mRole);
 
   if (!aSessionId.Equals(mId)) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  // A terminated connection should always remain in terminated.
+  if (mState == PresentationConnectionState::Terminated) {
+    return NS_OK;
+  }
+
   PresentationConnectionState state;
   switch (aState) {
     case nsIPresentationSessionListener::STATE_CONNECTING:
       state = PresentationConnectionState::Connecting;
       break;
     case nsIPresentationSessionListener::STATE_CONNECTED:
       state = PresentationConnectionState::Connected;
       break;
@@ -519,27 +524,16 @@ PresentationConnection::DoReceiveMessage
     if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
       return NS_ERROR_FAILURE;
     }
   }
 
   return DispatchMessageEvent(jsData);
 }
 
-NS_IMETHODIMP
-PresentationConnection::NotifyReplaced()
-{
-  PRES_DEBUG("connection %s:id[%s], role[%d]\n", __func__,
-             NS_ConvertUTF16toUTF8(mId).get(), mRole);
-
-  return NotifyStateChange(mId,
-                           nsIPresentationSessionListener::STATE_CLOSED,
-                           NS_OK);
-}
-
 nsresult
 PresentationConnection::DispatchConnectionClosedEvent(
   PresentationConnectionClosedReason aReason,
   const nsAString& aMessage,
   bool aDispatchNow)
 {
   if (mState != PresentationConnectionState::Closed) {
     MOZ_ASSERT(false, "The connection state should be closed.");
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -242,20 +242,16 @@ PresentationSessionInfo::Shutdown(nsresu
   mIsOnTerminating = false;
 
   ResetBuilder();
 }
 
 nsresult
 PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
 {
-  if (mListener && aListener) {
-    Unused << NS_WARN_IF(NS_FAILED(mListener->NotifyReplaced()));
-  }
-
   mListener = aListener;
 
   if (mListener) {
     // Enable data notification for the transport channel if it's available.
     if (mTransport) {
       nsresult rv = mTransport->EnableDataNotification();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
@@ -311,20 +307,16 @@ PresentationSessionInfo::SendBlob(nsIDOM
 
   return mTransport->SendBlob(aBlob);
 }
 
 nsresult
 PresentationSessionInfo::Close(nsresult aReason,
                                uint32_t aState)
 {
-  if (NS_WARN_IF(!IsSessionReady())) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
   // Do nothing if session is already terminated.
   if (nsIPresentationSessionListener::STATE_TERMINATED == mState) {
     return NS_OK;
   }
 
   SetStateWithReason(aState, aReason);
 
   switch (aState) {
@@ -515,33 +507,32 @@ PresentationSessionInfo::NotifyData(cons
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return mListener->NotifyMessage(mSessionId, aData, aIsBinary);
 }
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
-PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
+PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* aTransport)
 {
   PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
 
   ResetBuilder();
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
     return NS_ERROR_FAILURE;
   }
 
-  // The session transport is managed by content process
-  if (!transport) {
-    return NS_OK;
+  if (NS_WARN_IF(!aTransport)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  mTransport = transport;
+  mTransport = aTransport;
 
   nsresult rv = mTransport->SetCallback(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mListener) {
     mTransport->EnableDataNotification();
@@ -646,18 +637,16 @@ PresentationControllingInfo::Shutdown(ns
 {
   PresentationSessionInfo::Shutdown(aReason);
 
   // Close the server socket if any.
   if (mServerSocket) {
     Unused << NS_WARN_IF(NS_FAILED(mServerSocket->Close()));
     mServerSocket = nullptr;
   }
-
-  mIsReconnecting = false;
 }
 
 nsresult
 PresentationControllingInfo::GetAddress()
 {
 #if defined(MOZ_WIDGET_GONK)
   nsCOMPtr<nsINetworkManager> networkManager =
     do_GetService("@mozilla.org/network/manager;1");
@@ -837,23 +826,22 @@ PresentationControllingInfo::NotifyConne
 
 NS_IMETHODIMP
 PresentationControllingInfo::NotifyReconnected()
 {
   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
 
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mReconnectCallback);
 
   if (NS_WARN_IF(mState != nsIPresentationSessionListener::STATE_CONNECTING)) {
     return NS_ERROR_FAILURE;
   }
 
-  return mReconnectCallback->NotifySuccess(GetUrl());
+  return NotifyReconnectResult(NS_OK);
 }
 
 nsresult
 PresentationControllingInfo::BuildTransport()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
@@ -931,18 +919,33 @@ PresentationControllingInfo::NotifyDisco
 
   if (NS_WARN_IF(NS_FAILED(aReason) || !mIsResponderReady)) {
     // The presentation session instance may already exist.
     // Change the state to CLOSED if it is not terminated.
     if (nsIPresentationSessionListener::STATE_TERMINATED != mState) {
       SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aReason);
     }
 
-    // Reply error for an abnormal close.
-    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+    // If |aReason| is NS_OK, it implies that the user closes the connection
+    // before becomming connected. No need to call |ReplyError| in this case.
+    if (NS_FAILED(aReason)) {
+      if (mIsReconnecting) {
+        NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR);
+      }
+      // Reply error for an abnormal close.
+      return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+    }
+    Shutdown(aReason);
+  }
+
+  // This is the case for reconnecting a connection which is in
+  // connecting state and |mTransport| is not ready.
+  if (mDoReconnectAfterClose && !mTransport) {
+    mDoReconnectAfterClose = false;
+    return Reconnect(mReconnectCallback);
   }
 
   return NS_OK;
 }
 
 // nsIServerSocketListener
 NS_IMETHODIMP
 PresentationControllingInfo::OnSocketAccepted(nsIServerSocket* aServerSocket,
@@ -1017,60 +1020,72 @@ PresentationControllingInfo::OnStopListe
  * 5. Once both step 3 and 4 are done, the rest is to build a new data
  *    transport channel by following the same steps as starting a
  *    new session.
  */
 
 nsresult
 PresentationControllingInfo::Reconnect(nsIPresentationServiceCallback* aCallback)
 {
+  PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
+             NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
+
   if (!aCallback) {
     return NS_ERROR_INVALID_ARG;
   }
 
   mReconnectCallback = aCallback;
 
   if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) {
-    return mReconnectCallback->NotifyError(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return NotifyReconnectResult(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
-  SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK);
+  // If |mState| is not CLOSED, we have to close the connection before
+  // reconnecting. The process to reconnect will be continued after
+  // |NotifyDisconnected| or |NotifyTransportClosed| is invoked.
+  if (mState == nsIPresentationSessionListener::STATE_CONNECTING ||
+      mState == nsIPresentationSessionListener::STATE_CONNECTED) {
+    mDoReconnectAfterClose = true;
+    return Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED);
+  }
+
+  // Make sure |mState| is closed at this point.
+  MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED);
+
+  mState = nsIPresentationSessionListener::STATE_CONNECTING;
+  mIsReconnecting = true;
 
   nsresult rv = NS_OK;
   if (!mControlChannel) {
     nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
     rv = mDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel));
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+      return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR);
     }
 
     rv = Init(ctrlChannel);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+      return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR);
     }
-
-    mIsReconnecting = true;
   } else {
     return ContinueReconnect();
   }
 
   return NS_OK;
 }
 
 nsresult
 PresentationControllingInfo::ContinueReconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mControlChannel);
-  MOZ_ASSERT(mReconnectCallback);
 
   mIsReconnecting = false;
-  if (NS_WARN_IF(NS_FAILED(mControlChannel->Reconnect(mSessionId, GetUrl()))) &&
-      mReconnectCallback) {
-    return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+  if (NS_WARN_IF(NS_FAILED(mControlChannel->Reconnect(mSessionId, GetUrl())))) {
+    return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 // nsIListNetworkAddressesListener
 NS_IMETHODIMP
 PresentationControllingInfo::OnListedNetworkAddresses(const char** aAddressArray,
@@ -1110,16 +1125,62 @@ PresentationControllingInfo::OnListNetwo
     NewRunnableMethod<nsCString>(
       this,
       &PresentationControllingInfo::OnGetAddress,
       "127.0.0.1"));
 
   return NS_OK;
 }
 
+nsresult
+PresentationControllingInfo::NotifyReconnectResult(nsresult aStatus)
+{
+  if (!mReconnectCallback) {
+    MOZ_ASSERT(false, "mReconnectCallback can not be null here.");
+    return NS_ERROR_FAILURE;
+  }
+
+  mIsReconnecting = false;
+  nsCOMPtr<nsIPresentationServiceCallback> callback =
+    mReconnectCallback.forget();
+  if (NS_FAILED(aStatus)) {
+    return callback->NotifyError(aStatus);
+  }
+
+  return callback->NotifySuccess(GetUrl());
+}
+
+// nsIPresentationSessionTransportCallback
+NS_IMETHODIMP
+PresentationControllingInfo::NotifyTransportReady()
+{
+  return PresentationSessionInfo::NotifyTransportReady();
+}
+
+NS_IMETHODIMP
+PresentationControllingInfo::NotifyTransportClosed(nsresult aReason)
+{
+  if (!mDoReconnectAfterClose) {
+    return PresentationSessionInfo::NotifyTransportClosed(aReason);;
+  }
+
+  MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED);
+
+  mTransport = nullptr;
+  mIsTransportReady = false;
+  mDoReconnectAfterClose = false;
+  return Reconnect(mReconnectCallback);
+}
+
+NS_IMETHODIMP
+PresentationControllingInfo::NotifyData(const nsACString& aData, bool aIsBinary)
+{
+  return PresentationSessionInfo::NotifyData(aData, aIsBinary);
+}
+
 /**
  * Implementation of PresentationPresentingInfo
  *
  * During presentation session establishment, the receiver expects the following
  * after trying to launch the app by notifying "presentation-launch-receiver":
  * (The order between step 2 and 3 is not guaranteed.)
  * 1. |Observe| of |nsIObserver| is called with "presentation-receiver-launched".
  *    Then start listen to document |STATE_TRANSFERRING| event.
@@ -1177,27 +1238,27 @@ PresentationPresentingInfo::Shutdown(nsr
   mRequesterDescription = nullptr;
   mPendingCandidates.Clear();
   mPromise = nullptr;
   mHasFlushPendingEvents = false;
 }
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
-PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
+PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* aTransport)
 {
-  nsresult rv = PresentationSessionInfo::OnSessionTransport(transport);
+  nsresult rv = PresentationSessionInfo::OnSessionTransport(aTransport);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // The session transport is managed by content process
-  if (!transport) {
-    return NS_OK;
+  if (NS_WARN_IF(!aTransport)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
   // send answer for TCP session transport
   if (mTransportType == nsIPresentationChannelDescription::TYPE_TCP) {
     // Prepare and send the answer.
     // In the current implementation of |PresentationSessionTransport|,
     // |GetSelfAddress| cannot return the real info when it's initialized via
     // |buildTCPReceiverTransport|. Yet this deficiency only affects the channel
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -187,16 +187,17 @@ class PresentationControllingInfo final 
                                         , public nsIServerSocketListener
                                         , public nsIListNetworkAddressesListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSISERVERSOCKETLISTENER
   NS_DECL_NSILISTNETWORKADDRESSESLISTENER
+  NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
 
   PresentationControllingInfo(const nsAString& aUrl,
                               const nsAString& aSessionId)
     : PresentationSessionInfo(aUrl,
                               aSessionId,
                               nsIPresentationService::ROLE_CONTROLLER)
   {}
 
@@ -215,19 +216,22 @@ private:
   void Shutdown(nsresult aReason) override;
 
   nsresult GetAddress();
 
   nsresult OnGetAddress(const nsACString& aAddress);
 
   nsresult ContinueReconnect();
 
+  nsresult NotifyReconnectResult(nsresult aStatus);
+
   nsCOMPtr<nsIServerSocket> mServerSocket;
   nsCOMPtr<nsIPresentationServiceCallback> mReconnectCallback;
   bool mIsReconnecting = false;
+  bool mDoReconnectAfterClose = false;
 };
 
 // Session info with presenting browsing context (receiver side) behaviors.
 class PresentationPresentingInfo final : public PresentationSessionInfo
                                        , public PromiseNativeHandler
                                        , public nsITimerCallback
 {
 public:
--- a/dom/presentation/interfaces/nsIPresentationListener.idl
+++ b/dom/presentation/interfaces/nsIPresentationListener.idl
@@ -29,21 +29,16 @@ interface nsIPresentationSessionListener
                          in nsresult reason);
 
   /*
    * Called when receive messages.
    */
   void notifyMessage(in DOMString sessionId,
                      in ACString data,
                      in boolean isBinary);
-
-  /*
-   * Called when this listener is replaced by another one.
-   */
-  void notifyReplaced();
 };
 
 [scriptable, uuid(27f101d7-9ed1-429e-b4f8-43b00e8e111c)]
 interface nsIPresentationRespondingListener : nsISupports
 {
   /*
    * Called when an incoming session connects.
    */
--- a/dom/presentation/ipc/PPresentation.ipdl
+++ b/dom/presentation/ipc/PPresentation.ipdl
@@ -76,16 +76,19 @@ sync protocol PPresentation
 
 child:
   async NotifyAvailableChange(bool aAvailable);
   async NotifySessionStateChange(nsString aSessionId,
                                  uint16_t aState,
                                  nsresult aReason);
   async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary);
   async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);
+  async NotifyCloseSessionTransport(nsString aSessionId,
+                                    uint8_t aRole,
+                                    nsresult aReason);
 
   async PPresentationBuilder(nsString aSessionId, uint8_t aRole);
 
 parent:
   async __delete__();
 
   async RegisterAvailabilityHandler();
   async UnregisterAvailabilityHandler();
--- a/dom/presentation/ipc/PresentationBuilderParent.cpp
+++ b/dom/presentation/ipc/PresentationBuilderParent.cpp
@@ -6,16 +6,108 @@
 
 #include "DCPresentationChannelDescription.h"
 #include "PresentationBuilderParent.h"
 #include "PresentationSessionInfo.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+class PresentationSessionTransportIPC final :
+  public nsIPresentationSessionTransport
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT
+
+  PresentationSessionTransportIPC(PresentationParent* aParent,
+                                  const nsAString& aSessionId,
+                                  uint8_t aRole)
+    : mParent(aParent)
+    , mSessionId(aSessionId)
+    , mRole(aRole)
+  {
+    MOZ_ASSERT(mParent);
+  }
+
+private:
+  virtual ~PresentationSessionTransportIPC() = default;
+
+  RefPtr<PresentationParent> mParent;
+  nsString mSessionId;
+  uint8_t mRole;
+};
+
+NS_IMPL_ISUPPORTS(PresentationSessionTransportIPC,
+                  nsIPresentationSessionTransport)
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::GetCallback(
+                           nsIPresentationSessionTransportCallback** aCallback)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::SetCallback(
+                            nsIPresentationSessionTransportCallback* aCallback)
+{
+  if (aCallback) {
+    aCallback->NotifyTransportReady();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::GetSelfAddress(nsINetAddr** aSelfAddress)
+{
+  MOZ_ASSERT(false, "Not expected.");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::EnableDataNotification()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::Send(const nsAString& aData)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::SendBinaryMsg(const nsACString& aData)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::SendBlob(nsIDOMBlob* aBlob)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PresentationSessionTransportIPC::Close(nsresult aReason)
+{
+  if (NS_WARN_IF(!mParent->SendNotifyCloseSessionTransport(mSessionId,
+                                                           mRole,
+                                                           aReason))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+} // anonymous namespace
+
 NS_IMPL_ISUPPORTS(PresentationBuilderParent,
                   nsIPresentationSessionTransportBuilder,
                   nsIPresentationDataChannelSessionTransportBuilder)
 
 PresentationBuilderParent::PresentationBuilderParent(PresentationParent* aParent)
   : mParent(aParent)
 {
   MOZ_COUNT_CTOR(PresentationBuilderParent);
@@ -34,21 +126,25 @@ NS_IMETHODIMP
 PresentationBuilderParent::BuildDataChannelTransport(
                       uint8_t aRole,
                       mozIDOMWindow* aWindow, /* unused */
                       nsIPresentationSessionTransportBuilderListener* aListener)
 {
   mBuilderListener = aListener;
 
   RefPtr<PresentationSessionInfo> info = static_cast<PresentationSessionInfo*>(aListener);
+  nsAutoString sessionId(info->GetSessionId());
   if (NS_WARN_IF(!mParent->SendPPresentationBuilderConstructor(this,
-                                                               nsString(info->GetSessionId()),
+                                                               sessionId,
                                                                aRole))) {
     return NS_ERROR_FAILURE;
   }
+  mIPCSessionTransport = new PresentationSessionTransportIPC(mParent,
+                                                             sessionId,
+                                                             aRole);
   mNeedDestroyActor = true;
   mParent = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationBuilderParent::OnIceCandidate(const nsAString& aCandidate)
 {
@@ -145,27 +241,20 @@ PresentationBuilderParent::RecvClose(con
   }
   return true;
 }
 
 // Delegate to nsIPresentationSessionTransportBuilderListener
 bool
 PresentationBuilderParent::RecvOnSessionTransport()
 {
-  // To avoid releasing |this| in this method
-  NS_DispatchToMainThread(NS_NewRunnableFunction([this]() -> void {
-    Unused <<
-      NS_WARN_IF(!mBuilderListener ||
-                 NS_FAILED(mBuilderListener->OnSessionTransport(nullptr)));
-  }));
-
-  nsCOMPtr<nsIPresentationSessionTransportCallback>
-    callback(do_QueryInterface(mBuilderListener));
-
-  callback->NotifyTransportReady();
+  RefPtr<PresentationBuilderParent> kungFuDeathGrip = this;
+  Unused <<
+    NS_WARN_IF(!mBuilderListener ||
+               NS_FAILED(mBuilderListener->OnSessionTransport(mIPCSessionTransport)));
   return true;
 }
 
 bool
 PresentationBuilderParent::RecvOnSessionTransportError(const nsresult& aReason)
 {
   if (NS_WARN_IF(!mBuilderListener ||
                  NS_FAILED(mBuilderListener->OnError(aReason)))) {
--- a/dom/presentation/ipc/PresentationBuilderParent.h
+++ b/dom/presentation/ipc/PresentationBuilderParent.h
@@ -38,14 +38,15 @@ public:
 
   virtual bool RecvOnSessionTransportError(const nsresult& aReason) override;
 
 private:
   virtual ~PresentationBuilderParent();
   bool mNeedDestroyActor = false;
   RefPtr<PresentationParent> mParent;
   nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mBuilderListener;
+  nsCOMPtr<nsIPresentationSessionTransport> mIPCSessionTransport;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationBuilderParent_h__
--- a/dom/presentation/ipc/PresentationChild.cpp
+++ b/dom/presentation/ipc/PresentationChild.cpp
@@ -128,16 +128,28 @@ PresentationChild::RecvNotifySessionConn
                                             const nsString& aSessionId)
 {
   if (mService) {
     Unused << NS_WARN_IF(NS_FAILED(mService->NotifySessionConnect(aWindowId, aSessionId)));
   }
   return true;
 }
 
+bool
+PresentationChild::RecvNotifyCloseSessionTransport(const nsString& aSessionId,
+                                                   const uint8_t& aRole,
+                                                   const nsresult& aReason)
+{
+  if (mService) {
+    Unused << NS_WARN_IF(NS_FAILED(
+      mService->CloseContentSessionTransport(aSessionId, aRole, aReason)));
+  }
+  return true;
+}
+
 /*
  * Implementation of PresentationRequestChild
  */
 
 PresentationRequestChild::PresentationRequestChild(nsIPresentationServiceCallback* aCallback)
   : mActorDestroyed(false)
   , mCallback(aCallback)
 {
--- a/dom/presentation/ipc/PresentationChild.h
+++ b/dom/presentation/ipc/PresentationChild.h
@@ -54,16 +54,21 @@ public:
   RecvNotifyMessage(const nsString& aSessionId,
                     const nsCString& aData,
                     const bool& aIsBinary) override;
 
   virtual bool
   RecvNotifySessionConnect(const uint64_t& aWindowId,
                            const nsString& aSessionId) override;
 
+  virtual bool
+  RecvNotifyCloseSessionTransport(const nsString& aSessionId,
+                                  const uint8_t& aRole,
+                                  const nsresult& aReason) override;
+
 private:
   virtual ~PresentationChild();
 
   bool mActorDestroyed = false;
   RefPtr<PresentationIPCService> mService;
 };
 
 class PresentationRequestChild final : public PPresentationRequestChild
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -263,17 +263,16 @@ PresentationIPCService::RegisterSessionL
                                                 uint8_t aRole,
                                                 nsIPresentationSessionListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
 
   nsCOMPtr<nsIPresentationSessionListener> listener;
   if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) {
-    Unused << NS_WARN_IF(NS_FAILED(listener->NotifyReplaced()));
     mSessionListeners.Put(aSessionId, aListener);
     return NS_OK;
   }
 
   mSessionListeners.Put(aSessionId, aListener);
   if (sPresentationChild) {
     Unused <<
       NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(
@@ -503,8 +502,22 @@ nsresult
 PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId,
                                                 nsIDocShell* aDocShell)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mCallback = new PresentationResponderLoadingCallback(aSessionId);
   return mCallback->Init(aDocShell);
 }
+
+nsresult
+PresentationIPCService::CloseContentSessionTransport(const nsString& aSessionId,
+                                                     uint8_t aRole,
+                                                     nsresult aReason)
+{
+  RefPtr<PresentationContentSessionInfo> info =
+    GetSessionInfo(aSessionId, aRole);
+  if (NS_WARN_IF(!info)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return info->Close(aReason);
+}
--- a/dom/presentation/ipc/PresentationIPCService.h
+++ b/dom/presentation/ipc/PresentationIPCService.h
@@ -48,16 +48,20 @@ public:
 
   nsresult MonitorResponderLoading(const nsAString& aSessionId,
                                    nsIDocShell* aDocShell);
 
   nsresult NotifySessionTransport(const nsString& aSessionId,
                                   const uint8_t& aRole,
                                   nsIPresentationSessionTransport* transport);
 
+  nsresult CloseContentSessionTransport(const nsString& aSessionId,
+                                        uint8_t aRole,
+                                        nsresult aReason);
+
 private:
   virtual ~PresentationIPCService();
   nsresult SendRequest(nsIPresentationServiceCallback* aCallback,
                        const PresentationIPCRequest& aRequest);
 
   nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener> > mAvailabilityListeners;
   nsRefPtrHashtable<nsStringHashKey,
                     nsIPresentationSessionListener> mSessionListeners;
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -301,24 +301,16 @@ PresentationParent::NotifyStateChange(co
                                                aState,
                                                aReason))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationParent::NotifyReplaced()
-{
-  // Do nothing here, since |PresentationIPCService::RegisterSessionListener|
-  // already dealt with this in content process.
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 PresentationParent::NotifyMessage(const nsAString& aSessionId,
                                   const nsACString& aData,
                                   bool aIsBinary)
 {
   if (NS_WARN_IF(mActorDestroyed ||
                  !SendNotifyMessage(nsString(aSessionId),
                                     nsCString(aData),
                                     aIsBinary))) {