Bug 1201805 - [Presentation WebAPI] Fix collaboration issues with control channel. Part 2 - Adjust the timing to send offer. r=smaug
authorSean Lin <selin@mozilla.com>
Fri, 04 Sep 2015 15:54:34 +0800
changeset 294539 780bdbf0c0a9ac2ab5d18965f480760f6d5defa8
parent 294538 cd93c40bb34f1a169127ee8482bba020d872c2c2
child 294540 8d707bceab14479a09bc10dc13d7afd3969b343c
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1201805
milestone43.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 1201805 - [Presentation WebAPI] Fix collaboration issues with control channel. Part 2 - Adjust the timing to send offer. r=smaug
dom/presentation/PresentationSessionInfo.cpp
dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
dom/presentation/tests/mochitest/test_presentation_sender.html
dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -339,28 +339,31 @@ PresentationSessionInfo::NotifyData(cons
 
   return mListener->NotifyMessage(mSessionId, aData);
 }
 
 /*
  * Implementation of PresentationRequesterInfo
  *
  * During presentation session establishment, the sender expects the following
- * after trying to establish the control channel: (The order between step 2 and
- * 3 is not guaranteed.)
+ * after trying to establish the control channel: (The order between step 3 and
+ * 4 is not guaranteed.)
  * 1. |Init| is called to open a socket |mServerSocket| for data transport
- *    channel and send the offer to the receiver via the control channel.
- * 2.1 |OnSocketAccepted| of |nsIServerSocketListener| is called to indicate the
+ *    channel.
+ * 2. |NotifyOpened| of |nsIPresentationControlChannelListener| is called to
+ *    indicate the control channel is ready to use. Then send the offer to the
+ *    receiver via the control channel.
+ * 3.1 |OnSocketAccepted| of |nsIServerSocketListener| is called to indicate the
  *     data transport channel is connected. Then initialize |mTransport|.
- * 2.2 |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
+ * 3.2 |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
  *     called.
- * 3. |OnAnswer| of |nsIPresentationControlChannelListener| is called to
+ * 4. |OnAnswer| of |nsIPresentationControlChannelListener| is called to
  *    indicate the receiver is ready. Close the control channel since it's no
  *    longer needed.
- * 4. Once both step 2 and 3 are done, the presentation session is ready to use.
+ * 5. Once both step 3 and 4 are done, the presentation session is ready to use.
  *    So notify the listener of CONNECTED state.
  */
 
 NS_IMPL_ISUPPORTS_INHERITED(PresentationRequesterInfo,
                             PresentationSessionInfo,
                             nsIServerSocketListener)
 
 nsresult
@@ -380,36 +383,16 @@ PresentationRequesterInfo::Init(nsIPrese
     return rv;
   }
 
   rv = mServerSocket->AsyncListen(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Prepare and send the offer.
-  int32_t port;
-  rv = mServerSocket->GetPort(&port);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsCString address;
-  rv = GetAddress(address);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsRefPtr<PresentationChannelDescription> description =
-    new PresentationChannelDescription(address, static_cast<uint16_t>(port));
-  rv = mControlChannel->SendOffer(description);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
   return NS_OK;
 }
 
 void
 PresentationRequesterInfo::Shutdown(nsresult aReason)
 {
   PresentationSessionInfo::Shutdown(aReason);
 
@@ -491,18 +474,32 @@ PresentationRequesterInfo::OnAnswer(nsIP
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationRequesterInfo::NotifyOpened()
 {
-  // Do nothing and wait for receiver to be ready.
-  return NS_OK;
+  // Prepare and send the offer.
+  int32_t port;
+  nsresult rv = mServerSocket->GetPort(&port);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString address;
+  rv = GetAddress(address);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsRefPtr<PresentationChannelDescription> description =
+    new PresentationChannelDescription(address, static_cast<uint16_t>(port));
+  return mControlChannel->SendOffer(description);
 }
 
 NS_IMETHODIMP
 PresentationRequesterInfo::NotifyClosed(nsresult aReason)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Unset control channel here so it won't try to re-close it in potential
@@ -661,34 +658,29 @@ PresentationResponderInfo::InitTransport
   // Prepare and send the answer.
   // TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
   // In the current implementation of |PresentationSessionTransport|,
   // |GetSelfAddress| cannot return the real info when it's initialized via
   // |InitWithChannelDescription|. Yet this deficiency only affects the channel
   // description for the answer, which is not actually checked at requester side.
   nsCOMPtr<nsINetAddr> selfAddr;
   rv = mTransport->GetSelfAddress(getter_AddRefs(selfAddr));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  NS_WARN_IF(NS_FAILED(rv));
 
   nsCString address;
-  selfAddr->GetAddress(address);
-  uint16_t port;
-  selfAddr->GetPort(&port);
+  uint16_t port = 0;
+  if (NS_SUCCEEDED(rv)) {
+    selfAddr->GetAddress(address);
+    selfAddr->GetPort(&port);
+  }
   nsCOMPtr<nsIPresentationChannelDescription> description =
     new PresentationChannelDescription(address, port);
 
-  rv = mControlChannel->SendAnswer(description);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
- }
+  return mControlChannel->SendAnswer(description);
+}
 
 nsresult
 PresentationResponderInfo::UntrackFromService()
 {
   // Remove the OOP responding info (if it has never been used).
   if (mContentParent) {
     NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverCleanUp(mSessionId));
   }
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -147,16 +147,20 @@ const mockedControlChannel = {
   simulateOnOffer: function() {
     sendAsyncMessage('offer-received');
     this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onOffer(mockedChannelDescription);
   },
   simulateOnAnswer: function() {
     sendAsyncMessage('answer-received');
     this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onAnswer(mockedChannelDescription);
   },
+  simulateNotifyOpened: function() {
+    sendAsyncMessage('control-channel-opened');
+    this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyOpened();
+  },
 };
 
 const mockedDevice = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'id',
   name: 'name',
   type: 'type',
   establishControlChannel: function(url, presentationId) {
@@ -364,16 +368,20 @@ addMessageListener('trigger-incoming-off
 addMessageListener('trigger-incoming-answer', function() {
   mockedControlChannel.simulateOnAnswer();
 });
 
 addMessageListener('trigger-incoming-transport', function() {
   mockedServerSocket.simulateOnSocketAccepted(mockedServerSocket, mockedSocketTransport);
 });
 
+addMessageListener('trigger-control-channel-open', function(reason) {
+  mockedControlChannel.simulateNotifyOpened();
+});
+
 addMessageListener('trigger-control-channel-close', function(reason) {
   mockedControlChannel.close(reason);
 });
 
 addMessageListener('trigger-data-transport-close', function(reason) {
   mockedSessionTransport.close(reason);
 });
 
--- a/dom/presentation/tests/mochitest/test_presentation_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender.html
@@ -36,16 +36,22 @@ function testStartSession() {
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-select');
     });
 
     gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
       gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
       info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
     });
 
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       info("The control channel is closed. " + aReason);
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
--- a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
@@ -36,16 +36,22 @@ function testStartSession() {
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-select');
     });
 
     gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
       gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
       info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
     });
 
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       info("The control channel is closed. " + aReason);
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
--- a/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
@@ -71,16 +71,22 @@ function testStartSessionUnexpectedContr
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-select');
     });
 
     gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
       gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
       info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
     });
 
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       info("The control channel is closed. " + aReason);
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
@@ -108,16 +114,22 @@ function testStartSessionUnexpectedContr
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-select');
     });
 
     gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
       gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
       info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
     });
 
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       info("The control channel is closed. " + aReason);
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
@@ -156,16 +168,22 @@ function testStartSessionUnexpectedDataT
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-select');
     });
 
     gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
       gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
       info("A control channel is established.");
+      gScript.sendAsyncMessage('trigger-control-channel-open');
+    });
+
+    gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) {
+      gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler);
+      info("The control channel is opened.");
     });
 
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       info("The control channel is closed. " + aReason);
     });
 
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {