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 261678 cd93c40bb34f1a169127ee8482bba020d872c2c2
parent 261677 062f26a7b9327a802ee5220362c37f9766845b28
child 261679 780bdbf0c0a9ac2ab5d18965f480760f6d5defa8
push id17426
push usercbook@mozilla.com
push dateFri, 11 Sep 2015 08:06:54 +0000
treeherderb2g-inbound@b0c1649e39d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs1201805
milestone43.0a1
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');