Bug 1322274: Use destructuring in PeerConnection.js draft
authorJan-Ivar Bruaroey <jib@mozilla.com>
Sat, 19 Nov 2016 16:47:10 -0500
changeset 447678 987d4b6bcb68c49030a068c0a108bfc86cff1841
parent 447600 1a931536b39de884b9dc63caeda50bc493a0570d
child 447679 7ff2d453334145516d618d8ebaffe803bd69cd9c
push id38119
push userjbruaroey@mozilla.com
push dateWed, 07 Dec 2016 03:48:14 +0000
bugs1322274
milestone53.0a1
Bug 1322274: Use destructuring in PeerConnection.js MozReview-Commit-ID: 6qselFFnGJp
dom/media/PeerConnection.js
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -145,19 +145,19 @@ GlobalPCList.prototype = {
 
     // a plugin crashed; if it's associated with any of our PCs, fire an
     // event to the DOM window
     for (let winId in this._list) {
       broadcastPluginCrash(this._list, winId, data.pluginID, data.pluginName);
     }
   },
 
-  receiveMessage: function(message) {
-    if (message.name == "gmp-plugin-crash") {
-      this.handleGMPCrash(message.data);
+  receiveMessage: function({ name, data }) {
+    if (name == "gmp-plugin-crash") {
+      this.handleGMPCrash(data);
     }
   },
 
   observe: function(subject, topic, data) {
     let cleanupPcRef = function(pcref) {
       let pc = pcref.get();
       if (pc) {
         pc._pc.close();
@@ -187,18 +187,17 @@ GlobalPCList.prototype = {
       // return)! All socket operations must be queued to STS thread
       // before we return to here.
       // Also kill them if "Work Offline" is selected - more can be created
       // while offline, but attempts to connect them should fail.
       for (let winId in this._list) {
         cleanupWinId(this._list, winId);
       }
       this._networkdown = true;
-    }
-    else if (topic == "network:offline-status-changed") {
+    } else if (topic == "network:offline-status-changed") {
       if (data == "offline") {
         // this._list shold be empty here
         this._networkdown = true;
       } else if (data == "online") {
         this._networkdown = false;
       }
     } else if (topic == "gmp-plugin-crash") {
       if (subject instanceof Ci.nsIWritablePropertyBag2) {
@@ -460,19 +459,19 @@ RTCPeerConnection.prototype = {
     }
     return this._pc;
   },
 
   getConfiguration: function() {
     return this._config;
   },
 
-  _initCertificate: function(certificates) {
+  _initCertificate: function(certificates = []) {
     let certPromise;
-    if (certificates && certificates.length > 0) {
+    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) {
         throw new this._win.DOMException(
@@ -564,20 +563,20 @@ RTCPeerConnection.prototype = {
    *                   { url: "stun:stun.example.org", }, // deprecated version
    *                   { urls: ["turn:turn1.x.org", "turn:turn2.x.org"],
    *                     username:"jib", credential:"mypass"} ] }
    *
    * This function normalizes the structure of the input for rtcConfig.iceServers for us,
    * so we test well-formed stun/turn urls before passing along to C++.
    *   msg - Error message to detail which array-entry failed, if any.
    */
-  _mustValidateRTCConfiguration: function(rtcConfig, msg) {
+  _mustValidateRTCConfiguration: function({ iceServers }, msg) {
 
     // Normalize iceServers input
-    rtcConfig.iceServers.forEach(server => {
+    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.");
       }
     });
@@ -588,44 +587,42 @@ RTCPeerConnection.prototype = {
       try {
         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.urls) {
+    iceServers.forEach(({ urls, username, credential, credentialType }) => {
+      if (!urls) {
         throw new this._win.DOMException(msg + " - missing urls", "InvalidAccessError");
       }
-      server.urls.forEach(urlStr => {
-        let url = nicerNewURI(urlStr);
-        if (url.scheme in { turn:1, turns:1 }) {
-          if (server.username == undefined) {
-            throw new this._win.DOMException(msg + " - missing username: " + urlStr,
+      urls.map(url => nicerNewURI(url)).forEach(({ scheme, spec }) => {
+        if (scheme in { turn:1, turns:1 }) {
+          if (username == undefined) {
+            throw new this._win.DOMException(msg + " - missing username: " + spec,
                                              "InvalidAccessError");
           }
-          if (server.credential == undefined) {
-            throw new this._win.DOMException(msg + " - missing credential: " + urlStr,
+          if (credential == undefined) {
+            throw new this._win.DOMException(msg + " - missing credential: " + spec,
                                              "InvalidAccessError");
           }
-          if (server.credentialType != "password") {
+          if (credentialType != "password") {
             this.logWarning("RTCConfiguration TURN credentialType \""+
-                            server.credentialType +
+                            credentialType +
                             "\" is not yet implemented. Treating as password."+
                             " https://bugzil.la/1247616");
           }
-        }
-        else if (!(url.scheme in { stun:1, stuns:1 })) {
-          throw new this._win.DOMException(msg + " - improper scheme: " + url.scheme,
+        } else if (!(scheme in { stun:1, stuns:1 })) {
+          throw new this._win.DOMException(msg + " - improper scheme: " + scheme,
                                            "SyntaxError");
         }
-        if (url.scheme in { stuns:1, turns:1 }) {
-          this.logWarning(url.scheme.toUpperCase() + " is not yet supported.");
+        if (scheme in { stuns:1, turns:1 }) {
+          this.logWarning(scheme.toUpperCase() + " is not yet supported.");
         }
       });
     });
   },
 
   // 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
@@ -724,40 +721,40 @@ RTCPeerConnection.prototype = {
     if (typeof optionsOrOnSuccess == "function") {
       onSuccess = optionsOrOnSuccess;
     } else {
       options = optionsOrOnSuccess;
     }
     return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
       let origin = Cu.getWebIDLCallerPrincipal().origin;
       return this._chain(() => {
-        let p = Promise.all([this.getPermission(), this._certificateReady])
+        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: sdp }, this._win));
+        return p.then(sdp => 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, () => {
       let origin = Cu.getWebIDLCallerPrincipal().origin;
       return this._chain(() => {
-        let p = Promise.all([this.getPermission(), this._certificateReady])
+        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");
             }
@@ -765,77 +762,76 @@ RTCPeerConnection.prototype = {
               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: sdp }, this._win));
+        return p.then(sdp => Cu.cloneInto({ type: "answer", sdp }, this._win));
       });
     });
   },
 
-  getPermission: function() {
+  _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();
     }
-    return this._havePermission = new Promise((resolve, reject) => {
-      this._settlePermission = { allow: resolve, deny: reject };
+    return this._havePermission = new Promise((allow, deny) => {
+      this._settlePermission = { allow, deny };
       let outerId = this._win.QueryInterface(Ci.nsIInterfaceRequestor).
           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
 
       let chrome = new CreateOfferRequest(outerId, this._winID,
-                                                 this._globalPCListId, false);
+                                          this._globalPCListId, false);
       let request = this._win.CreateOfferRequest._create(this._win, chrome);
       Services.obs.notifyObservers(request, "PeerConnection:request", null);
     });
   },
 
-  setLocalDescription: function(desc, onSuccess, onError) {
-    return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
-      this._localType = desc.type;
+  _actions: {
+    offer: Ci.IPeerConnection.kActionOffer,
+    answer: Ci.IPeerConnection.kActionAnswer,
+    pranswer: Ci.IPeerConnection.kActionPRAnswer,
+    rollback: Ci.IPeerConnection.kActionRollback,
+    answer: Ci.IPeerConnection.kActionAnswer,
+  },
 
-      let type;
-      switch (desc.type) {
-        case "offer":
-          type = Ci.IPeerConnection.kActionOffer;
-          break;
-        case "answer":
-          type = Ci.IPeerConnection.kActionAnswer;
-          break;
-        case "pranswer":
-          throw new this._win.DOMException("pranswer not yet implemented",
-                                           "NotSupportedError");
-        case "rollback":
-          type = Ci.IPeerConnection.kActionRollback;
-          break;
-        default:
-          throw new this._win.DOMException(
-              "Invalid type " + desc.type + " provided to setLocalDescription",
-              "InvalidParameterError");
+  setLocalDescription: function({ type, sdp }, onSuccess, onError) {
+    return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
+      this._localType = type;
+
+      let action = this._actions[type];
+      if (action === undefined) {
+        throw new this._win.DOMException(
+            "Invalid type " + type + " provided to setLocalDescription",
+            "InvalidParameterError");
+      }
+      if (action == Ci.IPeerConnection.kActionPRAnswer) {
+        throw new this._win.DOMException("pranswer not yet implemented",
+                                         "NotSupportedError");
       }
 
-      if (desc.type !== "rollback" && !desc.sdp) {
+      if (!sdp && action != Ci.IPeerConnection.kActionRollback) {
         throw new this._win.DOMException(
             "Empty or null SDP provided to setLocalDescription",
             "InvalidParameterError");
       }
 
-      return this._chain(() => this.getPermission()
+      return this._chain(() => this._getPermission()
           .then(() => new Promise((resolve, reject) => {
         this._onSetLocalDescriptionSuccess = resolve;
         this._onSetLocalDescriptionFailure = reject;
-        this._impl.setLocalDescription(type, desc.sdp);
+        this._impl.setLocalDescription(action, sdp);
       })));
     });
   },
 
   _validateIdentity: function(sdp, origin) {
     let expectedIdentity;
 
     // Only run a single identity verification at a time.  We have to do this to
@@ -875,63 +871,54 @@ RTCPeerConnection.prototype = {
         throw e;
       });
     this._lastIdentityValidation = validation.catch(() => {});
 
     // Only wait for IdP validation if we need identity matching
     return expectedIdentity ? validation : this._win.Promise.resolve();
   },
 
-  setRemoteDescription: function(desc, onSuccess, onError) {
+  setRemoteDescription: function({ type, sdp }, onSuccess, onError) {
     return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
-      this._remoteType = desc.type;
+      this._remoteType = type;
 
-      let type;
-      switch (desc.type) {
-        case "offer":
-          type = Ci.IPeerConnection.kActionOffer;
-          break;
-        case "answer":
-          type = Ci.IPeerConnection.kActionAnswer;
-          break;
-        case "pranswer":
-          throw new this._win.DOMException("pranswer not yet implemented",
-                                           "NotSupportedError");
-        case "rollback":
-          type = Ci.IPeerConnection.kActionRollback;
-          break;
-        default:
-          throw new this._win.DOMException(
-              "Invalid type " + desc.type + " provided to setRemoteDescription",
-              "InvalidParameterError");
+      let action = this._actions[type];
+      if (action === undefined) {
+        throw new this._win.DOMException(
+            "Invalid type " + type + " provided to setRemoteDescription",
+            "InvalidParameterError");
+      }
+      if (action == Ci.IPeerConnection.kActionPRAnswer) {
+        throw new this._win.DOMException("pranswer not yet implemented",
+                                         "NotSupportedError");
       }
 
-      if (!desc.sdp && desc.type !== "rollback") {
+      if (!sdp && type != "rollback") {
         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()
+        let setRem = this._getPermission()
           .then(() => new Promise((resolve, reject) => {
             this._onSetRemoteDescriptionSuccess = resolve;
             this._onSetRemoteDescriptionFailure = reject;
-            this._impl.setRemoteDescription(type, desc.sdp);
+            this._impl.setRemoteDescription(action, sdp);
           })).then(() => { this._updateCanTrickle(); });
 
-        if (desc.type === "rollback") {
+        if (type == "rollback") {
           return setRem;
         }
 
         // Do setRemoteDescription and identity validation in parallel
-        let validId = this._validateIdentity(desc.sdp, origin);
+        let validId = this._validateIdentity(sdp, origin);
         return Promise.all([setRem, validId]).then(() => {}); // return undefined
       });
     });
   },
 
   setIdentityProvider: function(provider, protocol, username) {
     this._checkClosed();
     this._localIdp.setIdentityProvider(provider, protocol, username);
@@ -975,32 +962,32 @@ RTCPeerConnection.prototype = {
     }
 
     let sections = desc.sdp.split(/(?:\r\n?|\n)m=/);
     let topSection = sections.shift();
     this._canTrickle =
       containsTrickle(topSection) || sections.every(containsTrickle);
   },
 
-  addIceCandidate: function(c, onSuccess, onError) {
-    return this._legacyCatchAndCloseGuard(onSuccess, onError, () => {
-      if (!c) {
-        // TODO: Implement processing for end-of-candidates (bug 1318167)
-        return Promise.resolve();
-      }
-      if (c.sdpMid === null && c.sdpMLineIndex === null) {
-        throw new this._win.DOMException("Invalid candidate (both sdpMid and sdpMLineIndex are null).",
-                                         "TypeError");
+  // TODO: Implement processing for end-of-candidates (bug 1318167)
+  addIceCandidate: function(candidate, onSuccess, onError) {
+    let add = ({ 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) => {
         this._onAddIceCandidateSuccess = resolve;
         this._onAddIceCandidateError = reject;
-        this._impl.addIceCandidate(c.candidate, c.sdpMid || "", c.sdpMLineIndex);
+        this._impl.addIceCandidate(candidate, sdpMid || "", sdpMLineIndex);
       }));
-    });
+    };
+    return this._legacyCatchAndCloseGuard(onSuccess, onError,
+        () => candidate ? add(candidate) : Promise.resolve());
   },
 
   addStream: function(stream) {
     stream.getTracks().forEach(track => this.addTrack(track, stream));
   },
 
   addTrack: function(track, stream) {
     if (stream.currentTime === undefined) {
@@ -1034,65 +1021,54 @@ RTCPeerConnection.prototype = {
     return this._impl.insertDTMF(sender.__DOM_IMPL__, tones, duration, interToneGap);
   },
 
   _getDTMFToneBuffer: function(sender) {
     return this._impl.getDTMFToneBuffer(sender.__DOM_IMPL__);
   },
 
   _replaceTrack: function(sender, withTrack) {
-    // TODO: Do a (sender._stream.getTracks().indexOf(track) < 0) check
-    //       on both track args someday.
-    //
-    // The proposed API will be that both tracks must already be in the same
-    // stream. However, since our MediaStreams currently are limited to one
-    // track per type, we allow replacement with an outside track not already
-    // in the same stream.
-    //
-    // Since a track may be replaced more than once, the track being replaced
-    // may not be in the stream either, so we check neither arg right now.
-
     return new this._win.Promise((resolve, reject) => {
       this._onReplaceTrackSender = sender;
       this._onReplaceTrackWithTrack = withTrack;
       this._onReplaceTrackSuccess = resolve;
       this._onReplaceTrackFailure = reject;
       this._impl.replaceTrack(sender.track, withTrack);
     });
   },
 
-  _setParameters: function(sender, parameters) {
+  _setParameters: function({ track }, parameters) {
     if (!Services.prefs.getBoolPref("media.peerconnection.simulcast")) {
       return;
     }
     // validate parameters input
     var encodings = parameters.encodings || [];
 
-    encodings.reduce((uniqueRids, encoding) => {
-      if (encoding.scaleResolutionDownBy < 1.0) {
+    encodings.reduce((uniqueRids, { rid, scaleResolutionDownBy }) => {
+      if (scaleResolutionDownBy < 1.0) {
         throw new this._win.RangeError("scaleResolutionDownBy must be >= 1.0");
       }
-      if (!encoding.rid && encodings.length > 1) {
+      if (!rid && encodings.length > 1) {
         throw new this._win.DOMException("Missing rid", "TypeError");
       }
-      if (uniqueRids[encoding.rid]) {
+      if (uniqueRids[rid]) {
         throw new this._win.DOMException("Duplicate rid", "TypeError");
       }
-      uniqueRids[encoding.rid] = true;
+      uniqueRids[rid] = true;
       return uniqueRids;
     }, {});
 
-    this._impl.setParameters(sender.track, parameters);
+    this._impl.setParameters(track, parameters);
   },
 
-  _getParameters: function(sender) {
+  _getParameters: function({ track }) {
     if (!Services.prefs.getBoolPref("media.peerconnection.simulcast")) {
       return;
     }
-    return this._impl.getParameters(sender.track);
+    return this._impl.getParameters(track);
   },
 
   close: function() {
     if (this._closed) {
       return;
     }
     this._closed = true;
     this._inClose = true;
@@ -1126,17 +1102,16 @@ RTCPeerConnection.prototype = {
   },
 
   get localDescription() {
     this._checkClosed();
     let sdp = this._impl.localDescription;
     if (sdp.length == 0) {
       return null;
     }
-
     return new this._win.RTCSessionDescription({ type: this._localType, sdp });
   },
 
   get remoteDescription() {
     this._checkClosed();
     let sdp = this._impl.remoteDescription;
     if (sdp.length == 0) {
       return null;
@@ -1219,19 +1194,17 @@ RTCPeerConnection.prototype = {
     // Synchronous since it doesn't block.
     return this._impl.createDataChannel(label, protocol, type, ordered,
                                         maxPacketLifeTime, maxRetransmits,
                                         negotiated, id);
   }
 };
 
 // This is a separate object because we don't want to expose it to DOM.
-function PeerConnectionObserver() {
-  this._dompc = null;
-}
+function PeerConnectionObserver() {}
 PeerConnectionObserver.prototype = {
   classDescription: "PeerConnectionObserver",
   classID: PC_OBS_CID,
   contractID: PC_OBS_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIDOMGlobalPropertyInitializer]),
   init: function(win) { this._win = win; },
 
@@ -1298,28 +1271,21 @@ PeerConnectionObserver.prototype = {
   onAddIceCandidateSuccess: function() {
     this._dompc._onAddIceCandidateSuccess();
   },
 
   onAddIceCandidateError: function(code, message) {
     this._dompc._onAddIceCandidateError(this.newError(message, code));
   },
 
-  onIceCandidate: function(level, mid, candidate) {
-    if (candidate == "") {
-      this.foundIceCandidate(null);
-    } else {
-      this.foundIceCandidate(new this._dompc._win.RTCIceCandidate(
-          {
-              candidate: candidate,
-              sdpMid: mid,
-              sdpMLineIndex: level
-          }
-      ));
-    }
+  onIceCandidate: function(sdpMLineIndex, sdpMid, candidate) {
+    this.foundIceCandidate((candidate || null) &&
+                           new this._dompc._win.RTCIceCandidate({ candidate,
+                                                                  sdpMid,
+                                                                  sdpMLineIndex }));
   },
 
   onNegotiationNeeded: function() {
     this.dispatchEvent(new this._win.Event("negotiationneeded"));
   },
 
 
   // This method is primarily responsible for updating iceConnectionState.
@@ -1449,40 +1415,36 @@ PeerConnectionObserver.prototype = {
     pc._onGetStatsSuccess(webidlobj);
   },
 
   onGetStatsError: function(code, message) {
     this._dompc._onGetStatsFailure(this.newError(message, code));
   },
 
   onAddStream: function(stream) {
-    let ev = new this._dompc._win.MediaStreamEvent("addstream",
-                                                   { stream: stream });
+    let ev = new this._dompc._win.MediaStreamEvent("addstream", { stream });
     this.dispatchEvent(ev);
   },
 
   onRemoveStream: function(stream) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
-                                                             { stream: stream }));
+                                                             { stream }));
   },
 
   onAddTrack: function(track, streams) {
     let pc = this._dompc;
     let receiver = pc._win.RTCRtpReceiver._create(pc._win,
                                                   new RTCRtpReceiver(this,
                                                                      track));
     pc._receivers.push(receiver);
-    let ev = new pc._win.RTCTrackEvent("track",
-                                       { receiver: receiver,
-                                         track: track,
-                                         streams: streams });
+    let ev = new pc._win.RTCTrackEvent("track", { receiver, track, streams });
     this.dispatchEvent(ev);
 
     // Fire legacy event as well for a little bit.
-    ev = new pc._win.MediaStreamTrackEvent("addtrack", { track: track });
+    ev = new pc._win.MediaStreamTrackEvent("addtrack", { track });
     this.dispatchEvent(ev);
   },
 
   onRemoveTrack: function(track) {
     let pc = this._dompc;
     let i = pc._receivers.findIndex(receiver => receiver.track == track);
     if (i >= 0) {
       pc._receivers.splice(i, 1);
@@ -1499,31 +1461,31 @@ PeerConnectionObserver.prototype = {
 
   onReplaceTrackError: function(code, message) {
     var pc = this._dompc;
     pc._onReplaceTrackWithTrack = null;
     pc._onReplaceTrackSender = null;
     pc._onReplaceTrackFailure(this.newError(message, code));
   },
 
-  foundIceCandidate: function(cand) {
+  foundIceCandidate: function(candidate) {
     this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
-                                                                      { candidate: cand } ));
+                                                                      { candidate }));
   },
 
   notifyDataChannel: function(channel) {
     this.dispatchEvent(new this._dompc._win.RTCDataChannelEvent("datachannel",
-                                                                { channel: channel }));
+                                                                { channel }));
   },
 
   onDTMFToneChange: function(trackId, tone) {
     var pc = this._dompc;
-    var sender = pc._senders.find(sender => sender.track.id == trackId)
+    var sender = pc._senders.find(({track}) => track.id == trackId);
     sender.dtmf.dispatchEvent(new pc._win.RTCDTMFToneChangeEvent("tonechange",
-                                                                 { tone: tone }));
+                                                                 { tone }));
   }
 };
 
 function RTCPeerConnectionStatic() {
 }
 RTCPeerConnectionStatic.prototype = {
   classDescription: "RTCPeerConnectionStatic",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
@@ -1581,20 +1543,18 @@ RTCDTMFSender.prototype = {
                                                    "InvalidCharacterError");
     }
 
     this._sender._pc._insertDTMF(this._sender, tones, duration, interToneGap);
   },
 };
 
 function RTCRtpSender(pc, track, stream) {
-  this._pc = pc;
-  this.track = track;
-  this._stream = stream;
-  this.dtmf = pc._win.RTCDTMFSender._create(pc._win, new RTCDTMFSender(this));
+  let dtmf = pc._win.RTCDTMFSender._create(pc._win, new RTCDTMFSender(this));
+  Object.assign(this, { _pc: pc, track, _stream: stream, dtmf });
 }
 RTCRtpSender.prototype = {
   classDescription: "RTCRtpSender",
   classID: PC_SENDER_CID,
   contractID: PC_SENDER_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
   replaceTrack: function(withTrack) {
@@ -1608,31 +1568,27 @@ RTCRtpSender.prototype = {
   },
 
   getParameters: function() {
     return this._pc._getParameters(this);
   }
 };
 
 function RTCRtpReceiver(pc, track) {
-  this._pc = pc;
-  this.track = track;
+  Object.assign(this, { _pc: pc, track });
 }
 RTCRtpReceiver.prototype = {
   classDescription: "RTCRtpReceiver",
   classID: PC_RECEIVER_CID,
   contractID: PC_RECEIVER_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 };
 
 function CreateOfferRequest(windowID, innerWindowID, callID, isSecure) {
-  this.windowID = windowID;
-  this.innerWindowID = innerWindowID;
-  this.callID = callID;
-  this.isSecure = isSecure;
+  Object.assign(this, { windowID, innerWindowID, callID, isSecure });
 }
 CreateOfferRequest.prototype = {
   classDescription: "CreateOfferRequest",
   classID: PC_COREQUEST_CID,
   contractID: PC_COREQUEST_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 };