Bug 1209744 - Implement canTrickleIceCandidates attribute, r=bwc, r=khuey
☠☠ backed out by 3adc12ed39e2 ☠ ☠
authorMartin Thomson <martin.thomson@gmail.com>
Thu, 01 Oct 2015 12:52:00 +0200
changeset 265961 b958e25b1ecf218ca7ddb94357e83878ac9c753b
parent 265960 f8547896d5823f66f61430cdaf5d7104cb241604
child 265962 36cd98d7f222d2e4177dc43eb55e0b1f655b55dd
push id66078
push usercbook@mozilla.com
push dateMon, 05 Oct 2015 07:30:10 +0000
treeherdermozilla-inbound@b958e25b1ecf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc, khuey
bugs1209744
milestone44.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 1209744 - Implement canTrickleIceCandidates attribute, r=bwc, r=khuey
dom/media/PeerConnection.js
dom/media/tests/mochitest/nonTrickleIce.js
dom/media/tests/mochitest/templates.js
dom/media/tests/mochitest/test_peerConnection_remoteRollback.html
dom/webidl/RTCPeerConnection.webidl
testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -336,16 +336,20 @@ function RTCPeerConnection() {
   this._onGetStatsFailure = null;
   this._onReplaceTrackSender = null;
   this._onReplaceTrackWithTrack = null;
   this._onReplaceTrackSuccess = null;
   this._onReplaceTrackFailure = null;
 
   this._localType = null;
   this._remoteType = null;
+  // http://rtcweb-wg.github.io/jsep/#rfc.section.4.1.9
+  // canTrickle == null means unknown; when a remote description is received it
+  // is set to true or false based on the presence of the "trickle" ice-option
+  this._canTrickle = null;
 
   // States
   this._iceGatheringState = this._iceConnectionState = "new";
 }
 RTCPeerConnection.prototype = {
   classDescription: "RTCPeerConnection",
   classID: PC_CID,
   contractID: PC_CONTRACT,
@@ -914,17 +918,17 @@ RTCPeerConnection.prototype = {
       let origin = Cu.getWebIDLCallerPrincipal().origin;
 
       return this._chain(() => {
         let setRem = this.getPermission()
           .then(() => new this._win.Promise((resolve, reject) => {
             this._onSetRemoteDescriptionSuccess = resolve;
             this._onSetRemoteDescriptionFailure = reject;
             this._impl.setRemoteDescription(type, desc.sdp);
-          }));
+          })).then(() => { this._updateCanTrickle(); });
 
         if (desc.type === "rollback") {
           return setRem;
         }
 
         // Do setRemoteDescription and identity validation in parallel
         let validId = this._validateIdentity(desc.sdp, origin);
         return this._win.Promise.all([setRem, validId])
@@ -942,19 +946,47 @@ RTCPeerConnection.prototype = {
     let origin = Cu.getWebIDLCallerPrincipal().origin;
     return this._chain(
       () => this._certificateReady.then(
         () => this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin)
       )
     );
   },
 
-  updateIce: function(config) {
-    throw new this._win.DOMException("updateIce not yet implemented",
-                                     "NotSupportedError");
+  get canTrickleIceCandidates() {
+    return this._canTrickle;
+  },
+
+  _updateCanTrickle: function() {
+    let containsTrickle = section => {
+      let lines = section.toLowerCase().split(/(?:\r\n?|\n)/);
+      return lines.some(line => {
+        let prefix = "a=ice-options:";
+        if (line.substring(0, prefix.length) !== prefix) {
+          return false;
+        }
+        let tokens = line.substring(prefix.length).split(" ");
+        return tokens.some(x => x === "trickle");
+      });
+    };
+
+    let desc = null;
+    try {
+      // The getter for remoteDescription can throw if the pc is closed.
+      desc = this.remoteDescription;
+    } catch (e) {}
+    if (!desc) {
+      this._canTrickle = null;
+      return;
+    }
+
+    let sections = desc.sdp.split(/(?:\r\n?|\n)m=/);
+    let topSection = sections.shift();
+    this._canTrickle =
+      containsTrickle(topSection) || sections.every(containsTrickle);
   },
 
   addIceCandidate: function(c, onSuccess, onError) {
     return this._legacyCatch(onSuccess, onError, () => {
       if (!c.candidate && !c.sdpMLineIndex) {
         throw new this._win.DOMException("Invalid candidate passed to addIceCandidate!",
                                          "InvalidParameterError");
       }
--- a/dom/media/tests/mochitest/nonTrickleIce.js
+++ b/dom/media/tests/mochitest/nonTrickleIce.js
@@ -1,60 +1,71 @@
 /* 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/. */
 
+function removeTrickleOption(desc) {
+  var sdp = desc.sdp.replace(/\r\na=ice-options:trickle\r\n/, "\r\n");
+  return new mozRTCSessionDescription({ type: desc.type, sdp: sdp });
+}
+
 function makeOffererNonTrickle(chain) {
   chain.replace('PC_LOCAL_SETUP_ICE_HANDLER', [
     function PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER(test) {
       // We need to install this callback before calling setLocalDescription
       // otherwise we might miss callbacks
       test.pcLocal.setupIceCandidateHandler(test, () => {});
       // We ignore ICE candidates because we want the full offer
     }
   ]);
   chain.replace('PC_REMOTE_GET_OFFER', [
     function PC_REMOTE_GET_FULL_OFFER(test) {
       return test.pcLocal.endOfTrickleIce.then(() => {
-        test._local_offer = test.pcLocal.localDescription;
+        test._local_offer = removeTrickleOption(test.pcLocal.localDescription);
         test._offer_constraints = test.pcLocal.constraints;
         test._offer_options = test.pcLocal.offerOptions;
       });
     }
   ]);
   chain.insertAfter('PC_REMOTE_SANE_REMOTE_SDP', [
     function PC_REMOTE_REQUIRE_REMOTE_SDP_CANDIDATES(test) {
       info("test.pcLocal.localDescription.sdp: " + JSON.stringify(test.pcLocal.localDescription.sdp));
       info("test._local_offer.sdp" + JSON.stringify(test._local_offer.sdp));
+      is(test.pcRemote._pc.canTrickleIceCandidates, false,
+         "Remote thinks that trickle isn't supported");
       ok(!test.localRequiresTrickleIce, "Local does NOT require trickle");
       ok(test._local_offer.sdp.includes("a=candidate"), "offer has ICE candidates")
       ok(test._local_offer.sdp.includes("a=end-of-candidates"), "offer has end-of-candidates");
     }
   ]);
+  chain.remove('PC_REMOTE_CHECK_CAN_TRICKLE_SYNC');
 }
 
 function makeAnswererNonTrickle(chain) {
   chain.replace('PC_REMOTE_SETUP_ICE_HANDLER', [
     function PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER(test) {
       // We need to install this callback before calling setLocalDescription
       // otherwise we might miss callbacks
       test.pcRemote.setupIceCandidateHandler(test, () => {});
       // We ignore ICE candidates because we want the full offer
     }
   ]);
   chain.replace('PC_LOCAL_GET_ANSWER', [
     function PC_LOCAL_GET_FULL_ANSWER(test) {
       return test.pcRemote.endOfTrickleIce.then(() => {
-        test._remote_answer = test.pcRemote.localDescription;
+        test._remote_answer = removeTrickleOption(test.pcRemote.localDescription);
         test._answer_constraints = test.pcRemote.constraints;
       });
     }
   ]);
   chain.insertAfter('PC_LOCAL_SANE_REMOTE_SDP', [
     function PC_LOCAL_REQUIRE_REMOTE_SDP_CANDIDATES(test) {
       info("test.pcRemote.localDescription.sdp: " + JSON.stringify(test.pcRemote.localDescription.sdp));
       info("test._remote_answer.sdp" + JSON.stringify(test._remote_answer.sdp));
+      is(test.pcLocal._pc.canTrickleIceCandidates, false,
+         "Local thinks that trickle isn't supported");
       ok(!test.remoteRequiresTrickleIce, "Remote does NOT require trickle");
       ok(test._remote_answer.sdp.includes("a=candidate"), "answer has ICE candidates")
       ok(test._remote_answer.sdp.includes("a=end-of-candidates"), "answer has end-of-candidates");
     }
   ]);
+  chain.remove('PC_LOCAL_CHECK_CAN_TRICKLE_SYNC');
 }
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -188,16 +188,25 @@ var commandsPeerConnectionInitial = [
        "Initial local ICE connection state is 'new'");
   },
 
   function PC_REMOTE_CHECK_INITIAL_ICE_STATE(test) {
     is(test.pcRemote.iceConnectionState, ICE_NEW,
        "Initial remote ICE connection state is 'new'");
   },
 
+  function PC_LOCAL_CHECK_INITIAL_CAN_TRICKLE_SYNC(test) {
+    is(test.pcLocal._pc.canTrickleIceCandidates, null,
+       "Local trickle status should start out unknown");
+  },
+
+  function PC_REMOTE_CHECK_INITIAL_CAN_TRICKLE_SYNC(test) {
+    is(test.pcRemote._pc.canTrickleIceCandidates, null,
+       "Remote trickle status should start out unknown");
+  },
 ];
 
 var commandsGetUserMedia = [
   function PC_LOCAL_GUM(test) {
     return test.pcLocal.getAllUserMedia(test.pcLocal.constraints);
   },
 
   function PC_REMOTE_GUM(test) {
@@ -300,16 +309,21 @@ var commandsPeerConnectionOfferAnswer = 
   function PC_REMOTE_SET_REMOTE_DESCRIPTION(test) {
     return test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER)
       .then(() => {
         is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
            "signalingState after remote setRemoteDescription is 'have-remote-offer'");
       });
   },
 
+  function PC_REMOTE_CHECK_CAN_TRICKLE_SYNC(test) {
+    is(test.pcRemote._pc.canTrickleIceCandidates, true,
+       "Remote thinks that local can trickle");
+  },
+
   function PC_LOCAL_SANE_LOCAL_SDP(test) {
     test.pcLocal.localRequiresTrickleIce =
       sdputils.verifySdp(test._local_offer, "offer",
                          test._offer_constraints, test._offer_options,
                          test.testOptions);
   },
 
   function PC_REMOTE_SANE_REMOTE_SDP(test) {
@@ -371,16 +385,21 @@ var commandsPeerConnectionOfferAnswer = 
   },
   function PC_LOCAL_SANE_REMOTE_SDP(test) {
     test.pcLocal.remoteRequiresTrickleIce =
       sdputils.verifySdp(test._remote_answer, "answer",
                          test._offer_constraints, test._offer_options,
                          test.testOptions);
   },
 
+  function PC_LOCAL_CHECK_CAN_TRICKLE_SYNC(test) {
+    is(test.pcLocal._pc.canTrickleIceCandidates, true,
+       "Local thinks that remote can trickle");
+  },
+
   function PC_LOCAL_WAIT_FOR_ICE_CONNECTED(test) {
     return waitForIceConnected(test, test.pcLocal);
   },
 
   function PC_REMOTE_WAIT_FOR_ICE_CONNECTED(test) {
     return waitForIceConnected(test, test.pcRemote);
   },
 
--- a/dom/media/tests/mochitest/test_peerConnection_remoteRollback.html
+++ b/dom/media/tests/mochitest/test_peerConnection_remoteRollback.html
@@ -1,46 +1,50 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    bug: "952145",
-    title: "Rollback remote offer"
-  });
+createHTML({
+  bug: "952145",
+  title: "Rollback remote offer"
+});
+
+var test;
+runNetworkTest(function (options) {
+  test = new PeerConnectionTest(options);
+  test.setMediaConstraints([{audio: true}], [{audio: true}]);
+  test.chain.removeAfter('PC_REMOTE_CHECK_CAN_TRICKLE_SYNC');
+  test.chain.append([
+    function PC_REMOTE_ROLLBACK(test) {
+      return test.setRemoteDescription(
+        test.pcRemote,
+        new RTCSessionDescription({ type: "rollback" }),
+        STABLE);
+    },
 
-  var test;
-  runNetworkTest(function (options) {
-    test = new PeerConnectionTest(options);
-    test.setMediaConstraints([{audio: true}], [{audio: true}]);
-    test.chain.removeAfter('PC_REMOTE_SET_REMOTE_DESCRIPTION');
-    test.chain.append([
-        function PC_REMOTE_ROLLBACK(test) {
-          return test.setRemoteDescription(
-              test.pcRemote,
-              new RTCSessionDescription({ type: "rollback" }),
-              STABLE);
-        },
+    function PC_REMOTE_CHECK_CAN_TRICKLE_REVERT_SYNC(test) {
+      is(test.pcRemote._pc.canTrickleIceCandidates, null,
+         "Remote canTrickleIceCandidates is reverted to null");
+    },
 
-        function PC_LOCAL_ROLLBACK(test) {
-          return test.setLocalDescription(
-              test.pcLocal,
-              new RTCSessionDescription({ type: "rollback", sdp: ""}),
-              STABLE);
-        },
+    function PC_LOCAL_ROLLBACK(test) {
+      return test.setLocalDescription(
+        test.pcLocal,
+        new RTCSessionDescription({ type: "rollback", sdp: "" }),
+        STABLE);
+    },
 
-        // Rolling back should shut down gathering
-        function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
-          return test.pcLocal.endOfTrickleIce;
-        },
-    ]);
-    test.chain.append(commandsPeerConnectionOfferAnswer);
-    test.run();
-  });
+    // Rolling back should shut down gathering
+    function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
+      return test.pcLocal.endOfTrickleIce;
+    },
+  ]);
+  test.chain.append(commandsPeerConnectionOfferAnswer);
+  test.run();
+});
 </script>
 </pre>
 </body>
 </html>
-
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  *
  * The origin of this IDL file is
- * http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCPeerConnection
+ * http://w3c.github.io/webrtc-pc/#interface-definition
  */
 
 callback RTCSessionDescriptionCallback = void (RTCSessionDescription sdp);
 callback RTCPeerConnectionErrorCallback = void (DOMError error);
 callback VoidFunction = void ();
 callback RTCStatsCallback = void (RTCStatsReport report);
 
 enum RTCSignalingState {
@@ -98,18 +98,18 @@ interface RTCPeerConnection : EventTarge
   Promise<DOMString> getIdentityAssertion();
   Promise<RTCSessionDescription> createOffer (optional RTCOfferOptions options);
   Promise<RTCSessionDescription> createAnswer (optional RTCAnswerOptions options);
   Promise<void> setLocalDescription (RTCSessionDescription description);
   Promise<void> setRemoteDescription (RTCSessionDescription description);
   readonly attribute RTCSessionDescription? localDescription;
   readonly attribute RTCSessionDescription? remoteDescription;
   readonly attribute RTCSignalingState signalingState;
-  void updateIce (optional RTCConfiguration configuration);
   Promise<void> addIceCandidate (RTCIceCandidate candidate);
+  readonly attribute boolean? canTrickleIceCandidates;
   readonly attribute RTCIceGatheringState iceGatheringState;
   readonly attribute RTCIceConnectionState iceConnectionState;
   [Pref="media.peerconnection.identity.enabled"]
   readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
   [Pref="media.peerconnection.identity.enabled"]
   readonly attribute DOMString? idpLoginUrl;
 
   [ChromeOnly]
--- a/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
+++ b/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
@@ -8,17 +8,17 @@
 
   [RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescription)]
     expected: FAIL
 
   [RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidate)]
     expected: FAIL
 
   [RTCPeerConnection interface: attribute canTrickleIceCandidates]
-    expected: FAIL
+    expected: PASS
 
   [RTCPeerConnection interface: attribute onicegatheringstatechange]
     expected: FAIL
 
   [RTCPeerConnection interface: operation createOffer(RTCSessionDescriptionCallback,RTCPeerConnectionErrorCallback,RTCOfferOptions)]
     expected: FAIL
 
   [RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback)]
@@ -104,17 +104,17 @@
 
   [RTCPeerConnection interface: pc must inherit property "iceGatheringState" with the proper type (12)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "iceConnectionState" with the proper type (13)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "canTrickleIceCandidates" with the proper type (14)]
-    expected: FAIL
+    expected: PASS
 
   [RTCPeerConnection interface: pc must inherit property "setConfiguration" with the proper type (16)]
     expected: FAIL
 
   [RTCPeerConnection interface: calling setConfiguration(RTCConfiguration) on pc with too few arguments must throw TypeError]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "onnegotiationneeded" with the proper type (18)]
@@ -167,17 +167,17 @@
 
   [RTCPeerConnection interface: operation updateIce(RTCConfiguration)]
     expected: FAIL
 
   [RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidate)]
     expected: FAIL
 
   [RTCPeerConnection interface: attribute canTrickleIceCandidates]
-    expected: FAIL
+    expected: PASS
 
   [RTCPeerConnection interface: attribute onicegatheringstatechange]
     expected: FAIL
 
   [RTCPeerConnection interface: operation createOffer(RTCSessionDescriptionCallback,RTCPeerConnectionErrorCallback,RTCOfferOptions)]
     expected: FAIL
 
   [RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback)]
@@ -239,17 +239,17 @@
 
   [RTCPeerConnection interface: pc must inherit property "iceGatheringState" with the proper type (9)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "iceConnectionState" with the proper type (10)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "canTrickleIceCandidates" with the proper type (11)]
-    expected: FAIL
+    expected: PASS
 
   [RTCPeerConnection interface: pc must inherit property "onnegotiationneeded" with the proper type (14)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "onicecandidate" with the proper type (15)]
     expected: FAIL
 
   [RTCPeerConnection interface: pc must inherit property "onsignalingstatechange" with the proper type (16)]