--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -444,17 +444,18 @@ RTCPeerConnection.prototype = {
this.logWarning("non-maplike pc.getStats access is deprecated! " +
"See http://w3c.github.io/webrtc-pc/#example for usage.") };
// Add a reference to the PeerConnection to global list (before init).
_globalPCList.addPC(this);
this._impl.initialize(this._observer, this._win, rtcConfig,
Services.tm.currentThread);
- this._initCertificate(rtcConfig.certificates);
+
+ this._certificateReady = this._initCertificate(rtcConfig.certificates);
this._initIdp();
_globalPCList.notifyLifecycleObservers(this, "initialized");
},
get _impl() {
if (!this._pc) {
throw new this._win.DOMException(
"RTCPeerConnection is gone (did you enter Offline mode?)",
@@ -462,69 +463,75 @@ RTCPeerConnection.prototype = {
}
return this._pc;
},
getConfiguration: function() {
return this._config;
},
- _initCertificate: function(certificates = []) {
- let certPromise;
- if (certificates.length > 0) {
- if (certificates.length > 1) {
- throw new this._win.DOMException(
- "RTCPeerConnection does not currently support multiple certificates",
- "NotSupportedError");
- }
- let cert = certificates.find(c => c.expires > Date.now());
- if (!cert) {
+ _initCertificate: async function(certificates = []) {
+ let certificate;
+ if (certificates.length > 1) {
+ throw new this._win.DOMException(
+ "RTCPeerConnection does not currently support multiple certificates",
+ "NotSupportedError");
+ }
+ if (certificates.length) {
+ certificate = certificates.find(c => c.expires > Date.now());
+ if (!certificate) {
throw new this._win.DOMException(
"Unable to create RTCPeerConnection with an expired certificate",
"InvalidParameterError");
}
- certPromise = Promise.resolve(cert);
- } else {
- certPromise = this._win.RTCPeerConnection.generateCertificate({
+ }
+
+ if (!certificate) {
+ certificate = await this._win.RTCPeerConnection.generateCertificate({
name: "ECDSA", namedCurve: "P-256"
});
}
- this._certificateReady = certPromise
- .then(cert => this._impl.certificate = cert);
+ this._impl.certificate = certificate;
},
- _initIdp: function() {
+ _resetPeerIdentityPromise: function() {
this._peerIdentity = new this._win.Promise((resolve, reject) => {
this._resolvePeerIdentity = resolve;
this._rejectPeerIdentity = reject;
});
+ },
+
+ _initIdp: function() {
+ this._resetPeerIdentityPromise();
this._lastIdentityValidation = this._win.Promise.resolve();
let prefName = "media.peerconnection.identity.timeout";
let idpTimeout = Services.prefs.getIntPref(prefName);
this._localIdp = new PeerConnectionIdp(this._win, idpTimeout);
this._remoteIdp = new PeerConnectionIdp(this._win, idpTimeout);
},
// Add a function to the internal operations chain.
- _chain: function(func) {
- let p = this._operationsChain.then(() => {
+ _chain: async function(func) {
+ let p = (async () => {
+ await this._operationsChain;
// Don't _checkClosed() inside the chain, because it throws, and spec
- // behavior as of this writing is to NOT reject outstanding promises on
- // close. This is what happens most of the time anyways, as the c++ code
- // stops calling us once closed, hanging the chain. However, c++ may
- // already have queued tasks on us, so if we're one of those then sit back.
- if (!this._closed) {
- return func();
+ // behavior is to NOT reject outstanding promises on close. This is what
+ // happens most of the time anyways, as the c++ code stops calling us once
+ // closed, hanging the chain. However, c++ may already have queued tasks
+ // on us, so if we're one of those then sit back.
+ if (this._closed) {
+ return;
}
- });
+ return await func();
+ })();
// don't propagate errors in the operations chain (this is a fork of p).
this._operationsChain = p.catch(() => {});
- return p;
+ return await p;
},
// This wrapper helps implement legacy callbacks in a manner that produces
// correct line-numbers in errors, provided that methods validate their inputs
// before putting themselves on the pc's operations chain.
//
// It also serves as guard against settling promises past close().
@@ -713,116 +720,122 @@ RTCPeerConnection.prototype = {
get:function() { return this.getEH(name); },
set:function(h) {
this.logWarning(name + " is deprecated! " + msg);
return this.setEH(name, h);
}
});
},
- _addIdentityAssertion: function(sdpPromise, origin) {
- if (!this._localIdp.enabled) {
- return sdpPromise;
- }
- return Promise.all([
- this._certificateReady
- .then(() => this._localIdp.getIdentityAssertion(this._impl.fingerprint,
- origin)),
- sdpPromise
- ]).then(([,sdp]) => this._localIdp.addIdentityAttribute(sdp));
- },
-
createOffer: function(optionsOrOnSuccess, onError, options) {
// This entry-point handles both new and legacy call sig. Decipher which one
let onSuccess;
if (typeof optionsOrOnSuccess == "function") {
onSuccess = optionsOrOnSuccess;
} else {
options = optionsOrOnSuccess;
}
- return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
+ return this._legacyCatchAndCloseGuard(onSuccess, onError, async () => {
let origin = Cu.getWebIDLCallerPrincipal().origin;
- return this._chain(() => {
- let p = Promise.all([this._getPermission(), this._certificateReady])
- .then(() => new Promise((resolve, reject) => {
- this._onCreateOfferSuccess = resolve;
- this._onCreateOfferFailure = reject;
- this._impl.createOffer(options);
- }));
- p = this._addIdentityAssertion(p, origin);
- return p.then(sdp => Cu.cloneInto({ type: "offer", sdp }, this._win));
+ return await this._chain(async () => {
+ let haveAssertion;
+ if (this._localIdp.enabled) {
+ haveAssertion = this._getIdentityAssertion(origin);
+ }
+ await this._getPermission();
+ await this._certificateReady;
+ let sdp = await new Promise((resolve, reject) => {
+ this._onCreateOfferSuccess = resolve;
+ this._onCreateOfferFailure = reject;
+ this._impl.createOffer(options);
+ });
+ if (haveAssertion) {
+ await haveAssertion;
+ sdp = this._localIdp.addIdentityAttribute(sdp);
+ }
+ return Cu.cloneInto({ type: "offer", sdp }, this._win);
});
});
},
createAnswer: function(optionsOrOnSuccess, onError) {
// This entry-point handles both new and legacy call sig. Decipher which one
let onSuccess, options;
if (typeof optionsOrOnSuccess == "function") {
onSuccess = optionsOrOnSuccess;
} else {
options = optionsOrOnSuccess;
}
- return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
+ return this._legacyCatchAndCloseGuard(onSuccess, onError, async () => {
let origin = Cu.getWebIDLCallerPrincipal().origin;
- return this._chain(() => {
- let p = Promise.all([this._getPermission(), this._certificateReady])
- .then(() => new Promise((resolve, reject) => {
- // We give up line-numbers in errors by doing this here, but do all
- // state-checks inside the chain, to support the legacy feature that
- // callers don't have to wait for setRemoteDescription to finish.
- if (!this.remoteDescription) {
- throw new this._win.DOMException("setRemoteDescription not called",
- "InvalidStateError");
- }
- if (this.remoteDescription.type != "offer") {
- throw new this._win.DOMException("No outstanding offer",
- "InvalidStateError");
- }
- this._onCreateAnswerSuccess = resolve;
- this._onCreateAnswerFailure = reject;
- this._impl.createAnswer();
- }));
- p = this._addIdentityAssertion(p, origin);
- return p.then(sdp => Cu.cloneInto({ type: "answer", sdp }, this._win));
+ return await this._chain(async () => {
+ // We give up line-numbers in errors by doing this here, but do all
+ // state-checks inside the chain, to support the legacy feature that
+ // callers don't have to wait for setRemoteDescription to finish.
+ if (!this.remoteDescription) {
+ throw new this._win.DOMException("setRemoteDescription not called",
+ "InvalidStateError");
+ }
+ if (this.remoteDescription.type != "offer") {
+ throw new this._win.DOMException("No outstanding offer",
+ "InvalidStateError");
+ }
+ let haveAssertion;
+ if (this._localIdp.enabled) {
+ haveAssertion = this._getIdentityAssertion(origin);
+ }
+ await this._getPermission();
+ await this._certificateReady;
+ let sdp = await new Promise((resolve, reject) => {
+ this._onCreateAnswerSuccess = resolve;
+ this._onCreateAnswerFailure = reject;
+ this._impl.createAnswer();
+ });
+ if (haveAssertion) {
+ await haveAssertion;
+ sdp = this._localIdp.addIdentityAttribute(sdp);
+ }
+ return Cu.cloneInto({ type: "answer", sdp }, this._win);
});
});
},
- _getPermission: function() {
- if (this._havePermission) {
- return this._havePermission;
- }
- if (this._isChrome ||
- AppConstants.MOZ_B2G ||
- Services.prefs.getBoolPref("media.navigator.permission.disabled")) {
- return this._havePermission = Promise.resolve();
+ _getPermission: async function() {
+ if (!this._havePermission) {
+ let privileged = this._isChrome || AppConstants.MOZ_B2G ||
+ Services.prefs.getBoolPref("media.navigator.permission.disabled");
+
+ if (privileged) {
+ this._havePermission = Promise.resolve();
+ } else {
+ this._havePermission = new Promise((resolve, reject) => {
+ this._settlePermission = { allow: resolve, deny: reject };
+ let outerId = this._win.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+
+ let chrome = new CreateOfferRequest(outerId, this._winID,
+ this._globalPCListId, false);
+ let request = this._win.CreateOfferRequest._create(this._win, chrome);
+ Services.obs.notifyObservers(request, "PeerConnection:request", null);
+ });
+ }
}
- return this._havePermission = new Promise((resolve, reject) => {
- this._settlePermission = { allow: resolve, deny: reject };
- let outerId = this._win.QueryInterface(Ci.nsIInterfaceRequestor).
- getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
-
- let chrome = new CreateOfferRequest(outerId, this._winID,
- this._globalPCListId, false);
- let request = this._win.CreateOfferRequest._create(this._win, chrome);
- Services.obs.notifyObservers(request, "PeerConnection:request", null);
- });
+ return await this._havePermission;
},
_actions: {
offer: Ci.IPeerConnection.kActionOffer,
answer: Ci.IPeerConnection.kActionAnswer,
pranswer: Ci.IPeerConnection.kActionPRAnswer,
rollback: Ci.IPeerConnection.kActionRollback,
answer: Ci.IPeerConnection.kActionAnswer,
},
setLocalDescription: function({ type, sdp }, onSuccess, onError) {
- return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
+ return this._legacyCatchAndCloseGuard(onSuccess, onError, async () => {
this._localType = type;
let action = this._actions[type];
if (action === undefined) {
throw new this._win.DOMException(
"Invalid type " + type + " provided to setLocalDescription",
"InvalidParameterError");
}
@@ -832,72 +845,74 @@ RTCPeerConnection.prototype = {
}
if (!sdp && action != Ci.IPeerConnection.kActionRollback) {
throw new this._win.DOMException(
"Empty or null SDP provided to setLocalDescription",
"InvalidParameterError");
}
- return this._chain(() => this._getPermission()
- .then(() => new Promise((resolve, reject) => {
- this._onSetLocalDescriptionSuccess = resolve;
- this._onSetLocalDescriptionFailure = reject;
- this._impl.setLocalDescription(action, sdp);
- })));
+ return await this._chain(async () => {
+ await this._getPermission();
+ await new Promise((resolve, reject) => {
+ this._onSetLocalDescriptionSuccess = resolve;
+ this._onSetLocalDescriptionFailure = reject;
+ this._impl.setLocalDescription(action, sdp);
+ });
+ });
});
},
- _validateIdentity: function(sdp, origin) {
+ _validateIdentity: async function(sdp, origin) {
let expectedIdentity;
// Only run a single identity verification at a time. We have to do this to
// avoid problems with the fact that identity validation doesn't block the
// resolution of setRemoteDescription().
- let validation = this._lastIdentityValidation
- .then(() => this._remoteIdp.verifyIdentityFromSDP(sdp, origin))
- .then(msg => {
+ let p = (async () => {
+ try {
+ await this._lastIdentityValidation;
+ let msg = await this._remoteIdp.verifyIdentityFromSDP(sdp, origin);
expectedIdentity = this._impl.peerIdentity;
// If this pc has an identity already, then the identity in sdp must match
if (expectedIdentity && (!msg || msg.identity !== expectedIdentity)) {
this.close();
throw new this._win.DOMException(
"Peer Identity mismatch, expected: " + expectedIdentity,
"IncompatibleSessionDescriptionError");
}
if (msg) {
// Set new identity and generate an event.
this._impl.peerIdentity = msg.identity;
this._resolvePeerIdentity(Cu.cloneInto({
idp: this._remoteIdp.provider,
name: msg.identity
}, this._win));
}
- })
- .catch(e => {
+ } catch(e) {
this._rejectPeerIdentity(e);
// If we don't expect a specific peer identity, failure to get a valid
// peer identity is not a terminal state, so replace the promise to
// allow another attempt.
if (!this._impl.peerIdentity) {
- this._peerIdentity = new this._win.Promise((resolve, reject) => {
- this._resolvePeerIdentity = resolve;
- this._rejectPeerIdentity = reject;
- });
+ this._resetPeerIdentityPromise();
}
throw e;
- });
- this._lastIdentityValidation = validation.catch(() => {});
+ }
+ })();
+ this._lastIdentityValidation = p.catch(() => {});
// Only wait for IdP validation if we need identity matching
- return expectedIdentity ? validation : this._win.Promise.resolve();
+ if (expectedIdentity) {
+ await p;
+ }
},
setRemoteDescription: function({ type, sdp }, onSuccess, onError) {
- return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
+ return this._legacyCatchAndCloseGuard(onSuccess, onError, async () => {
this._remoteType = type;
let action = this._actions[type];
if (action === undefined) {
throw new this._win.DOMException(
"Invalid type " + type + " provided to setRemoteDescription",
"InvalidParameterError");
}
@@ -910,48 +925,51 @@ RTCPeerConnection.prototype = {
throw new this._win.DOMException(
"Empty or null SDP provided to setRemoteDescription",
"InvalidParameterError");
}
// Get caller's origin before hitting the promise chain
let origin = Cu.getWebIDLCallerPrincipal().origin;
- return this._chain(() => {
- let setRem = this._getPermission()
- .then(() => new Promise((resolve, reject) => {
+ return await this._chain(async () => {
+ let haveSetRemote = (async () => {
+ await this._getPermission();
+ await new Promise((resolve, reject) => {
this._onSetRemoteDescriptionSuccess = resolve;
this._onSetRemoteDescriptionFailure = reject;
this._impl.setRemoteDescription(action, sdp);
- })).then(() => { this._updateCanTrickle(); });
+ });
+ this._updateCanTrickle();
+ })();
- if (action == Ci.IPeerConnection.kActionRollback) {
- return setRem;
+ if (action != Ci.IPeerConnection.kActionRollback) {
+ // Do setRemoteDescription and identity validation in parallel
+ await this._validateIdentity(sdp, origin);
}
-
- // Do setRemoteDescription and identity validation in parallel
- let validId = this._validateIdentity(sdp, origin);
- return Promise.all([setRem, validId]).then(() => {}); // return undefined
+ await haveSetRemote;
});
});
},
setIdentityProvider: function(provider, protocol, username) {
this._checkClosed();
this._localIdp.setIdentityProvider(provider, protocol, username);
},
+ _getIdentityAssertion: async function(origin) {
+ await this._certificateReady;
+ return await this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin);
+ },
+
getIdentityAssertion: function() {
this._checkClosed();
let origin = Cu.getWebIDLCallerPrincipal().origin;
- return this._chain(
- () => this._certificateReady.then(
- () => this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin)
- )
- );
+ return this._win.Promise.resolve(this._chain(() =>
+ this._getIdentityAssertion(origin)));
},
get canTrickleIceCandidates() {
return this._canTrickle;
},
_updateCanTrickle: function() {
let containsTrickle = section => {
@@ -979,23 +997,23 @@ RTCPeerConnection.prototype = {
let sections = desc.sdp.split(/(?:\r\n?|\n)m=/);
let topSection = sections.shift();
this._canTrickle =
containsTrickle(topSection) || sections.every(containsTrickle);
},
// TODO: Implement processing for end-of-candidates (bug 1318167)
addIceCandidate: function(candidate, onSuccess, onError) {
- let add = ({ candidate, sdpMid, sdpMLineIndex }) => {
+ let add = async ({ candidate, sdpMid, sdpMLineIndex }) => {
if (sdpMid === null && sdpMLineIndex === null) {
throw new this._win.DOMException(
"Invalid candidate (both sdpMid and sdpMLineIndex are null).",
"TypeError");
}
- return this._chain(() => new Promise((resolve, reject) => {
+ return await this._chain(() => new Promise((resolve, reject) => {
this._onAddIceCandidateSuccess = resolve;
this._onAddIceCandidateError = reject;
this._impl.addIceCandidate(candidate, sdpMid || "", sdpMLineIndex);
}));
};
return this._legacyCatchAndCloseGuard(onSuccess, onError,
() => candidate ? add(candidate) : Promise.resolve());
},