Bug 1209744 - Implement canTrickleIceCandidates attribute, r=bwc,khuey
MozReview-Commit-ID: BblTl0v4OT4
--- 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,
@@ -931,17 +935,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])
@@ -959,21 +963,50 @@ 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");
}
return this._chain(() => new this._win.Promise((resolve, reject) => {
this._onAddIceCandidateSuccess = resolve;
--- 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) {
@@ -371,16 +380,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
@@ -10,41 +10,45 @@
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_SET_REMOTE_DESCRIPTION');
+ test.chain.removeAfter('PC_REMOTE_CHECK_CAN_TRICKLE_SYNC');
test.chain.append([
- function PC_REMOTE_ROLLBACK(test) {
- // We still haven't negotiated the tracks
- test.pcRemote.expectNegotiationNeeded();
- return test.setRemoteDescription(
- test.pcRemote,
- new RTCSessionDescription({ type: "rollback" }),
- STABLE);
- },
+ function PC_REMOTE_ROLLBACK(test) {
+ // We still haven't negotiated the tracks
+ test.pcRemote.expectNegotiationNeeded();
+ return test.setRemoteDescription(
+ test.pcRemote,
+ new RTCSessionDescription({ type: "rollback" }),
+ STABLE);
+ },
- function PC_LOCAL_ROLLBACK(test) {
- // We still haven't negotiated the tracks
- test.pcLocal.expectNegotiationNeeded();
- return test.setLocalDescription(
- test.pcLocal,
- new RTCSessionDescription({ type: "rollback", sdp: ""}),
- STABLE);
- },
+ function PC_REMOTE_CHECK_CAN_TRICKLE_REVERT_SYNC(test) {
+ is(test.pcRemote._pc.canTrickleIceCandidates, null,
+ "Remote canTrickleIceCandidates is reverted to null");
+ },
- // Rolling back should shut down gathering
- function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
- return test.pcLocal.endOfTrickleIce;
- },
+ function PC_LOCAL_ROLLBACK(test) {
+ // We still haven't negotiated the tracks
+ test.pcLocal.expectNegotiationNeeded();
+ 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();
});
</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
@@ -1,12 +1,12 @@
[rtcpeerconnection-idl.html]
type: testharness
[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)]
@@ -71,17 +71,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)]
@@ -113,17 +113,17 @@
[RTCPeerConnection interface: operation createAnswer()]
expected: FAIL
[RTCPeerConnection interface: operation updateIce(RTCConfiguration)]
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)]
@@ -167,17 +167,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)]