Bug 1201805 - [Presentation WebAPI] Fix collaboration issues with control channel. Part 1 - String mismatch in channel description. r=fabrice
authorSean Lin <selin@mozilla.com>
Tue, 01 Sep 2015 16:52:51 +0800
changeset 261939 cd93c40bb34f1a169127ee8482bba020d872c2c2
parent 261938 062f26a7b9327a802ee5220362c37f9766845b28
child 261940 780bdbf0c0a9ac2ab5d18965f480760f6d5defa8
push id29356
push userphilringnalda@gmail.com
push dateSat, 12 Sep 2015 03:27:28 +0000
treeherdermozilla-central@a4c276cfaf9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
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 1 - String mismatch in channel description. r=fabrice
dom/presentation/interfaces/nsIPresentationControlChannel.idl
dom/presentation/provider/TCPPresentationServer.js
dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
dom/presentation/tests/mochitest/test_presentation_receiver.html
dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
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
dom/presentation/tests/xpcshell/test_tcp_control_channel.js
--- a/dom/presentation/interfaces/nsIPresentationControlChannel.idl
+++ b/dom/presentation/interfaces/nsIPresentationControlChannel.idl
@@ -11,17 +11,17 @@ interface nsIInputStream;
 interface nsIPresentationChannelDescription: nsISupports
 {
   const unsigned short TYPE_TCP = 1;
   const unsigned short TYPE_DATACHANNEL = 2;
 
   // Type of transport channel.
   readonly attribute uint8_t type;
 
-  // Addresses for TCP channel.
+  // Addresses for TCP channel (as a list of nsISupportsCString).
   // Should only be used while type == TYPE_TCP.
   readonly attribute nsIArray tcpAddress;
 
   // Port number for TCP channel.
   // Should only be used while type == TYPE_TCP.
   readonly attribute uint16_t tcpPort;
 
   // SDP for Data Channel.
--- a/dom/presentation/provider/TCPPresentationServer.js
+++ b/dom/presentation/provider/TCPPresentationServer.js
@@ -259,18 +259,18 @@ TCPPresentationServer.prototype = {
 
 function ChannelDescription(aInit) {
   this._type = aInit.type;
   switch (this._type) {
     case Ci.nsIPresentationChannelDescription.TYPE_TCP:
       this._tcpAddresses = Cc["@mozilla.org/array;1"]
                            .createInstance(Ci.nsIMutableArray);
       for (let address of aInit.tcpAddress) {
-        let wrapper = Cc["@mozilla.org/supports-string;1"]
-                      .createInstance(Ci.nsISupportsString);
+        let wrapper = Cc["@mozilla.org/supports-cstring;1"]
+                      .createInstance(Ci.nsISupportsCString);
         wrapper.data = address;
         this._tcpAddresses.appendElement(wrapper, false);
       }
 
       this._tcpPort = aInit.tcpPort;
       break;
     case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
       this._dataChannelSDP = aInit.dataChannelSDP;
@@ -308,17 +308,17 @@ ChannelDescription.prototype = {
 function discriptionAsJson(aDescription) {
   let json = {};
   json.type = aDescription.type;
   switch(aDescription.type) {
     case Ci.nsIPresentationChannelDescription.TYPE_TCP:
       let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray);
       json.tcpAddress = [];
       for (let idx = 0; idx < addresses.length; idx++) {
-        let address = addresses.queryElementAt(idx, Ci.nsISupportsString);
+        let address = addresses.queryElementAt(idx, Ci.nsISupportsCString);
         json.tcpAddress.push(address.data);
       }
       json.tcpPort = aDescription.tcpPort;
       break;
     case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
       json.dataChannelSDP = aDescription.dataChannelSDP;
       break;
   }
@@ -536,21 +536,21 @@ TCPControlChannel.prototype = {
         this._presentationServer.onSessionRequest(aMsg.id,
                                                   aMsg.url,
                                                   aMsg.presentationId,
                                                   this);
         this._notifyOpened();
         break;
       }
       case "requestSession:Offer": {
-        this._listener.onOffer(new ChannelDescription(aMsg.offer));
+        this._onOffer(aMsg.offer);
         break;
       }
       case "requestSession:Answer": {
-        this._listener.onAnswer(new ChannelDescription(aMsg.answer));
+        this._onAnswer(aMsg.answer);
         break;
       }
     }
   },
 
   get listener() {
     return this._listener;
   },
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -95,19 +95,51 @@ const mockedControlChannel = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
   set listener(listener) {
     this._listener = listener;
   },
   get listener() {
     return this._listener;
   },
   sendOffer: function(offer) {
-    sendAsyncMessage('offer-sent');
+    var isValid = false;
+    try {
+      var addresses = offer.tcpAddress;
+      if (addresses.length > 0) {
+        for (var i = 0; i < addresses.length; i++) {
+          // Ensure CString addresses are used. Otherwise, an error will be thrown.
+          addresses.queryElementAt(i, Ci.nsISupportsCString);
+        }
+
+        isValid = true;
+      }
+    } catch (e) {
+      isValid = false;
+    }
+
+    sendAsyncMessage('offer-sent', isValid);
   },
   sendAnswer: function(answer) {
+    var isValid = false;
+    try {
+      var addresses = answer.tcpAddress;
+      if (addresses.length > 0) {
+        for (var i = 0; i < addresses.length; i++) {
+          // Ensure CString addresses are used. Otherwise, an error will be thrown.
+          addresses.queryElementAt(i, Ci.nsISupportsCString);
+        }
+
+        isValid = true;
+      }
+    } catch (e) {
+      isValid = false;
+    }
+
+    sendAsyncMessage('answer-sent', isValid);
+
     this._listener.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady();
   },
   close: function(reason) {
     sendAsyncMessage('control-channel-closed', reason);
     this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyClosed(reason);
   },
   simulateReceiverReady: function() {
     this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyReceiverReady();
--- a/dom/presentation/tests/mochitest/test_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver.html
@@ -57,16 +57,21 @@ function setup() {
     });
     obs.notifyObservers(promise, 'setup-request-promise', null);
 
     gScript.addMessageListener('offer-received', function offerReceivedHandler() {
       gScript.removeMessageListener('offer-received', offerReceivedHandler);
       info("An offer is received.");
     });
 
+    gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) {
+      gScript.removeMessageListener('answer-sent', answerSentHandler);
+      ok(aIsValid, "A valid answer is sent.");
+    });
+
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally.");
     });
 
     gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() {
       gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler);
       info("Data notification is enabled for data transport channel.");
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
@@ -106,16 +106,21 @@ function setup() {
 
     document.body.appendChild(nonReceiverIframe);
 
     gScript.addMessageListener('offer-received', function offerReceivedHandler() {
       gScript.removeMessageListener('offer-received', offerReceivedHandler);
       info("An offer is received.");
     });
 
+    gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) {
+      gScript.removeMessageListener('answer-sent', answerSentHandler);
+      ok(aIsValid, "A valid answer is sent.");
+    });
+
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally.");
     });
 
     gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() {
       gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler);
       info("Data notification is enabled for data transport channel.");
--- a/dom/presentation/tests/mochitest/test_presentation_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender.html
@@ -43,19 +43,19 @@ function testStartSession() {
       info("A control channel is established.");
     });
 
     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() {
+    gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
-      info("An offer is sent out.");
+      ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-incoming-transport');
     });
 
     gScript.addMessageListener('answer-received', function answerReceivedHandler() {
       gScript.removeMessageListener('answer-received', answerReceivedHandler);
       info("An answer is received.");
     });
 
--- a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
@@ -43,19 +43,19 @@ function testStartSession() {
       info("A control channel is established.");
     });
 
     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() {
+    gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
-      info("An offer is sent out.");
+      ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-incoming-answer');
     });
 
     gScript.addMessageListener('answer-received', function answerReceivedHandler() {
       gScript.removeMessageListener('answer-received', answerReceivedHandler);
       info("An answer is received.");
       gScript.sendAsyncMessage('trigger-incoming-transport');
     });
--- a/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
@@ -78,19 +78,19 @@ function testStartSessionUnexpectedContr
       info("A control channel is established.");
     });
 
     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() {
+    gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
-      info("An offer is sent out.");
+      ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
     });
 
     presentation.startSession("http://example.com").then(
       function(aSession) {
         ok(false, "startSession shouldn't succeed in this case.");
         aReject();
       },
@@ -115,19 +115,19 @@ function testStartSessionUnexpectedContr
       info("A control channel is established.");
     });
 
     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() {
+    gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
-      info("An offer is sent out.");
+      ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-incoming-transport');
     });
 
     gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
       gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
       info("Data transport channel is initialized.");
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT);
     });
@@ -163,19 +163,19 @@ function testStartSessionUnexpectedDataT
       info("A control channel is established.");
     });
 
     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() {
+    gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
-      info("An offer is sent out.");
+      ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-incoming-transport');
     });
 
     gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
       gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
       info("Data transport channel is initialized.");
       gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED);
     });
--- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
+++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
@@ -24,18 +24,18 @@ function makeJointSuccess(names) {
   return funcs;
 }
 
 function TestDescription(aType, aTcpAddress, aTcpPort) {
   this.type = aType;
   this.tcpAddress = Cc["@mozilla.org/array;1"]
                       .createInstance(Ci.nsIMutableArray);
   for (let address of aTcpAddress) {
-    let wrapper = Cc["@mozilla.org/supports-string;1"]
-                    .createInstance(Ci.nsISupportsString);
+    let wrapper = Cc["@mozilla.org/supports-cstring;1"]
+                    .createInstance(Ci.nsISupportsCString);
     wrapper.data = address;
     this.tcpAddress.appendElement(wrapper, false);
   }
   this.tcpPort = aTcpPort;
 }
 
 TestDescription.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
@@ -86,17 +86,17 @@ function testPresentationServer() {
 
       controllerControlChannel.listener = {
         status: 'created',
         onOffer: function(aOffer) {
           Assert.equal(this.status, 'opened', '1. controllerControlChannel: get offer, send answer');
           this.status = 'onOffer';
 
           let offer = aOffer.QueryInterface(Ci.nsIPresentationChannelDescription);
-          Assert.strictEqual(offer.tcpAddress.queryElementAt(0,Ci.nsISupportsString).data,
+          Assert.strictEqual(offer.tcpAddress.queryElementAt(0,Ci.nsISupportsCString).data,
                              OFFER_ADDRESS,
                              'expected offer address array');
           Assert.equal(offer.tcpPort, OFFER_PORT, 'expected offer port');
           try {
             let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP;
             let answer = new TestDescription(tcpType, [ANSWER_ADDRESS], ANSWER_PORT);
             controllerControlChannel.sendAnswer(answer);
           } catch (e) {
@@ -143,17 +143,17 @@ function testPresentationServer() {
     status: 'created',
     onOffer: function(offer) {
       Assert.ok(false, 'get offer');
     },
     onAnswer: function(aAnswer) {
       Assert.equal(this.status, 'opened', '2. presenterControlChannel: get answer, close channel');
 
       let answer = aAnswer.QueryInterface(Ci.nsIPresentationChannelDescription);
-      Assert.strictEqual(answer.tcpAddress.queryElementAt(0,Ci.nsISupportsString).data,
+      Assert.strictEqual(answer.tcpAddress.queryElementAt(0,Ci.nsISupportsCString).data,
                          ANSWER_ADDRESS,
                          'expected answer address array');
       Assert.equal(answer.tcpPort, ANSWER_PORT, 'expected answer port');
 
       presenterControlChannel.close(Cr.NS_OK);
     },
     notifyOpened: function() {
       Assert.equal(this.status, 'created', '0. presenterControlChannel: opened, send offer');