Bug 802694: Pass along constraints from PC JS module to PCImpl; r=ekr,jesup

This commit is contained in:
Anant Narayanan 2012-10-25 12:24:30 -07:00
parent fc01bff121
commit f90c9929ee
4 changed files with 164 additions and 91 deletions

View File

@ -230,22 +230,51 @@ PeerConnection.prototype = {
}
},
/**
* Constraints look like this:
*
* {
* mandatory: {"foo": true, "bar": 10, "baz": "boo"},
* optional: [{"foo": true}, {"bar": 10}]
* }
*
* We check for basic structure but not the validity of the constraints
* themselves before passing them along to C++.
*/
_validateConstraints: function(constraints) {
function isObject(obj) {
return obj && (typeof obj === "object");
}
function isArray(obj) {
return isObject(obj) &&
(Object.prototype.toString.call(obj) === "[object Array]");
}
if (!isObject(constraints)) {
return false;
}
if (constraints.mandatory && !isObject(constraints.mandatory)) {
return false;
}
if (constraints.optional && !isArray(constraints.optional)) {
return false;
}
return true;
},
createOffer: function(onSuccess, onError, constraints) {
if (this._onCreateOfferSuccess) {
if (onError) {
onError.onCallback("createOffer already called");
}
return;
throw new Error("createOffer already called");
}
if (!this._validateConstraints(constraints)) {
throw new Error("createOffer passed invalid constraints");
}
this._onCreateOfferSuccess = onSuccess;
this._onCreateOfferFailure = onError;
// TODO: Implement constraints/hints.
if (!constraints) {
constraints = "";
}
this._queueOrRun({
func: this._pc.createOffer,
args: [constraints],
@ -255,56 +284,39 @@ PeerConnection.prototype = {
createAnswer: function(onSuccess, onError, constraints, provisional) {
if (this._onCreateAnswerSuccess) {
if (onError) {
try {
onError.onCallback("createAnswer already called");
} catch(e) {}
}
return;
throw new Error("createAnswer already called");
}
if (!this.remoteDescription) {
if (onError) {
try {
onError.onCallback("setRemoteDescription not called");
} catch(e) {}
}
throw new Error("setRemoteDescription not called");
}
if (this.remoteDescription.type != "offer") {
if (onError) {
try {
onError.onCallback("No outstanding offer");
} catch(e) {}
}
throw new Error("No outstanding offer");
}
if (!this._validateConstraints(constraints)) {
throw new Error("createAnswer passed invalid constraints");
}
this._onCreateAnswerSuccess = onSuccess;
this._onCreateAnswerFailure = onError;
if (!constraints) {
constraints = "";
}
if (!provisional) {
provisional = false;
}
// TODO: Implement provisional answer & constraints.
// TODO: Implement provisional answer.
this._queueOrRun({
func: this._pc.createAnswer,
args: ["", this.remoteDescription.sdp],
args: [constraints],
wait: true
});
},
setLocalDescription: function(desc, onSuccess, onError) {
if (this._onSetLocalDescriptionSuccess) {
if (onError) {
try {
onError.onCallback("setLocalDescription already called");
} catch(e) {}
}
return;
throw new Error("setLocalDescription already called");
}
this._onSetLocalDescriptionSuccess = onSuccess;
@ -319,14 +331,9 @@ PeerConnection.prototype = {
type = Ci.IPeerConnection.kActionAnswer;
break;
default:
if (onError) {
try {
onError.onCallback(
"Invalid type " + desc.type + " provided to setLocalDescription"
);
} catch(e) {}
return;
}
throw new Error(
"Invalid type " + desc.type + " provided to setLocalDescription"
);
break;
}
@ -339,12 +346,7 @@ PeerConnection.prototype = {
setRemoteDescription: function(desc, onSuccess, onError) {
if (this._onSetRemoteDescriptionSuccess) {
if (onError) {
try {
onError.onCallback("setRemoteDescription already called");
} catch(e) {}
}
return;
throw new Error("setRemoteDescription already called");
}
this._onSetRemoteDescriptionSuccess = onSuccess;
@ -359,14 +361,9 @@ PeerConnection.prototype = {
type = Ci.IPeerConnection.kActionAnswer;
break;
default:
if (onError) {
try {
onError.onCallback(
"Invalid type " + desc.type + " provided to setRemoteDescription"
);
} catch(e) {}
return;
}
throw new Error(
"Invalid type " + desc.type + " provided to setRemoteDescription"
);
break;
}

View File

@ -1,5 +1,6 @@
#include "nsIThread.idl"
#include "nsIDOMWindow.idl"
#include "nsIPropertyBag2.idl"
interface nsIDOMMediaStream;
interface nsIDOMDataChannel;
@ -51,7 +52,7 @@ interface IPeerConnectionObserver : nsISupports
void foundIceCandidate(in string candidate);
};
[scriptable, uuid(cb3f0048-1009-11e2-b822-87ee49eface7)]
[scriptable, uuid(f6819246-f5af-40f2-ab82-e166d5da7ba0)]
interface IPeerConnection : nsISupports
{
const unsigned long kHintAudio = 0x00000001;
@ -73,8 +74,8 @@ interface IPeerConnection : nsISupports
[optional] in nsIThread thread);
/* JSEP calls */
void createOffer(in string hints);
void createAnswer(in string hints, in string offer);
[implicit_jscontext] void createOffer(in jsval constraints);
[implicit_jscontext] void createAnswer(in jsval constraints);
void setLocalDescription(in long action, in string sdp);
void setRemoteDescription(in long action, in string sdp);

View File

@ -15,14 +15,19 @@
#include "cpr_string.h"
#include "cpr_stdlib.h"
#include "jsapi.h"
#include "nspr.h"
#include "nss.h"
#include "pk11pub.h"
#include "nsNetCID.h"
#include "nsIProperty.h"
#include "nsIPropertyBag2.h"
#include "nsIServiceManager.h"
#include "nsISimpleEnumerator.h"
#include "nsServiceManagerUtils.h"
#include "nsISocketTransportService.h"
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
@ -730,24 +735,91 @@ PeerConnectionImpl::NotifyDataChannel(mozilla::DataChannel *aChannel)
#endif
}
/*
* the Constraints UI IDL work is being done. The CreateOffer below is the one
* currently called by the signaling unit tests.
/**
* Constraints look like this:
*
* {
* "mandatory": {"foo":"hello", "bar": false, "baz": 10},
* "optional": [{"hello":"foo"}, {"baz": false}]
* }
*
* Optional constraints are ordered, and hence in an array. This function
* converts a jsval that looks like the above into a MediaConstraints object.
*/
NS_IMETHODIMP
PeerConnectionImpl::CreateOffer(const char* constraints) {
MOZ_ASSERT(constraints);
nsresult
PeerConnectionImpl::ConvertConstraints(
const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx)
{
size_t i;
jsval mandatory, optional;
JSObject& constraints = aConstraints.toObject();
// Mandatory constraints.
if (JS_GetProperty(aCx, &constraints, "mandatory", &mandatory)) {
if (JSVAL_IS_PRIMITIVE(mandatory) && mandatory.isObject() && !JSVAL_IS_NULL(mandatory)) {
JSObject* opts = JSVAL_TO_OBJECT(mandatory);
JS::AutoIdArray mandatoryOpts(aCx, JS_Enumerate(aCx, opts));
// Iterate over each property.
for (i = 0; i < mandatoryOpts.length(); i++) {
jsval option, optionName;
if (JS_GetPropertyById(aCx, opts, mandatoryOpts[i], &option)) {
if (JS_IdToValue(aCx, mandatoryOpts[i], &optionName)) {
// We only support boolean constraints for now.
if (JSVAL_IS_BOOLEAN(option)) {
JSString* optionNameString = JS_ValueToString(aCx, optionName);
NS_ConvertUTF16toUTF8 stringVal(JS_GetStringCharsZ(aCx, optionNameString));
aObj->setBooleanConstraint(stringVal.get(), JSVAL_TO_BOOLEAN(option), true);
}
}
}
}
}
}
// Optional constraints.
if (JS_GetProperty(aCx, &constraints, "optional", &optional)) {
if (JSVAL_IS_PRIMITIVE(optional) && optional.isObject() && !JSVAL_IS_NULL(optional)) {
JSObject* opts = JSVAL_TO_OBJECT(optional);
if (JS_IsArrayObject(aCx, opts)) {
uint32_t length;
if (!JS_GetArrayLength(aCx, opts, &length)) {
return NS_ERROR_FAILURE;
}
for (i = 0; i < length; i++) {
jsval val;
JS_GetElement(aCx, opts, i, &val);
if (JSVAL_IS_PRIMITIVE(val)) {
// Extract name & value and store.
// FIXME: MediaConstraints does not support optional constraints?
}
}
}
}
}
CheckIceState();
mRole = kRoleOfferer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
MediaConstraints aconstraints;
CreateOffer(aconstraints);
return NS_OK;
}
NS_IMETHODIMP
PeerConnectionImpl::CreateOffer(MediaConstraints& constraints) {
PeerConnectionImpl::CreateOffer(const JS::Value& aConstraints, JSContext* aCx)
{
CheckIceState();
mRole = kRoleOfferer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
MediaConstraints* cs = new MediaConstraints();
nsresult rv = ConvertConstraints(aConstraints, cs, aCx);
if (rv != NS_OK) {
return rv;
}
return CreateOffer(*cs);
}
// Used by unit tests and the IDL CreateOffer.
NS_IMETHODIMP
PeerConnectionImpl::CreateOffer(MediaConstraints& constraints)
{
cc_media_constraints_t* cc_constraints = nullptr;
constraints.buildArray(&cc_constraints);
@ -755,27 +827,25 @@ PeerConnectionImpl::CreateOffer(MediaConstraints& constraints) {
return NS_OK;
}
/*
* the Constraints UI IDL work is being done. The CreateAnswer below is the one
* currently called by the signaling unit tests.
*
* The aOffer parameter needs to be removed here and in the PeerConnection IDL
*/
NS_IMETHODIMP
PeerConnectionImpl::CreateAnswer(const char* constraints, const char* aOffer) {
MOZ_ASSERT(constraints);
PeerConnectionImpl::CreateAnswer(const JS::Value& aConstraints, JSContext* aCx)
{
CheckIceState();
mRole = kRoleAnswerer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
MediaConstraints aconstraints;
CreateAnswer(aconstraints);
MediaConstraints* cs = new MediaConstraints();
nsresult rv = ConvertConstraints(aConstraints, cs, aCx);
if (rv != NS_OK) {
return rv;
}
CreateAnswer(*cs);
return NS_OK;
}
NS_IMETHODIMP
PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints) {
PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints)
{
cc_media_constraints_t* cc_constraints = nullptr;
constraints.buildArray(&cc_constraints);
@ -784,7 +854,8 @@ PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints) {
}
NS_IMETHODIMP
PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
{
if (!aSDP) {
CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
return NS_ERROR_FAILURE;
@ -797,7 +868,8 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
}
NS_IMETHODIMP
PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) {
PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
{
if (!aSDP) {
CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
return NS_ERROR_FAILURE;

View File

@ -396,9 +396,12 @@ public:
nsPIDOMWindow* GetWindow() const { return mWindow; }
NS_IMETHODIMP CreateOffer(MediaConstraints& constraints);
NS_IMETHODIMP CreateAnswer(MediaConstraints& constraints);
// Validate constraints and construct a MediaConstraints object
// from a JS::Value.
nsresult ConvertConstraints(
const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx);
NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints);
NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints);
private:
PeerConnectionImpl(const PeerConnectionImpl&rhs);