Bug 1091898 - add RTCPeerConnection hybrid with promises. r=bz, r=mt

This commit is contained in:
Jan-Ivar Bruaroey 2014-12-08 09:36:18 -06:00
parent 47af3a98f2
commit a0290b4999
4 changed files with 102 additions and 76 deletions

View File

@ -564,13 +564,15 @@ RTCPeerConnection.prototype = {
});
},
createOffer: function(onSuccess, onError, options) {
createOffer: function(optionsOrOnSuccess, onError, options) {
// TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223).
// Note that webidl bindings make o.mandatory implicit but not o.optional.
function convertLegacyOptions(o) {
if (!(Object.keys(o.mandatory).length || o.optional) ||
Object.keys(o).length != (o.optional? 2 : 1)) {
// Detect (mandatory OR optional) AND no other top-level members.
let lcy = ((o.mandatory && Object.keys(o.mandatory).length) || o.optional) &&
Object.keys(o).length == (o.mandatory? 1 : 0) + (o.optional? 1 : 0);
if (!lcy) {
return false;
}
let old = o.mandatory || {};
@ -600,17 +602,28 @@ RTCPeerConnection.prototype = {
return true;
}
let onSuccess;
if (optionsOrOnSuccess && typeof optionsOrOnSuccess === "function") {
onSuccess = optionsOrOnSuccess;
} else {
options = optionsOrOnSuccess;
onError = undefined;
}
if (options && convertLegacyOptions(options)) {
this.logWarning(
"Mandatory/optional in createOffer options is deprecated! Use " +
JSON.stringify(options) + " instead (note the case difference)!",
null, 0);
}
this._queueOrRun({
func: this._createOffer,
args: [onSuccess, onError, options],
wait: true
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._createOffer,
args: [resolve, reject, options],
wait: true
});
});
return (onSuccess || onError)?
p.then(offer => { onSuccess(offer); }, onError) : p;
},
_createOffer: function(onSuccess, onError, options) {
@ -640,20 +653,18 @@ RTCPeerConnection.prototype = {
},
createAnswer: function(onSuccess, onError) {
this._queueOrRun({
func: this._createAnswer,
args: [onSuccess, onError],
wait: true
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._createAnswer,
args: [resolve, reject],
wait: true
});
});
return (onSuccess || onError)?
p.then(answer => { onSuccess(answer); }, onError) : p;
},
setLocalDescription: function(desc, onSuccess, onError) {
if (!onSuccess || !onError) {
this.logWarning(
"setLocalDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.",
null, 0);
}
this._localType = desc.type;
let type;
@ -671,11 +682,14 @@ RTCPeerConnection.prototype = {
"Invalid type " + desc.type + " provided to setLocalDescription");
}
this._queueOrRun({
func: this._setLocalDescription,
args: [type, desc.sdp, onSuccess, onError],
wait: true
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._setLocalDescription,
args: [type, desc.sdp, resolve, reject],
wait: true
});
});
return (onSuccess || onError)? p.then(() => { onSuccess(); }, onError) : p;
},
_setLocalDescription: function(type, sdp, onSuccess, onError) {
@ -685,11 +699,6 @@ RTCPeerConnection.prototype = {
},
setRemoteDescription: function(desc, onSuccess, onError) {
if (!onSuccess || !onError) {
this.logWarning(
"setRemoteDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.",
null, 0);
}
this._remoteType = desc.type;
let type;
@ -710,11 +719,14 @@ RTCPeerConnection.prototype = {
// Have to get caller's origin outside of Promise constructor and pass it in
let origin = Cu.getWebIDLCallerPrincipal().origin;
this._queueOrRun({
func: this._setRemoteDescription,
args: [type, desc.sdp, origin, onSuccess, onError],
wait: true
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._setRemoteDescription,
args: [type, desc.sdp, origin, resolve, reject],
wait: true
});
});
return (onSuccess || onError)? p.then(() => { onSuccess(); }, onError) : p;
},
/**
@ -829,21 +841,19 @@ RTCPeerConnection.prototype = {
},
addIceCandidate: function(cand, onSuccess, onError) {
if (!onSuccess || !onError) {
this.logWarning(
"addIceCandidate called without success/failure callbacks. This is deprecated, and will be an error in the future.",
null, 0);
}
if (!cand.candidate && !cand.sdpMLineIndex) {
throw new this._win.DOMError("InvalidParameterError",
"Invalid candidate passed to addIceCandidate!");
}
this._queueOrRun({
func: this._addIceCandidate,
args: [cand, onSuccess, onError],
wait: false
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._addIceCandidate,
args: [cand, resolve, reject],
wait: false
});
});
return (onSuccess || onError)? p.then(() => { onSuccess(); }, onError) : p;
},
_addIceCandidate: function(cand, onSuccess, onError) {
@ -1020,11 +1030,15 @@ RTCPeerConnection.prototype = {
},
getStats: function(selector, onSuccess, onError) {
this._queueOrRun({
func: this._getStats,
args: [selector, onSuccess, onError],
wait: false
let p = new this._win.Promise((resolve, reject) => {
this._queueOrRun({
func: this._getStats,
args: [selector, resolve, reject],
wait: false
});
});
return (onSuccess || onError)?
p.then(stats => { onSuccess(stats); }, onError) : p;
},
_getStats: function(selector, onSuccess, onError) {
@ -1428,13 +1442,17 @@ RTCRtpSender.prototype = {
contractID: PC_SENDER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
replaceTrack: function(withTrack, onSuccess, onError) {
replaceTrack: function(withTrack) {
this._pc._checkClosed();
this._pc._queueOrRun({
func: this._pc._replaceTrack,
args: [this, withTrack, onSuccess, onError],
wait: false
let p = new this._pc._win.Promise((resolve, reject) => {
this._pc._queueOrRun({
func: this._pc._replaceTrack,
args: [this, withTrack, resolve, reject],
wait: false
});
});
return p;
}
};

View File

@ -37,17 +37,13 @@
var track = stream.getVideoTracks()[0];
var sender = test.pcLocal._pc.getSenders().find(isSenderOfTrack, track);
ok(sender, "track has a sender");
var newtrack;
navigator.mediaDevices.getUserMedia({video:true, fake: true})
.then(function(newStream) {
var newtrack = newStream.getVideoTracks()[0];
return new Promise(function(resolve, reject) {
sender.replaceTrack(newtrack, function() {
resolve(newtrack);
}, reject);
});
newtrack = newStream.getVideoTracks()[0];
return sender.replaceTrack(newtrack);
})
.then(function(newtrack) {
ok(true, "replaceTrack success callback is called");
.then(function() {
is(sender.track, newtrack, "sender.track has been replaced");
})
.catch(function(reason) {

View File

@ -84,24 +84,15 @@ interface mozRTCPeerConnection : EventTarget {
optional DOMString username);
[Pref="media.peerconnection.identity.enabled"]
void getIdentityAssertion();
void createOffer (RTCSessionDescriptionCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback,
optional RTCOfferOptions options);
void createAnswer (RTCSessionDescriptionCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback);
void setLocalDescription (mozRTCSessionDescription description,
optional VoidFunction successCallback,
optional RTCPeerConnectionErrorCallback failureCallback);
void setRemoteDescription (mozRTCSessionDescription description,
optional VoidFunction successCallback,
optional RTCPeerConnectionErrorCallback failureCallback);
Promise<mozRTCSessionDescription> createOffer (optional RTCOfferOptions options);
Promise<mozRTCSessionDescription> createAnswer ();
Promise<void> setLocalDescription (mozRTCSessionDescription description);
Promise<void> setRemoteDescription (mozRTCSessionDescription description);
readonly attribute mozRTCSessionDescription? localDescription;
readonly attribute mozRTCSessionDescription? remoteDescription;
readonly attribute RTCSignalingState signalingState;
void updateIce (optional RTCConfiguration configuration);
void addIceCandidate (mozRTCIceCandidate candidate,
optional VoidFunction successCallback,
optional RTCPeerConnectionErrorCallback failureCallback);
Promise<void> addIceCandidate (mozRTCIceCandidate candidate);
readonly attribute RTCIceGatheringState iceGatheringState;
readonly attribute RTCIceConnectionState iceConnectionState;
[Pref="media.peerconnection.identity.enabled"]
@ -138,9 +129,7 @@ interface mozRTCPeerConnection : EventTarget {
attribute EventHandler onremovestream;
attribute EventHandler oniceconnectionstatechange;
void getStats (MediaStreamTrack? selector,
RTCStatsCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<RTCStatsReport> getStats (MediaStreamTrack? selector);
// Data channel.
RTCDataChannel createDataChannel (DOMString label,
@ -156,3 +145,28 @@ interface mozRTCPeerConnection : EventTarget {
attribute EventHandler onidpvalidationerror;
};
// Legacy callback API
partial interface mozRTCPeerConnection {
// Dummy Promise<void> return values avoid "WebIDL.WebIDLError: error:
// We have overloads with both Promise and non-Promise return types"
Promise<void> createOffer (RTCSessionDescriptionCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback,
optional RTCOfferOptions options);
Promise<void> createAnswer (RTCSessionDescriptionCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<void> setLocalDescription (mozRTCSessionDescription description,
VoidFunction successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<void> setRemoteDescription (mozRTCSessionDescription description,
VoidFunction successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<void> addIceCandidate (mozRTCIceCandidate candidate,
VoidFunction successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<void> getStats (MediaStreamTrack? selector,
RTCStatsCallback successCallback,
RTCPeerConnectionErrorCallback failureCallback);
};

View File

@ -12,7 +12,5 @@
interface RTCRtpSender {
readonly attribute MediaStreamTrack track;
void replaceTrack(MediaStreamTrack track,
VoidFunction successCallback,
RTCPeerConnectionErrorCallback failureCallback);
Promise<void> replaceTrack(MediaStreamTrack track);
};