Bug 1115998 - Support RTCIceServer.urls (plural) array form. r=smaug, r=mt
authorJan-Ivar Bruaroey <jib@mozilla.com>
Tue, 20 Jan 2015 10:08:00 -0500
changeset 252068 a227e6b5d0be30c88cfb6f93679676bd32614524
parent 252067 f29bb4c35c4d0f775cfffb5cc62d62ebba8801c0
child 252069 6287bcc6cce328dec0f88c67ab1e426dcf8f1da0
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, mt
bugs1115998
milestone38.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 1115998 - Support RTCIceServer.urls (plural) array form. r=smaug, r=mt
CLOBBER
dom/media/PeerConnection.js
dom/media/tests/mochitest/test_peerConnection_bug825703.html
dom/webidl/RTCConfiguration.webidl
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
mobile/android/modules/TabMirror.jsm
modules/libpref/init/all.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,10 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1100184 - Lots of file moves from the /netwerk flattening and zero faith
-in the build system to properly handle them.
+Bug 1115998 - (DOMString or sequence<DOMString>) needs binding flush (Bug 1103153)
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -318,23 +318,34 @@ RTCPeerConnection.prototype = {
   classDescription: "mozRTCPeerConnection",
   classID: PC_CID,
   contractID: PC_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIDOMGlobalPropertyInitializer]),
   init: function(win) { this._win = win; },
 
   __init: function(rtcConfig) {
+    this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+
     if (!rtcConfig.iceServers ||
         !Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
       rtcConfig.iceServers =
         JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"));
     }
-    this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+    // Normalize iceServers input
+    rtcConfig.iceServers.forEach(server => {
+      if (typeof server.urls === "string") {
+        server.urls = [server.urls];
+      } else if (!server.urls && server.url) {
+        // TODO: Remove support for legacy iceServer.url eventually (Bug 1116766)
+        server.urls = [server.url];
+        this.logWarning("RTCIceServer.url is deprecated! Use urls instead.", null, 0);
+      }
+    });
     this._mustValidateRTCConfiguration(rtcConfig,
         "RTCPeerConnection constructor passed invalid RTCConfiguration");
     if (_globalPCList._networkdown || !this._win.navigator.onLine) {
       throw new this._win.DOMException(
           "Can't create RTCPeerConnections when the network is down",
           "InvalidStateError");
     }
 
@@ -427,20 +438,21 @@ RTCPeerConnection.prototype = {
         func && func(result);
       } catch (e) {
         this.logErrorAndCallOnError(e);
       }
     };
   },
 
   /**
-   * An RTCConfiguration looks like this:
+   * An RTCConfiguration may look like this:
    *
-   * { "iceServers": [ { url:"stun:stun.example.org" },
-   *                   { url:"turn:turn.example.org",
+   * { "iceServers": [ { urls: "stun:stun.example.org", },
+   *                   { url: "stun:stun.example.org", }, // deprecated version
+   *                   { urls: ["turn:turn1.x.org", "turn:turn2.x.org"],
    *                     username:"jib", credential:"mypass"} ] }
    *
    * WebIDL normalizes structure for us, so we test well-formed stun/turn urls,
    * but not validity of servers themselves, before passing along to C++.
    *
    *   msg - Error message to detail which array-entry failed, if any.
    */
   _mustValidateRTCConfiguration: function(rtcConfig, msg) {
@@ -451,37 +463,39 @@ RTCPeerConnection.prototype = {
         return ios.newURI(uriStr, null, null);
       } catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) {
         throw new this._win.DOMException(msg + " - malformed URI: " + uriStr,
                                          "SyntaxError");
       }
     };
 
     rtcConfig.iceServers.forEach(server => {
-      if (!server.url) {
-        throw new this._win.DOMException(msg + " - missing url", "InvalidAccessError");
+      if (!server.urls) {
+        throw new this._win.DOMException(msg + " - missing urls", "InvalidAccessError");
       }
-      let url = nicerNewURI(server.url);
-      if (url.scheme in { turn:1, turns:1 }) {
-        if (!server.username) {
-          throw new this._win.DOMException(msg + " - missing username: " + server.url,
-                                           "InvalidAccessError");
+      server.urls.forEach(urlStr => {
+        let url = nicerNewURI(urlStr);
+        if (url.scheme in { turn:1, turns:1 }) {
+          if (!server.username) {
+            throw new this._win.DOMException(msg + " - missing username: " + urlStr,
+                                             "InvalidAccessError");
+          }
+          if (!server.credential) {
+            throw new this._win.DOMException(msg + " - missing credential: " + urlStr,
+                                             "InvalidAccessError");
+          }
         }
-        if (!server.credential) {
-          throw new this._win.DOMException(msg + " - missing credential: " + server.url,
-                                           "InvalidAccessError");
+        else if (!(url.scheme in { stun:1, stuns:1 })) {
+          throw new this._win.DOMException(msg + " - improper scheme: " + url.scheme,
+                                           "SyntaxError");
         }
-      }
-      else if (!(url.scheme in { stun:1, stuns:1 })) {
-        throw new this._win.DOMException(msg + " - improper scheme: " + url.scheme,
-                                         "SyntaxError");
-      }
-      if (url.scheme in { stuns:1, turns:1 }) {
-        this.logWarning(url.scheme.toUpperCase() + " is not yet supported.", null, 0);
-      }
+        if (url.scheme in { stuns:1, turns:1 }) {
+          this.logWarning(url.scheme.toUpperCase() + " is not yet supported.", null, 0);
+        }
+      });
     });
   },
 
   // 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
   // spec. See Bug 831756.
   _checkClosed: function() {
--- a/dom/media/tests/mochitest/test_peerConnection_bug825703.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug825703.html
@@ -9,87 +9,75 @@
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "825703",
     title: "RTCConfiguration valid/invalid permutations"
   });
 
-  makePC = function (config, expect_success) {
-      var exception = null;
-      var pc = null;
-
-      try {
-          pc = new mozRTCPeerConnection(config);
-      } catch (e) {
-          exception = e;
-      }
-      if (pc !== null) {
-        pc.close();
-      }
-      pc = null
-
-      if (expect_success) {
-          ok(!exception, "mozRTCPeerConnection(" +
-             JSON.stringify(config) + ") succeeds");
-      } else {
-          ok(exception, "mozRTCPeerConnection(" +
-             JSON.stringify(config) + ") throws");
-      }
+  makePC = (config, expected_error) => {
+    var exception;
+    try {
+      new mozRTCPeerConnection(config).close();
+    } catch (e) {
+      exception = e;
+    }
+    is((exception? exception.name : "success"), expected_error || "success",
+       "mozRTCPeerConnection(" + JSON.stringify(config) + ")");
   }
 
   // This is a test of the iceServers parsing code + readable errors
 
   runNetworkTest(function () {
-    var pcs = null;
     var exception = null;
-    var config;
 
     try {
-      pcs = new mozRTCPeerConnection();
+      new mozRTCPeerConnection().close();
     } catch (e) {
       exception = e;
     }
     ok(!exception, "mozRTCPeerConnection() succeeds");
-    if (pcs !== null) {
-      pcs.close();
-    }
-    pcs = null;
     exception = null;
 
-    makePC(1, false);
+    makePC();
 
-    makePC({}, true);
+    makePC(1, "TypeError");
 
-    makePC({ iceServers: [] }, true);
+    makePC({});
 
-    makePC({ iceServers: [{ url:"" }] }, false);
+    makePC({ iceServers: [] });
+
+    makePC({ iceServers: [{ urls:"" }] }, "SyntaxError");
 
     makePC({ iceServers: [
-                { url:"stun:127.0.0.1" },
-                { url:"stuns:localhost", foo:"" },
-                { url:"turn:[::1]:3478", username:"p", credential:"p" },
-                { url:"turns:localhost:3478?transport=udp", username:"p", credential:"p" }
-                ]}, true);
-
-    makePC({ iceServers: [{ url:"turns:localhost:3478", username:"p" }] }, false);
-
-    makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, false);
+      { urls:"stun:127.0.0.1" },
+      { urls:"stun:localhost", foo:"" },
+      { urls: ["stun:127.0.0.1", "stun:localhost"] },
+      { urls:"stuns:localhost", foo:"" },
+      { urls:"turn:[::1]:3478", username:"p", credential:"p" },
+      { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" },
+      { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" },
+      { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" },
+      { url:"stun:localhost", foo:"" },
+      { url:"turn:localhost", username:"p", credential:"p" }
+    ]});
 
-    makePC({ iceServers: [{ url:"http:0.0.0.0" }] }, false);
+    makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError");
+
+    makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError");
+
+    makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError");
+
+    makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError");
     try {
-        pcs = new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] });
+      new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close();
     } catch (e) {
-        ok(e.message.indexOf("http") > 0,
-           "mozRTCPeerConnection() constructor has readable exceptions");
+      ok(e.message.indexOf("http") > 0,
+         "mozRTCPeerConnection() constructor has readable exceptions");
     }
-    if (pcs !== null) {
-      pcs.close();
-    }
-    pcs = null;
 
     networkTestFinished();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/RTCConfiguration.webidl
+++ b/dom/webidl/RTCConfiguration.webidl
@@ -3,17 +3,18 @@
  * 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-RTCConfiguration
  */
 
 dictionary RTCIceServer {
-    DOMString  url;
+    (DOMString or sequence<DOMString>) urls;
+    DOMString  url; //deprecated
     DOMString? credential = null;
     DOMString? username = null;
 };
 
 dictionary RTCConfiguration {
     sequence<RTCIceServer> iceServers;
     DOMString? peerIdentity = null;
 };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -463,30 +463,42 @@ PeerConnectionImpl::CreateRemoteSourceSt
  *
  * This function converts that into an internal IceConfiguration object.
  */
 nsresult
 PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc,
                                             IceConfiguration *aDst)
 {
 #ifdef MOZILLA_INTERNAL_API
-  if (!aSrc.mIceServers.WasPassed()) {
-    return NS_OK;
+  if (aSrc.mIceServers.WasPassed()) {
+    for (size_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
+      nsresult rv = AddIceServer(aSrc.mIceServers.Value()[i], aDst);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
   }
-  for (uint32_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
-    const RTCIceServer& server = aSrc.mIceServers.Value()[i];
-    NS_ENSURE_TRUE(server.mUrl.WasPassed(), NS_ERROR_UNEXPECTED);
-
+#endif
+  return NS_OK;
+}
+
+nsresult
+PeerConnectionImpl::AddIceServer(const RTCIceServer &aServer,
+                                 IceConfiguration *aDst)
+{
+#ifdef MOZILLA_INTERNAL_API
+  NS_ENSURE_STATE(aServer.mUrls.WasPassed());
+  NS_ENSURE_STATE(aServer.mUrls.Value().IsStringSequence());
+  auto &urls = aServer.mUrls.Value().GetAsStringSequence();
+  for (size_t i = 0; i < urls.Length(); i++) {
     // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than
     // nsStandardURL. To parse STUN/TURN URI's to spec
     // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3
     // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3
     // we parse out the query-string, and use ParseAuthority() on the rest
     nsRefPtr<nsIURI> url;
-    nsresult rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value());
+    nsresult rv = NS_NewURI(getter_AddRefs(url), urls[i]);
     NS_ENSURE_SUCCESS(rv, rv);
     bool isStun = false, isStuns = false, isTurn = false, isTurns = false;
     url->SchemeIs("stun", &isStun);
     url->SchemeIs("stuns", &isStuns);
     url->SchemeIs("turn", &isTurn);
     url->SchemeIs("turns", &isTurns);
     if (!(isStun || isStuns || isTurn || isTurns)) {
       return NS_ERROR_FAILURE;
@@ -537,18 +549,18 @@ PeerConnectionImpl::ConvertRTCConfigurat
       if (hostPos > 1)  /* The username was removed */
         return NS_ERROR_FAILURE;
       path.Mid(host, hostPos, hostLen);
     }
     if (port == -1)
       port = (isStuns || isTurns)? 5349 : 3478;
 
     if (isTurn || isTurns) {
-      NS_ConvertUTF16toUTF8 credential(server.mCredential);
-      NS_ConvertUTF16toUTF8 username(server.mUsername);
+      NS_ConvertUTF16toUTF8 credential(aServer.mCredential);
+      NS_ConvertUTF16toUTF8 username(aServer.mUsername);
 
       // Bug 1039655 - TURN TCP is not e10s ready
       if ((transport == kNrIceTransportTcp) &&
           (XRE_GetProcessType() != GeckoProcessType_Default)) {
         continue;
       }
 
       if (!aDst->addTurnServer(host.get(), port,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -73,16 +73,17 @@ class MediaPipeline;
 #ifdef USE_FAKE_MEDIA_STREAMS
 typedef Fake_DOMMediaStream DOMMediaStream;
 #else
 class DOMMediaStream;
 #endif
 
 namespace dom {
 struct RTCConfiguration;
+struct RTCIceServer;
 struct RTCOfferOptions;
 #ifdef USE_FAKE_MEDIA_STREAMS
 typedef Fake_MediaStreamTrack MediaStreamTrack;
 #else
 class MediaStreamTrack;
 #endif
 
 #ifdef USE_FAKE_PCOBSERVER
@@ -113,16 +114,17 @@ NS_IMETHODIMP func(__VA_ARGS__, resultty
 already_AddRefed<resulttype> func (__VA_ARGS__, rv)
 
 struct MediaStreamTable;
 
 namespace mozilla {
 
 using mozilla::dom::PeerConnectionObserver;
 using mozilla::dom::RTCConfiguration;
+using mozilla::dom::RTCIceServer;
 using mozilla::dom::RTCOfferOptions;
 using mozilla::DOMMediaStream;
 using mozilla::NrIceCtx;
 using mozilla::NrIceMediaStream;
 using mozilla::DtlsIdentity;
 using mozilla::ErrorResult;
 using mozilla::NrIceStunServer;
 using mozilla::NrIceTurnServer;
@@ -254,16 +256,18 @@ public:
   bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
 #endif
 
   static already_AddRefed<PeerConnectionImpl>
       Constructor(const mozilla::dom::GlobalObject& aGlobal, ErrorResult& rv);
   static PeerConnectionImpl* CreatePeerConnection();
   static nsresult ConvertRTCConfiguration(const RTCConfiguration& aSrc,
                                           IceConfiguration *aDst);
+  static nsresult AddIceServer(const RTCIceServer& aServer,
+                               IceConfiguration* aDst);
   already_AddRefed<DOMMediaStream> MakeMediaStream(uint32_t aHint);
 
   nsresult CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* aInfo,
                                         const std::string& aId);
 
   // DataConnection observers
   void NotifyDataChannel(already_AddRefed<mozilla::DataChannel> aChannel)
 #ifdef MOZILLA_INTERNAL_API
--- a/mobile/android/modules/TabMirror.jsm
+++ b/mobile/android/modules/TabMirror.jsm
@@ -2,17 +2,17 @@
 /* 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/. */
 "use strict";
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Messaging.jsm");
 
-const CONFIG = { iceServers: [{ "url": "stun:stun.services.mozilla.com" }] };
+const CONFIG = { iceServers: [{ "urls": ["stun:stun.services.mozilla.com"] }] };
 
 let log = Cu.import("resource://gre/modules/AndroidLog.jsm",
                     {}).AndroidLog.d.bind(null, "TabMirror");
 
 let failure = function(x) {
   log("ERROR: " + JSON.stringify(x));
 };
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -355,17 +355,17 @@ pref("media.getusermedia.aec", 1);
 pref("media.getusermedia.browser.enabled", true);
 // Desktop is typically VGA capture or more; and qm_select will not drop resolution
 // below 1/2 in each dimension (or so), so QVGA (320x200) is the lowest here usually.
 pref("media.peerconnection.video.min_bitrate", 200);
 pref("media.peerconnection.video.start_bitrate", 300);
 pref("media.peerconnection.video.max_bitrate", 2000);
 #endif
 pref("media.navigator.permission.disabled", false);
-pref("media.peerconnection.default_iceservers", "[{\"url\": \"stun:stun.services.mozilla.com\"}]");
+pref("media.peerconnection.default_iceservers", "[{\"urls\": [\"stun:stun.services.mozilla.com\"]}]");
 pref("media.peerconnection.ice.loopback", false); // Set only for testing in offline environments.
 pref("media.peerconnection.use_document_iceservers", true);
 // Do not enable identity before ensuring that the UX cannot be spoofed
 // see Bug 884573 for details
 // Do not enable identity before fixing domain comparison: see Bug 958741
 // Do not enable identity before fixing origin spoofing: see Bug 968335
 pref("media.peerconnection.identity.enabled", false);
 pref("media.peerconnection.identity.timeout", 10000);