Bug 917328: First, update PeerConnection's constraints to webidl. r=bz, jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Wed, 09 Oct 2013 22:27:54 -0400
changeset 165468 6417162f58a39517d679b6eeea9faf15ef5f0586
parent 165467 f613d7363bd24b8df846d58397909f6d690ad87e
child 165469 e93223d403fee9656b424826f41cbf594a3cb687
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jesup
bugs917328
milestone27.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 917328: First, update PeerConnection's constraints to webidl. r=bz, jesup
dom/media/PeerConnection.js
dom/media/tests/mochitest/test_peerConnection_bug835370.html
dom/webidl/RTCPeerConnection.webidl
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -319,85 +319,54 @@ RTCPeerConnection.prototype = {
       let len = rtcConfig.iceServers.length;
       for (let i=0; i < len; i++) {
         mustValidateServer (rtcConfig.iceServers[i], errorMsg);
       }
     }
   },
 
   /**
-   * Constraints look like this:
+   * MediaConstraints look like this:
    *
    * {
    *   mandatory: {"OfferToReceiveAudio": true, "OfferToReceiveVideo": true },
    *   optional: [{"VoiceActivityDetection": true}, {"FooBar": 10}]
    * }
    *
-   * We check for basic structure of constraints and the validity of
-   * mandatory constraints against those we support (fail if we don't).
-   * Unknown optional constraints may be of any type.
+   * WebIDL normalizes the top structure for us, but the mandatory constraints
+   * member comes in as a raw object so we can detect unknown constraints.
+   * We compare its members against ones we support, and fail if not found.
    */
   _mustValidateConstraints: function(constraints, errorMsg) {
-    function isObject(obj) {
-      return obj && (typeof obj === "object");
-    }
-    function isArraylike(obj) {
-      return isObject(obj) && ("length" in obj);
-    }
-    const SUPPORTED_CONSTRAINTS = {
-      OfferToReceiveAudio:1,
-      OfferToReceiveVideo:1,
-      MozDontOfferDataChannel:1
-    };
-    const OTHER_KNOWN_CONSTRAINTS = {
-      VoiceActivityDetection:1,
-      IceTransports:1,
-      RequestIdentity:1
-    };
-    // Parse-aid: Testing for pilot error of missing outer block avoids
-    // otherwise silent no-op since both mandatory and optional are optional
-    if (!isObject(constraints) || Array.isArray(constraints)) {
-      throw new this._win.DOMError("", errorMsg);
-    }
     if (constraints.mandatory) {
-      // Testing for pilot error of using [] on mandatory here throws nicer msg
-      // (arrays would throw in loop below regardless but with more cryptic msg)
-      if (!isObject(constraints.mandatory) || Array.isArray(constraints.mandatory)) {
-        throw new this._win.DOMError("",
-            errorMsg + " - malformed mandatory constraints");
+      let supported;
+      try {
+        // Passing the raw constraints.mandatory here validates its structure
+        supported = this._observer.getSupportedConstraints(constraints.mandatory);
+      } catch (e) {
+        throw new this._win.DOMError("", errorMsg + " - " + e.message);
       }
-      for (let constraint in constraints.mandatory) {
-        if (!(constraint in SUPPORTED_CONSTRAINTS) &&
-            constraints.mandatory.hasOwnProperty(constraint)) {
-          throw new this._win.DOMError("", errorMsg + " - " +
-              ((constraint in OTHER_KNOWN_CONSTRAINTS)? "unsupported" : "unknown") +
-              " mandatory constraint: " + constraint);
+
+      for (let constraint of Object.keys(constraints.mandatory)) {
+        if (!(constraint in supported)) {
+          throw new this._win.DOMError("",
+              errorMsg + " - unknown mandatory constraint: " + constraint);
         }
       }
     }
     if (constraints.optional) {
-      if (!isArraylike(constraints.optional)) {
-        throw new this._win.DOMError("",
-            errorMsg + " - malformed optional constraint array");
-      }
       let len = constraints.optional.length;
-      for (let i = 0; i < len; i += 1) {
-        if (!isObject(constraints.optional[i])) {
-          throw new this._win.DOMError("", errorMsg +
-              " - malformed optional constraint: " + constraints.optional[i]);
-        }
+      for (let i = 0; i < len; i++) {
         let constraints_per_entry = 0;
-        for (let constraint in constraints.optional[i]) {
-          if (constraints.optional[i].hasOwnProperty(constraint)) {
-            if (constraints_per_entry) {
-              throw new this._win.DOMError("", errorMsg +
-                  " - optional constraint must be single key/value pair");
-            }
-            constraints_per_entry += 1;
+        for (let constraint in Object.keys(constraints.optional[i])) {
+          if (constraints_per_entry) {
+            throw new this._win.DOMError("", errorMsg +
+                " - optional constraint must be single key/value pair");
           }
+          constraints_per_entry += 1;
         }
       }
     }
   },
 
   // Ideally, this should be of the form _checkState(state),
   // where the state is taken from an enumeration containing
   // the valid peer connection states defined in the WebRTC
@@ -1101,14 +1070,22 @@ PeerConnectionObserver.prototype = {
   },
 
   notifyConnection: function() {
     this.dispatchEvent(new this._dompc._win.Event("connection"));
   },
 
   notifyClosedConnection: function() {
     this.dispatchEvent(new this._dompc._win.Event("closedconnection"));
+  },
+
+  getSupportedConstraints: function(dict) {
+// TODO: Once Bug 917328 makes this a webidl object, we just return our arg
+//    return dict;
+    return { "OfferToReceiveAudio":true,
+             "OfferToReceiveVideo":true,
+             "MozDontOfferDataChannel":true };
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection]
 );
--- a/dom/media/tests/mochitest/test_peerConnection_bug835370.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug835370.html
@@ -22,36 +22,27 @@
 
     var exception = null;
     try { pconnects.createOffer(step1, failed); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed) succeeds");
     exception = null;
     try { pconnect.createOffer(step1, failed, 1); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, 1) throws");
     exception = null;
-    try { pconnect.createOffer(step1, failed, []); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, []) throws");
-    exception = null;
     try { pconnects.createOffer(step1, failed, {}); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed, {}) succeeds");
     exception = null;
-    try { pconnect.createOffer(step1, failed, { mandatory: [] }); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, { mandatory: [] }) throws");
-    exception = null;
     try {
         pconnect.createOffer(step1, failed, { mandatory: { FooBar: true } });
     } catch (e) {
         ok(e.message.indexOf("FooBar") > 0, "createOffer has readable exceptions");
         exception = e;
     }
     ok(exception, "createOffer(step1, failed, { mandatory: { FooBar: true } }) throws");
     exception = null;
-    try { pconnect.createOffer(step1, failed, { optional: {} }); } catch (e) { exception = e; }
-    ok(exception, "createOffer(step1, failed, { optional: {} }) throws");
-    exception = null;
     try { pconnects.createOffer(step1, failed, { optional: [] }); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed, { optional: [] }) succeeds");
     exception = null;
     try { pconnect.createOffer(step1, failed, { optional: [1] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [1] }) throws");
     exception = null;
     try { pconnect.createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }) throws");
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -46,50 +46,76 @@ dictionary RTCDataChannelInit {
 
   // these are deprecated due to renaming in the spec, but still supported for Fx22
   boolean outOfOrderAllowed; // now ordered, and the default changes to keep behavior the same
   unsigned short maxRetransmitNum; // now maxRetransmits
   boolean preset; // now negotiated
   unsigned short stream; // now id
 };
 
+// Misnomer dictionaries housing PeerConnection-specific constraints.
+//
+// Important! Do not ever add members that might need tracing (e.g. object)
+// to MediaConstraintSet or any dictionary marked XxxInternal here
+
+dictionary MediaConstraintSet {
+  boolean OfferToReceiveAudio;
+  boolean OfferToReceiveVideo;
+  boolean MozDontOfferDataChannel;
+};
+
+// MediaConstraint = single-property-subset of MediaConstraintSet
+// Implemented as full set. Test Object.keys(pair).length == 1
+
+// typedef MediaConstraintSet MediaConstraint; // TODO: Bug 913053
+
+dictionary MediaConstraints {
+  object mandatory; // so we can see unknown + unsupported constraints
+  sequence<MediaConstraintSet> _optional; // a.k.a. MediaConstraint
+};
+
+dictionary MediaConstraintsInternal {
+  MediaConstraintSet mandatory; // holds only supported constraints
+  sequence<MediaConstraintSet> _optional; // a.k.a. MediaConstraint
+};
+
 interface RTCDataChannel;
 
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/peerconnection;1",
  Constructor (optional RTCConfiguration configuration,
               optional object? constraints)]
 // moz-prefixed until sufficiently standardized.
 interface mozRTCPeerConnection : EventTarget  {
   void createOffer (RTCSessionDescriptionCallback successCallback,
                     RTCPeerConnectionErrorCallback? failureCallback, // for apprtc
-                    optional object? constraints);
+                    optional MediaConstraints constraints);
   void createAnswer (RTCSessionDescriptionCallback successCallback,
                      RTCPeerConnectionErrorCallback? failureCallback, // for apprtc
-                     optional object? constraints);
+                     optional MediaConstraints constraints);
   void setLocalDescription (mozRTCSessionDescription description,
                             optional VoidFunction successCallback,
                             optional RTCPeerConnectionErrorCallback failureCallback);
   void setRemoteDescription (mozRTCSessionDescription description,
                              optional VoidFunction successCallback,
                              optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute mozRTCSessionDescription? localDescription;
   readonly attribute mozRTCSessionDescription? remoteDescription;
   readonly attribute RTCSignalingState signalingState;
   void updateIce (optional RTCConfiguration configuration,
-                  optional object? constraints);
+                  optional MediaConstraints constraints);
   void addIceCandidate (mozRTCIceCandidate candidate,
                         optional VoidFunction successCallback,
                         optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute RTCIceGatheringState iceGatheringState;
   readonly attribute RTCIceConnectionState iceConnectionState;
   sequence<MediaStream> getLocalStreams ();
   sequence<MediaStream> getRemoteStreams ();
   MediaStream? getStreamById (DOMString streamId);
-  void addStream (MediaStream stream, optional object? constraints);
+  void addStream (MediaStream stream, optional MediaConstraints constraints);
   void removeStream (MediaStream stream);
   void close ();
   attribute EventHandler onnegotiationneeded;
   attribute EventHandler onicecandidate;
   attribute EventHandler onsignalingstatechange;
   attribute EventHandler onaddstream;
   attribute EventHandler onremovestream;
   attribute EventHandler oniceconnectionstatechange;