Bug 1528323 - Part 1: Bring replaceTrack up to spec in terms of async and timing. r=jib
authorByron Campen [:bwc] <docfaraday@gmail.com>
Wed, 27 Feb 2019 16:05:29 +0000
changeset 519346 a2c753e0e87199ffbfb0f3754f9f951010051dfe
parent 519345 fd5c4700d912e9ced215e800d66c73cf7ee23325
child 519347 c998f84e769717ef32c9ef956bab4ab3645488b8
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs1528323
milestone67.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 1528323 - Part 1: Bring replaceTrack up to spec in terms of async and timing. r=jib Differential Revision: https://phabricator.services.mozilla.com/D19984
dom/media/PeerConnection.jsm
--- a/dom/media/PeerConnection.jsm
+++ b/dom/media/PeerConnection.jsm
@@ -581,21 +581,25 @@ class RTCPeerConnection {
       wrapCallback(onSucc)(await func());
     } catch (e) {
       wrapCallback(onErr)(e);
     }
   }
 
   // This implements the fairly common "Queue a task" logic
   async _queueTaskWithClosedCheck(func) {
-    return new this._win.Promise(resolve => {
+    return new this._win.Promise((resolve, reject) => {
       Services.tm.dispatchToMainThread({ run() {
-        if (!this._closed) {
-          func();
-          resolve();
+        try {
+          if (!this._closed) {
+            func();
+            resolve();
+          }
+        } catch (e) {
+          reject(e);
         }
       }});
     });
   }
 
   /**
    * An RTCConfiguration may look like this:
    *
@@ -1917,48 +1921,44 @@ class RTCRtpSender {
 
   replaceTrack(withTrack) {
     // async functions in here return a chrome promise, which is not something
     // content can use. This wraps that promise in something content can use.
     return this._pc._win.Promise.resolve(this._replaceTrack(withTrack));
   }
 
   async _replaceTrack(withTrack) {
-    this._pc._checkClosed();
-
-    if (this._transceiver.stopped) {
-      throw new this._pc._win.DOMException(
-          "Cannot call replaceTrack when transceiver is stopped",
-          "InvalidStateError");
-    }
-
+    let pc = this._pc;
     if (withTrack && (withTrack.kind != this._transceiver.getKind())) {
-      throw new this._pc._win.DOMException(
+      throw new pc._win.DOMException(
           "Cannot replaceTrack with a different kind!",
           "TypeError");
     }
 
-    // Updates the track on the MediaPipeline; this is needed whether or not
-    // we've associated this transceiver, the spec language notwithstanding.
-    // Synchronous, and will throw on failure.
-    this._pc._replaceTrackNoRenegotiation(this._transceiverImpl, withTrack);
+    pc._checkClosed();
+
+    await pc._chain(async () => {
+      if (this._transceiver.stopped) {
+        throw new pc._win.DOMException(
+            "Cannot call replaceTrack when transceiver is stopped",
+            "InvalidStateError");
+      }
 
-    let setTrack = () => {
-      this.track = withTrack;
-      this._transceiver.sync();
-    };
-
-    // Spec is a little weird here; we only queue if the transceiver was
-    // associated, otherwise we update the track synchronously.
-    if (this._transceiver.mid == null) {
-      setTrack();
-    } else {
-      // We're supposed to queue a task if the transceiver is associated
-      await this._pc._queueTaskWithClosedCheck(setTrack);
-    }
+      await pc._queueTaskWithClosedCheck(() => {
+        // Updates the track on the MediaPipeline, will throw on failure.
+        try {
+          pc._replaceTrackNoRenegotiation(this._transceiverImpl, withTrack);
+        } catch (e) {
+          throw new pc._win.DOMException("Track could not be replaced without renegotiation",
+                                         "InvalidModificationError");
+        }
+        this.track = withTrack;
+        this._transceiver.sync();
+      });
+    });
   }
 
   setParameters(parameters) {
     return this._pc._win.Promise.resolve(this._setParameters(parameters));
   }
 
   async _setParameters(parameters) {
     this._pc._checkClosed();