Bug 1492479 - Have MediaManager::GetUserMedia() return a promise. r=achronop,jya
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 30 Nov 2018 05:13:58 +0000
changeset 508134 46c936ce7efed56027d7770690f94ee31f415d7a
parent 508133 b5e141f4ca9b59ae61e778c54536bf7aadff0dfc
child 508135 a616c269bbccf381c45e6909417819103179a67a
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersachronop, jya
bugs1492479
milestone65.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 1492479 - Have MediaManager::GetUserMedia() return a promise. r=achronop,jya Differential Revision: https://phabricator.services.mozilla.com/D8008
dom/base/Navigator.cpp
dom/media/MediaDevices.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/MediaStreamTrack.cpp
dom/media/MediaStreamTrack.h
dom/media/webspeech/recognition/SpeechRecognition.cpp
dom/media/webspeech/recognition/SpeechRecognition.h
media/webrtc/signaling/src/peerconnection/RemoteTrackSource.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1200,28 +1200,48 @@ Navigator::GetMediaDevices(ErrorResult& 
 
 void
 Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
                            NavigatorUserMediaSuccessCallback& aOnSuccess,
                            NavigatorUserMediaErrorCallback& aOnError,
                            CallerType aCallerType,
                            ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (!mWindow || !mWindow->GetOuterWindow() ||
       mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   MediaManager::GetUserMediaSuccessCallback onsuccess(&aOnSuccess);
   MediaManager::GetUserMediaErrorCallback onerror(&aOnError);
 
-  MediaManager* manager = MediaManager::Get();
-  aRv = manager->GetUserMedia(mWindow, aConstraints, std::move(onsuccess),
-                              std::move(onerror), aCallerType);
+  nsWeakPtr weakWindow = nsWeakPtr(do_GetWeakReference(mWindow));
+
+  MediaManager::Get()->GetUserMedia(mWindow, aConstraints, aCallerType)->Then(
+    GetMainThreadSerialEventTarget(), __func__,
+    [weakWindow, onsuccess = std::move(onsuccess)](const RefPtr<DOMMediaStream>& aStream) {
+      nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
+      if (!window || !window->GetOuterWindow() ||
+          window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
+        return; // Leave Promise pending after navigation by design.
+      }
+      MediaManager::CallOnSuccess(&onsuccess, *aStream);
+    },
+    [weakWindow, onerror = std::move(onerror)](const RefPtr<dom::MediaStreamError>& aError) {
+      nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
+      if (!window || !window->GetOuterWindow() ||
+          window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
+        return; // Leave Promise pending after navigation by design.
+      }
+      MediaManager::CallOnError(&onerror, *aError);
+    }
+  );
 }
 
 void
 Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints,
                                   MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
                                   NavigatorUserMediaErrorCallback& aOnError,
                                   uint64_t aInnerWindowID,
                                   const nsAString& aCallID,
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -43,37 +43,16 @@ class FuzzTimerCallBack final : public n
   }
 
  private:
   nsCOMPtr<MediaDevices> mMediaDevices;
 };
 
 NS_IMPL_ISUPPORTS(FuzzTimerCallBack, nsITimerCallback, nsINamed)
 
-class MediaDevices::GumResolver : public nsIDOMGetUserMediaSuccessCallback {
- public:
-  NS_DECL_ISUPPORTS
-
-  explicit GumResolver(Promise* aPromise) : mPromise(aPromise) {}
-
-  NS_IMETHOD
-  OnSuccess(nsISupports* aStream) override {
-    RefPtr<DOMMediaStream> stream = do_QueryObject(aStream);
-    if (!stream) {
-      return NS_ERROR_FAILURE;
-    }
-    mPromise->MaybeResolve(stream);
-    return NS_OK;
-  }
-
- private:
-  virtual ~GumResolver() {}
-  RefPtr<Promise> mPromise;
-};
-
 class MediaDevices::EnumDevResolver
     : public nsIGetUserMediaDevicesSuccessCallback {
  public:
   NS_DECL_ISUPPORTS
 
   EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
       : mPromise(aPromise), mWindowId(aWindowId) {}
 
@@ -158,33 +137,43 @@ class MediaDevices::GumRejecter : public
 
 MediaDevices::~MediaDevices() {
   MediaManager* mediamanager = MediaManager::GetIfExists();
   if (mediamanager) {
     mediamanager->RemoveDeviceChangeCallback(this);
   }
 }
 
-NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
 NS_IMPL_ISUPPORTS(MediaDevices::EnumDevResolver,
                   nsIGetUserMediaDevicesSuccessCallback)
 NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
 
 already_AddRefed<Promise> MediaDevices::GetUserMedia(
     const MediaStreamConstraints& aConstraints, CallerType aCallerType,
     ErrorResult& aRv) {
   RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-
-  MediaManager::GetUserMediaSuccessCallback resolver(new GumResolver(p));
-  MediaManager::GetUserMediaErrorCallback rejecter(new GumRejecter(p));
-
-  aRv = MediaManager::Get()->GetUserMedia(GetOwner(), aConstraints,
-                                          std::move(resolver),
-                                          std::move(rejecter), aCallerType);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+  RefPtr<MediaDevices> self(this);
+  MediaManager::Get()
+      ->GetUserMedia(GetOwner(), aConstraints, aCallerType)
+      ->Then(GetCurrentThreadSerialEventTarget(), __func__,
+             [this, self, p](RefPtr<DOMMediaStream>&& aStream) {
+               if (NS_FAILED(CheckInnerWindowCorrectness())) {
+                 return;  // Leave Promise pending after navigation by design.
+               }
+               p->MaybeResolve(std::move(aStream));
+             },
+             [this, self, p](const RefPtr<MediaStreamError>& error) {
+               if (NS_FAILED(CheckInnerWindowCorrectness())) {
+                 return;  // Leave Promise pending after navigation by design.
+               }
+               p->MaybeReject(error);
+             });
   return p.forget();
 }
 
 already_AddRefed<Promise> MediaDevices::EnumerateDevices(CallerType aCallerType,
                                                          ErrorResult& aRv) {
   RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -297,28 +297,30 @@ static CaptureState CombineCaptureState(
 }
 
 static uint16_t FromCaptureState(CaptureState aState) {
   MOZ_ASSERT(aState == CaptureState::Off || aState == CaptureState::Enabled ||
              aState == CaptureState::Disabled);
   return static_cast<uint16_t>(aState);
 }
 
-static void CallOnError(MediaManager::GetUserMediaErrorCallback* aCallback,
-                        MediaStreamError& aError) {
+void MediaManager::CallOnError(
+    const MediaManager::GetUserMediaErrorCallback* aCallback,
+    MediaStreamError& aError) {
   MOZ_ASSERT(aCallback);
   if (aCallback->HasWebIDLCallback()) {
     aCallback->GetWebIDLCallback()->Call(aError);
   } else {
     aCallback->GetXPCOMCallback()->OnError(&aError);
   }
 }
 
-static void CallOnSuccess(MediaManager::GetUserMediaSuccessCallback* aCallback,
-                          DOMMediaStream& aStream) {
+void MediaManager::CallOnSuccess(
+    const MediaManager::GetUserMediaSuccessCallback* aCallback,
+    DOMMediaStream& aStream) {
   MOZ_ASSERT(aCallback);
   if (aCallback->HasWebIDLCallback()) {
     aCallback->GetWebIDLCallback()->Call(aStream);
   } else {
     aCallback->GetXPCOMCallback()->OnSuccess(&aStream);
   }
 }
 
@@ -329,20 +331,18 @@ static void CallOnSuccess(MediaManager::
  * don't hold a reference to it during late shutdown.
  *
  * There's also a hard reference to the SourceListener through its
  * SourceStreamListener and the MediaStreamGraph. MediaStreamGraph
  * clears this on XPCOM_WILL_SHUTDOWN, before MediaManager enters shutdown.
  */
 class SourceListener : public SupportsWeakPtr<SourceListener> {
  public:
-  typedef MozPromise<bool /* aIgnored */, Maybe<nsString>, true>
-      ApplyConstraintsPromise;
   typedef MozPromise<bool /* aIgnored */, RefPtr<MediaMgrError>, true>
-      InitPromise;
+      SourceListenerPromise;
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION_AND_RECORDING(
       SourceListener, recordreplay::Behavior::Preserve)
 
   SourceListener();
 
   /**
@@ -354,17 +354,17 @@ class SourceListener : public SupportsWe
    * Marks this listener as active and adds itself as a listener to aStream.
    */
   void Activate(SourceMediaStream* aStream, MediaDevice* aAudioDevice,
                 MediaDevice* aVideoDevice);
 
   /**
    * Posts a task to initialize and start all associated devices.
    */
-  RefPtr<InitPromise> InitializeAsync();
+  RefPtr<SourceListenerPromise> InitializeAsync();
 
   /**
    * Stops all live tracks, finishes the associated MediaStream and cleans up.
    */
   void Stop();
 
   /**
    * Removes this SourceListener from its associated MediaStream and marks it
@@ -446,17 +446,17 @@ class SourceListener : public SupportsWe
   bool Stopped() const { return mStopped; }
 
   bool CapturingVideo() const;
 
   bool CapturingAudio() const;
 
   CaptureState CapturingSource(MediaSourceEnum aSource) const;
 
-  RefPtr<ApplyConstraintsPromise> ApplyConstraintsToTrack(
+  RefPtr<SourceListenerPromise> ApplyConstraintsToTrack(
       nsPIDOMWindowInner* aWindow, TrackID aTrackID,
       const dom::MediaTrackConstraints& aConstraints,
       dom::CallerType aCallerType);
 
   PrincipalHandle GetPrincipalHandle() const;
 
  private:
   virtual ~SourceListener() = default;
@@ -805,58 +805,16 @@ class GetUserMediaWindowListener {
   // The task will reset this to false. MainThread only.
   bool mChromeNotificationTaskPosted;
 
   nsTArray<RefPtr<SourceListener>> mInactiveListeners;
   nsTArray<RefPtr<SourceListener>> mActiveListeners;
 };
 
 /**
- * Send an error back to content. Do this only on the main thread.
- */
-class ErrorCallbackRunnable : public Runnable {
- public:
-  ErrorCallbackRunnable(
-      const nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback>&
-          aOnFailure,
-      MediaMgrError& aError, uint64_t aWindowID)
-      : Runnable("ErrorCallbackRunnable"),
-        mOnFailure(aOnFailure),
-        mError(&aError),
-        mWindowID(aWindowID),
-        mManager(MediaManager::GetInstance()) {}
-
-  NS_IMETHOD
-  Run() override {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Only run if the window is still active.
-    if (!(mManager->IsWindowStillActive(mWindowID))) {
-      return NS_OK;
-    }
-    // This is safe since we're on main-thread, and the windowlist can only
-    // be invalidated from the main-thread (see OnNavigation)
-    if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) {
-      RefPtr<MediaStreamError> error =
-          new MediaStreamError(window->AsInner(), *mError);
-      CallOnError(mOnFailure, *error);
-    }
-    return NS_OK;
-  }
-
- private:
-  ~ErrorCallbackRunnable() override = default;
-
-  nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback> mOnFailure;
-  RefPtr<MediaMgrError> mError;
-  uint64_t mWindowID;
-  RefPtr<MediaManager> mManager;  // get ref to this when creating the runnable
-};
-
-/**
  * nsIMediaDevice implementation.
  */
 NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice)
 
 MediaDevice::MediaDevice(const RefPtr<MediaEngineSource>& aSource,
                          const nsString& aName, const nsString& aID,
                          const nsString& aRawID)
     : mSource(aSource),
@@ -1122,55 +1080,76 @@ static const MediaTrackConstraints& GetI
  * at once, we could convert everything to nsTArray<RefPtr<blah> >'s,
  * though that would complicate the constructors some.  Currently the
  * GetUserMedia spec does not allow for more than 2 streams to be obtained in
  * one call, to simplify handling of constraints.
  */
 class GetUserMediaStreamRunnable : public Runnable {
  public:
   GetUserMediaStreamRunnable(
-      const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>&
-          aOnSuccess,
-      const nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback>&
-          aOnFailure,
+      MozPromiseHolder<MediaManager::StreamPromise>&& aHolder,
       uint64_t aWindowID, GetUserMediaWindowListener* aWindowListener,
       SourceListener* aSourceListener, const ipc::PrincipalInfo& aPrincipalInfo,
       const MediaStreamConstraints& aConstraints, MediaDevice* aAudioDevice,
       MediaDevice* aVideoDevice, PeerIdentity* aPeerIdentity, bool aIsChrome)
       : Runnable("GetUserMediaStreamRunnable"),
-        mOnSuccess(aOnSuccess),
-        mOnFailure(aOnFailure),
+        mHolder(std::move(aHolder)),
         mConstraints(aConstraints),
         mAudioDevice(aAudioDevice),
         mVideoDevice(aVideoDevice),
         mWindowID(aWindowID),
         mWindowListener(aWindowListener),
         mSourceListener(aSourceListener),
         mPrincipalInfo(aPrincipalInfo),
         mPeerIdentity(aPeerIdentity),
         mManager(MediaManager::GetInstance()) {}
 
-  ~GetUserMediaStreamRunnable() {}
+  ~GetUserMediaStreamRunnable() {
+    if (!mHolder.IsEmpty()) {
+      nsGlobalWindowInner* globalWindow =
+          nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
+      nsPIDOMWindowInner* window =
+          globalWindow ? globalWindow->AsInner() : nullptr;
+
+      mHolder.Reject(MakeRefPtr<MediaStreamError>(
+                         window, MediaStreamError::Name::AbortError),
+                     __func__);
+    }
+  }
 
   class TracksCreatedListener : public MediaStreamTrackListener {
    public:
     TracksCreatedListener(
         MediaManager* aManager,
-        const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>&
-            aSuccess,
-        GetUserMediaWindowListener* aWindowListener, DOMMediaStream* aStream,
-        MediaStreamTrack* aTrack)
+        MozPromiseHolder<MediaManager::StreamPromise>&& aHolder,
+        GetUserMediaWindowListener* aWindowListener, uint64_t aWindowID,
+        DOMMediaStream* aStream, MediaStreamTrack* aTrack)
         : mWindowListener(aWindowListener),
-          mOnSuccess(aSuccess),
+          mHolder(std::move(aHolder)),
           mManager(aManager),
+          mWindowID(aWindowID),
           mGraph(aTrack->GraphImpl()),
           mStream(new nsMainThreadPtrHolder<DOMMediaStream>(
               "TracksCreatedListener::mStream", aStream)),
           mTrack(new nsMainThreadPtrHolder<MediaStreamTrack>(
               "TracksCreatedListener::mTrack", aTrack)) {}
+
+    ~TracksCreatedListener() {
+      if (!mHolder.IsEmpty()) {
+        nsGlobalWindowInner* globalWindow =
+            nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
+        nsPIDOMWindowInner* window =
+            globalWindow ? globalWindow->AsInner() : nullptr;
+
+        mHolder.Reject(MakeRefPtr<MediaStreamError>(
+                           window, MediaMgrError::Name::AbortError),
+                       __func__);
+      }
+    }
+
     void NotifyOutput(MediaStreamGraph* aGraph,
                       StreamTime aCurrentTrackTime) override {
       // It's enough to know that one of the tracks have output, as both tracks
       // are guaranteed to be created in the graph at this point.
 
       if (mDispatchedTracksCreated) {
         return;
       }
@@ -1182,17 +1161,17 @@ class GetUserMediaStreamRunnable : publi
 
             if (!mManager->IsWindowListenerStillActive(mWindowListener)) {
               return;
             }
 
             // This is safe since we're on main-thread, and the windowlist can
             // only be invalidated from the main-thread (see OnNavigation)
             LOG(("Returning success for getUserMedia()"));
-            CallOnSuccess(mOnSuccess, *mStream);
+            mHolder.Resolve(RefPtr<DOMMediaStream>(mStream), __func__);
           });
       // DispatchToMainThreadAfterStreamStateUpdate will make the runnable run
       // in stable state. But since the runnable runs JS we need to make a
       // double dispatch.
       mGraph->DispatchToMainThreadAfterStreamStateUpdate(NS_NewRunnableFunction(
           "TracksCreatedListener::NotifyOutput Stable State Notifier",
           [graph = mGraph, r = std::move(r)]() mutable {
             graph->Dispatch(r.forget());
@@ -1201,19 +1180,19 @@ class GetUserMediaStreamRunnable : publi
     void NotifyRemoved() override {
       mGraph->Dispatch(NS_NewRunnableFunction(
           "TracksCreatedListener::NotifyRemoved CycleBreaker",
           [self = RefPtr<TracksCreatedListener>(this)]() {
             self->mTrack->RemoveListener(self);
           }));
     }
     const RefPtr<GetUserMediaWindowListener> mWindowListener;
-    const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>
-        mOnSuccess;
+    MozPromiseHolder<MediaManager::StreamPromise> mHolder;
     const RefPtr<MediaManager> mManager;
+    uint64_t mWindowID;
     const RefPtr<MediaStreamGraphImpl> mGraph;
     // Keep the DOMMediaStream alive until the success callback has been called,
     // otherwise we might immediately destroy the DOMMediaStream and
     // shut down the underlying MediaStream prematurely.
     // This creates a cycle which is broken when we're destroyed, i.e., either
     // when we've called the success callback and thus removed the listener from
     // the graph, or on graph shutdown.
     nsMainThreadPtrHandle<DOMMediaStream> mStream;
@@ -1281,71 +1260,49 @@ class GetUserMediaStreamRunnable : publi
               mPeerIdentity(aPeerIdentity) {}
 
         MediaSourceEnum GetMediaSource() const override { return mSource; }
 
         const PeerIdentity* GetPeerIdentity() const override {
           return mPeerIdentity;
         }
 
-        already_AddRefed<PledgeVoid> ApplyConstraints(
-            nsPIDOMWindowInner* aWindow,
-            const MediaTrackConstraints& aConstraints,
-            dom::CallerType aCallerType) override {
-          RefPtr<PledgeVoid> p = new PledgeVoid();
+        RefPtr<MediaStreamTrackSource::ApplyConstraintsPromise>
+        ApplyConstraints(nsPIDOMWindowInner* aWindow,
+                         const MediaTrackConstraints& aConstraints,
+                         dom::CallerType aCallerType) override {
+          MOZ_ASSERT(NS_IsMainThread());
           if (sHasShutdown || !mListener) {
             // Track has been stopped, or we are in shutdown. In either case
             // there's no observable outcome, so pretend we succeeded.
-            p->Resolve(false);
-            return p.forget();
+            return MediaStreamTrackSource::ApplyConstraintsPromise::
+                CreateAndResolve(false, __func__);
           }
 
-          mListener
+          return mListener
               ->ApplyConstraintsToTrack(aWindow, mTrackID, aConstraints,
                                         aCallerType)
               ->Then(GetMainThreadSerialEventTarget(), __func__,
-                     [p]() {
-                       if (!MediaManager::Exists()) {
-                         return;
-                       }
-
-                       p->Resolve(false);
+                     [](bool) {
+                       return MediaStreamTrackSource::ApplyConstraintsPromise::
+                           CreateAndResolve(false, __func__);
                      },
-                     [p, weakWindow = nsWeakPtr(do_GetWeakReference(aWindow)),
-                      listener = mListener,
-                      trackID = mTrackID](Maybe<nsString>&& aBadConstraint) {
-                       if (!MediaManager::Exists()) {
-                         return;
-                       }
-
-                       if (!weakWindow->IsAlive()) {
-                         return;
+                     [weakWindow = nsWeakPtr(do_GetWeakReference(aWindow))](
+                         RefPtr<MediaMgrError>&& aError)
+                         -> RefPtr<
+                             MediaStreamTrackSource::ApplyConstraintsPromise> {
+                       RefPtr<MediaStreamError> error;
+                       if (weakWindow->IsAlive()) {
+                         nsCOMPtr<nsPIDOMWindowInner> window =
+                             do_QueryReferent(weakWindow);
+                         error = new MediaStreamError(window, *aError);
                        }
-
-                       if (aBadConstraint.isNothing()) {
-                         // Unexpected error during reconfig that left the
-                         // source stopped. We resolve the promise and end the
-                         // track.
-                         if (listener) {
-                           listener->StopTrack(trackID);
-                         }
-                         p->Resolve(false);
-                         return;
-                       }
-
-                       nsCOMPtr<nsPIDOMWindowInner> window =
-                           do_QueryReferent(weakWindow);
-                       auto error = MakeRefPtr<MediaStreamError>(
-                           window, MediaMgrError::Name::OverconstrainedError,
-                           NS_LITERAL_STRING(""),
-                           aBadConstraint.valueOr(nsString()));
-                       p->Reject(error);
+                       return MediaStreamTrackSource::ApplyConstraintsPromise::
+                           CreateAndReject(error, __func__);
                      });
-
-          return p.forget();
         }
 
         void GetSettings(dom::MediaTrackSettings& aOutSettings) override {
           if (mListener) {
             mListener->GetSettingsFor(mTrackID, aOutSettings);
           }
         }
 
@@ -1423,36 +1380,38 @@ class GetUserMediaStreamRunnable : publi
         domStream->AddTrackInternal(track);
       }
     }
 
     if (!domStream || !stream || sHasShutdown) {
       LOG(("Returning error for getUserMedia() - no stream"));
 
       if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) {
-        RefPtr<MediaStreamError> error = new MediaStreamError(
-            window->AsInner(), MediaStreamError::Name::AbortError,
-            sHasShutdown ? NS_LITERAL_STRING("In shutdown")
-                         : NS_LITERAL_STRING("No stream."));
-        CallOnError(mOnFailure, *error);
+        mHolder.Reject(
+            MakeRefPtr<MediaStreamError>(
+                window->AsInner(), MediaStreamError::Name::AbortError,
+                sHasShutdown ? NS_LITERAL_STRING("In shutdown")
+                             : NS_LITERAL_STRING("No stream.")),
+            __func__);
       }
       return NS_OK;
     }
 
     // Activate our source listener. We'll call Start() on the source when we
     // get a callback that the MediaStream has started consuming. The listener
     // is freed when the page is invalidated (on navigation or close).
     mWindowListener->Activate(mSourceListener, stream, mAudioDevice,
                               mVideoDevice);
 
     nsTArray<RefPtr<MediaStreamTrack>> tracks(2);
     domStream->GetTracks(tracks);
     RefPtr<MediaStreamTrack> track = tracks[0];
     auto tracksCreatedListener = MakeRefPtr<TracksCreatedListener>(
-        mManager, mOnSuccess, mWindowListener, domStream, track);
+        mManager, std::move(mHolder), mWindowListener, mWindowID, domStream,
+        track);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of domStream through the lambda to the nested chrome
     // notification lambda to ensure it's kept alive until that lambda runs or
     // is discarded.
     mSourceListener->InitializeAsync()->Then(
         GetMainThreadSerialEventTarget(), __func__,
@@ -1461,35 +1420,35 @@ class GetUserMediaStreamRunnable : publi
           LOG(
               ("GetUserMediaStreamRunnable::Run: starting success callback "
                "following InitializeAsync()"));
           // Initiating and starting devices succeeded.
           track->AddListener(tracksCreatedListener);
           windowListener->ChromeAffectingStateChanged();
           manager->SendPendingGUMRequest();
         },
-        [manager = mManager, windowID = mWindowID,
-         onFailure =
-             std::move(mOnFailure)](const RefPtr<MediaMgrError>& error) {
+        [manager = mManager, windowID = mWindowID, holder = std::move(mHolder)](
+            const RefPtr<MediaMgrError>& error) mutable {
+          MOZ_ASSERT(NS_IsMainThread());
           LOG(
               ("GetUserMediaStreamRunnable::Run: starting failure callback "
                "following InitializeAsync()"));
           // Initiating and starting devices failed.
 
           // Only run if the window is still active for our window listener.
           if (!(manager->IsWindowStillActive(windowID))) {
             return;
           }
           // This is safe since we're on main-thread, and the windowlist can
           // only be invalidated from the main-thread (see OnNavigation)
           if (auto* window =
                   nsGlobalWindowInner::GetInnerWindowWithId(windowID)) {
-            auto streamError =
-                MakeRefPtr<MediaStreamError>(window->AsInner(), *error);
-            CallOnError(onFailure, *streamError);
+            holder.Reject(
+                MakeRefPtr<MediaStreamError>(window->AsInner(), *error),
+                __func__);
           }
         });
 
     if (!IsPincipalInfoPrivate(mPrincipalInfo)) {
       // Call GetPrincipalKey again, this time w/persist = true, to promote
       // deviceIds to persistent, in case they're not already. Fire'n'forget.
       media::GetPrincipalKey(mPrincipalInfo, true)
           ->Then(GetCurrentThreadSerialEventTarget(), __func__,
@@ -1500,18 +1459,17 @@ class GetUserMediaStreamRunnable : publi
                           "will be broken"));
                    }
                  });
     }
     return NS_OK;
   }
 
  private:
-  nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback> mOnSuccess;
-  nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback> mOnFailure;
+  MozPromiseHolder<MediaManager::StreamPromise> mHolder;
   MediaStreamConstraints mConstraints;
   RefPtr<MediaDevice> mAudioDevice;
   RefPtr<MediaDevice> mVideoDevice;
   uint64_t mWindowID;
   RefPtr<GetUserMediaWindowListener> mWindowListener;
   RefPtr<SourceListener> mSourceListener;
   ipc::PrincipalInfo mPrincipalInfo;
   RefPtr<PeerIdentity> mPeerIdentity;
@@ -1561,17 +1519,17 @@ static void GetMediaDevices(MediaEngine*
 
 // TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
 // error: 'this' was not captured for this lambda function
 
 static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
 static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
 
 RefPtr<MediaManager::BadConstraintsPromise> MediaManager::SelectSettings(
-    MediaStreamConstraints& aConstraints, bool aIsChrome,
+    const MediaStreamConstraints& aConstraints, bool aIsChrome,
     const RefPtr<MediaDeviceSetRefCnt>& aSources) {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Algorithm accesses device capabilities code and must run on media thread.
   // Modifies passed-in aSources.
 
   return MediaManager::PostTask<BadConstraintsPromise>(
       __func__, [aConstraints, aSources, aIsChrome](
@@ -1628,63 +1586,72 @@ RefPtr<MediaManager::BadConstraintsPromi
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia is called, and the results are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
 class GetUserMediaTask : public Runnable {
  public:
-  GetUserMediaTask(
-      const MediaStreamConstraints& aConstraints,
-      const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>&
-          aOnSuccess,
-      const nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback>&
-          aOnFailure,
-      uint64_t aWindowID, GetUserMediaWindowListener* aWindowListener,
-      SourceListener* aSourceListener, MediaEnginePrefs& aPrefs,
-      const ipc::PrincipalInfo& aPrincipalInfo, bool aIsChrome,
-      MediaManager::MediaDeviceSet* aMediaDeviceSet, bool aShouldFocusSource)
+  GetUserMediaTask(const MediaStreamConstraints& aConstraints,
+                   MozPromiseHolder<MediaManager::StreamPromise>&& aHolder,
+                   uint64_t aWindowID,
+                   GetUserMediaWindowListener* aWindowListener,
+                   SourceListener* aSourceListener,
+                   const MediaEnginePrefs& aPrefs,
+                   const ipc::PrincipalInfo& aPrincipalInfo, bool aIsChrome,
+                   MediaManager::MediaDeviceSet* aMediaDeviceSet,
+                   bool aShouldFocusSource)
       : Runnable("GetUserMediaTask"),
         mConstraints(aConstraints),
-        mOnSuccess(aOnSuccess),
-        mOnFailure(aOnFailure),
+        mHolder(std::move(aHolder)),
         mWindowID(aWindowID),
         mWindowListener(aWindowListener),
         mSourceListener(aSourceListener),
         mPrefs(aPrefs),
         mPrincipalInfo(aPrincipalInfo),
         mIsChrome(aIsChrome),
         mShouldFocusSource(aShouldFocusSource),
         mDeviceChosen(false),
         mMediaDeviceSet(aMediaDeviceSet),
         mManager(MediaManager::GetInstance()) {}
 
-  ~GetUserMediaTask() {}
-
-  void Fail(MediaMgrError::Name aName,
-            const nsAString& aMessage = EmptyString(),
-            const nsAString& aConstraint = EmptyString()) {
-    RefPtr<MediaMgrError> error =
-        new MediaMgrError(aName, aMessage, aConstraint);
-    auto errorRunnable =
-        MakeRefPtr<ErrorCallbackRunnable>(mOnFailure, *error, mWindowID);
-
-    NS_DispatchToMainThread(errorRunnable.forget());
-    // Do after ErrorCallbackRunnable Run()s, as it checks active window list
+  ~GetUserMediaTask() {
+    if (!mHolder.IsEmpty()) {
+      Fail(MediaStreamError::Name::NotAllowedError);
+    }
+  }
+
+  void Fail(MediaMgrError::Name aName, const nsString& aMessage = EmptyString(),
+            const nsString& aConstraint = EmptyString()) {
+    NS_DispatchToMainThread(NS_NewRunnableFunction(
+        "GetUserMediaTask::Fail",
+        [aName, aMessage, aConstraint, windowId = mWindowID,
+         windowListener = mWindowListener,
+         holder = std::move(mHolder)]() mutable {
+          auto* globalWindow =
+              nsGlobalWindowInner::GetInnerWindowWithId(windowId);
+          RefPtr<nsPIDOMWindowInner> window =
+              globalWindow ? globalWindow->AsInner() : nullptr;
+          RefPtr<MediaStreamError> error;
+          if (window) {
+            error = new MediaStreamError(window, aName, aMessage, aConstraint);
+          }
+          holder.Reject(error, __func__);
+          return;
+        }));
+    // Do after the above runs, as it checks active window list
     NS_DispatchToMainThread(NewRunnableMethod<RefPtr<SourceListener>>(
         "GetUserMediaWindowListener::Remove", mWindowListener,
         &GetUserMediaWindowListener::Remove, mSourceListener));
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(mOnSuccess);
-    MOZ_ASSERT(mOnFailure);
     MOZ_ASSERT(mDeviceChosen);
     LOG(("GetUserMediaTask::Run()"));
 
     // Allocate a video or audio device and return a MediaStream via
     // a GetUserMediaStreamRunnable.
 
     nsresult rv;
     const char* errorMsg = nullptr;
@@ -1751,34 +1718,31 @@ class GetUserMediaTask : public Runnable
       return NS_OK;
     }
     PeerIdentity* peerIdentity = nullptr;
     if (!mConstraints.mPeerIdentity.IsEmpty()) {
       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
     }
 
     NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable(
-        mOnSuccess, mOnFailure, mWindowID, mWindowListener, mSourceListener,
+        std::move(mHolder), mWindowID, mWindowListener, mSourceListener,
         mPrincipalInfo, mConstraints, mAudioDevice, mVideoDevice, peerIdentity,
         mIsChrome)));
     return NS_OK;
   }
 
   nsresult Denied(MediaMgrError::Name aName,
-                  const nsAString& aMessage = EmptyString()) {
-    MOZ_ASSERT(mOnSuccess);
-    MOZ_ASSERT(mOnFailure);
-
+                  const nsString& aMessage = EmptyString()) {
     // We add a disabled listener to the StreamListeners array until accepted
     // If this was the only active MediaStream, remove the window from the list.
     if (NS_IsMainThread()) {
       if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) {
-        RefPtr<MediaStreamError> error =
-            new MediaStreamError(window->AsInner(), aName, aMessage);
-        CallOnError(mOnFailure, *error);
+        mHolder.Reject(
+            MakeRefPtr<MediaStreamError>(window->AsInner(), aName, aMessage),
+            __func__);
       }
       // Should happen *after* error runs for consistency, but may not matter
       mWindowListener->Remove(mSourceListener);
     } else {
       // This will re-check the window being alive on main-thread
       Fail(aName, aMessage);
     }
 
@@ -1804,24 +1768,23 @@ class GetUserMediaTask : public Runnable
     return NS_OK;
   }
 
   uint64_t GetWindowID() { return mWindowID; }
 
  private:
   MediaStreamConstraints mConstraints;
 
-  nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback> mOnSuccess;
-  nsMainThreadPtrHandle<MediaManager::GetUserMediaErrorCallback> mOnFailure;
+  MozPromiseHolder<MediaManager::StreamPromise> mHolder;
   uint64_t mWindowID;
   RefPtr<GetUserMediaWindowListener> mWindowListener;
   RefPtr<SourceListener> mSourceListener;
   RefPtr<MediaDevice> mAudioDevice;
   RefPtr<MediaDevice> mVideoDevice;
-  MediaEnginePrefs mPrefs;
+  const MediaEnginePrefs mPrefs;
   ipc::PrincipalInfo mPrincipalInfo;
   bool mIsChrome;
   bool mShouldFocusSource;
 
   bool mDeviceChosen;
 
  public:
   nsAutoPtr<MediaManager::MediaDeviceSet> mMediaDeviceSet;
@@ -2415,66 +2378,60 @@ static void ReduceConstraint(
   aConstraint.SetAsMediaTrackConstraints().mMediaSource = mediaSource;
 }
 
 /**
  * The entry point for this file. A call from Navigator::mozGetUserMedia
  * will end up here. MediaManager is a singleton that is responsible
  * for handling all incoming getUserMedia calls from every window.
  */
-nsresult MediaManager::GetUserMedia(
+RefPtr<MediaManager::StreamPromise> MediaManager::GetUserMedia(
     nsPIDOMWindowInner* aWindow,
     const MediaStreamConstraints& aConstraintsPassedIn,
-    GetUserMediaSuccessCallback&& aOnSuccess,
-    GetUserMediaErrorCallback&& aOnFailure, dom::CallerType aCallerType) {
+    dom::CallerType aCallerType) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(aOnFailure.GetISupports());
-  MOZ_ASSERT(aOnSuccess.GetISupports());
-  nsMainThreadPtrHandle<GetUserMediaSuccessCallback> onSuccess(
-      new nsMainThreadPtrHolder<GetUserMediaSuccessCallback>(
-          "GetUserMedia::SuccessCallback", std::move(aOnSuccess)));
-  nsMainThreadPtrHandle<GetUserMediaErrorCallback> onFailure(
-      new nsMainThreadPtrHolder<GetUserMediaErrorCallback>(
-          "GetUserMedia::FailureCallback", std::move(aOnFailure)));
   uint64_t windowID = aWindow->WindowID();
 
   MediaStreamConstraints c(aConstraintsPassedIn);  // use a modifiable copy
 
   // Do all the validation we can while we're sync (to return an
   // already-rejected promise on failure).
 
   if (!IsOn(c.mVideo) && !IsOn(c.mAudio)) {
-    RefPtr<MediaStreamError> error = new MediaStreamError(
-        aWindow, MediaStreamError::Name::TypeError,
-        NS_LITERAL_STRING("audio and/or video is required"));
-    CallOnError(onFailure, *error);
-    return NS_OK;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(
+            aWindow, MediaStreamError::Name::TypeError,
+            NS_LITERAL_STRING("audio and/or video is required")),
+        __func__);
   }
 
   if (!IsFullyActive(aWindow)) {
-    RefPtr<MediaStreamError> error = new MediaStreamError(
-        aWindow, MediaStreamError::Name::InvalidStateError);
-    CallOnError(onFailure, *error);
-    return NS_OK;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::InvalidStateError),
+        __func__);
   }
 
   if (sHasShutdown) {
-    RefPtr<MediaStreamError> error =
-        new MediaStreamError(aWindow, MediaStreamError::Name::AbortError,
-                             NS_LITERAL_STRING("In shutdown"));
-    CallOnError(onFailure, *error);
-    return NS_OK;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::AbortError,
+                                     NS_LITERAL_STRING("In shutdown")),
+        __func__);
   }
 
   // Determine permissions early (while we still have a stack).
 
   nsIURI* docURI = aWindow->GetDocumentURI();
   if (!docURI) {
-    return NS_ERROR_UNEXPECTED;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::AbortError),
+        __func__);
   }
   bool isChrome = (aCallerType == dom::CallerType::System);
   bool privileged =
       isChrome ||
       Preferences::GetBool("media.navigator.permission.disabled", false);
   bool isHTTPS = false;
   bool isHandlingUserInput = EventStateManager::IsHandlingUserInput();
   ;
@@ -2513,30 +2470,39 @@ nsresult MediaManager::GetUserMedia(
   } else {
     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
                           (uint32_t)GetUserMediaSecurityState::Other);
   }
 
   nsCOMPtr<nsIPrincipal> principal =
       nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
   if (NS_WARN_IF(!principal)) {
-    return NS_ERROR_FAILURE;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::SecurityError),
+        __func__);
   }
 
   nsIDocument* doc = aWindow->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
-    return NS_ERROR_FAILURE;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::SecurityError),
+        __func__);
   }
 
   // This principal needs to be sent to different threads and so via IPC.
   // For this reason it's better to convert it to PrincipalInfo right now.
   ipc::PrincipalInfo principalInfo;
   rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+    return StreamPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::SecurityError),
+        __func__);
   }
 
   const bool resistFingerprinting =
       nsContentUtils::ResistFingerprinting(aCallerType);
 
   if (resistFingerprinting) {
     ReduceConstraint(c.mVideo);
     ReduceConstraint(c.mAudio);
@@ -2574,31 +2540,31 @@ nsresult MediaManager::GetUserMedia(
         // Deny screensharing request if support is disabled, or
         // the requesting document is not from a host on the whitelist.
         if (!Preferences::GetBool(
                 ((videoType == MediaSourceEnum::Browser)
                      ? "media.getusermedia.browser.enabled"
                      : "media.getusermedia.screensharing.enabled"),
                 false) ||
             (!privileged && !aWindow->IsSecureContext())) {
-          RefPtr<MediaStreamError> error = new MediaStreamError(
-              aWindow, MediaStreamError::Name::NotAllowedError);
-          CallOnError(onFailure, *error);
-          return NS_OK;
+          return StreamPromise::CreateAndReject(
+              MakeRefPtr<MediaStreamError>(
+                  aWindow, MediaStreamError::Name::NotAllowedError),
+              __func__);
         }
         break;
 
       case MediaSourceEnum::Microphone:
       case MediaSourceEnum::Other:
       default: {
-        RefPtr<MediaStreamError> error = new MediaStreamError(
-            aWindow, MediaStreamError::Name::OverconstrainedError,
-            NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource"));
-        CallOnError(onFailure, *error);
-        return NS_OK;
+        return StreamPromise::CreateAndReject(
+            MakeRefPtr<MediaStreamError>(
+                aWindow, MediaStreamError::Name::OverconstrainedError,
+                NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource")),
+            __func__);
       }
     }
 
     if (vc.mAdvanced.WasPassed() && videoType != MediaSourceEnum::Camera) {
       // iterate through advanced, forcing all unset mediaSources to match
       // "root"
       const char* unset = EnumToASCII(dom::MediaSourceEnumValues::strings,
                                       MediaSourceEnum::Camera);
@@ -2644,30 +2610,30 @@ nsresult MediaManager::GetUserMedia(
     switch (audioType) {
       case MediaSourceEnum::Microphone:
         break;
 
       case MediaSourceEnum::AudioCapture:
         // Only enable AudioCapture if the pref is enabled. If it's not, we can
         // deny right away.
         if (!Preferences::GetBool("media.getusermedia.audiocapture.enabled")) {
-          RefPtr<MediaStreamError> error = new MediaStreamError(
-              aWindow, MediaStreamError::Name::NotAllowedError);
-          CallOnError(onFailure, *error);
-          return NS_OK;
+          return StreamPromise::CreateAndReject(
+              MakeRefPtr<MediaStreamError>(
+                  aWindow, MediaStreamError::Name::NotAllowedError),
+              __func__);
         }
         break;
 
       case MediaSourceEnum::Other:
       default: {
-        RefPtr<MediaStreamError> error = new MediaStreamError(
-            aWindow, MediaStreamError::Name::OverconstrainedError,
-            NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource"));
-        CallOnError(onFailure, *error);
-        return NS_OK;
+        return StreamPromise::CreateAndReject(
+            MakeRefPtr<MediaStreamError>(
+                aWindow, MediaStreamError::Name::OverconstrainedError,
+                NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource")),
+            __func__);
       }
     }
     if (ac.mAdvanced.WasPassed()) {
       // iterate through advanced, forcing all unset mediaSources to match
       // "root"
       const char* unset = EnumToASCII(dom::MediaSourceEnumValues::strings,
                                       MediaSourceEnum::Camera);
       for (MediaTrackConstraintSet& cs : ac.mAdvanced.Value()) {
@@ -2697,74 +2663,74 @@ nsresult MediaManager::GetUserMedia(
 
   RefPtr<SourceListener> sourceListener = new SourceListener();
   windowListener->Register(sourceListener);
 
   if (!privileged) {
     // Check if this site has had persistent permissions denied.
     nsCOMPtr<nsIPermissionManager> permManager =
         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 
     uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
     if (IsOn(c.mAudio)) {
       if (audioType == MediaSourceEnum::Microphone) {
         if (Preferences::GetBool("media.getusermedia.microphone.deny", false) ||
             !dom::FeaturePolicyUtils::IsFeatureAllowed(
                 doc, NS_LITERAL_STRING("microphone"))) {
           audioPerm = nsIPermissionManager::DENY_ACTION;
         } else {
           rv = permManager->TestExactPermissionFromPrincipal(
               principal, "microphone", &audioPerm);
-          NS_ENSURE_SUCCESS(rv, rv);
+          MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
         }
       } else {
         rv = permManager->TestExactPermissionFromPrincipal(principal, "screen",
                                                            &audioPerm);
-        NS_ENSURE_SUCCESS(rv, rv);
+        MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
       }
     }
 
     uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
     if (IsOn(c.mVideo)) {
       if (videoType == MediaSourceEnum::Camera) {
         if (Preferences::GetBool("media.getusermedia.camera.deny", false) ||
             !dom::FeaturePolicyUtils::IsFeatureAllowed(
                 doc, NS_LITERAL_STRING("camera"))) {
           videoPerm = nsIPermissionManager::DENY_ACTION;
         } else {
           rv = permManager->TestExactPermissionFromPrincipal(
               principal, "camera", &videoPerm);
-          NS_ENSURE_SUCCESS(rv, rv);
+          MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
         }
       } else {
         rv = permManager->TestExactPermissionFromPrincipal(principal, "screen",
                                                            &videoPerm);
-        NS_ENSURE_SUCCESS(rv, rv);
+        MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
       }
     }
 
     if ((!IsOn(c.mAudio) && !IsOn(c.mVideo)) ||
         (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) ||
         (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) {
-      RefPtr<MediaStreamError> error = new MediaStreamError(
-          aWindow, MediaStreamError::Name::NotAllowedError);
-      CallOnError(onFailure, *error);
       windowListener->Remove(sourceListener);
-      return NS_OK;
+      return StreamPromise::CreateAndReject(
+          MakeRefPtr<MediaStreamError>(aWindow,
+                                       MediaStreamError::Name::NotAllowedError),
+          __func__);
     }
   }
 
   // Get list of all devices, with origin-specific device ids.
 
   MediaEnginePrefs prefs = mPrefs;
 
   nsString callID;
   rv = GenerateUUID(callID);
-  NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 
   bool hasVideo = videoType != MediaSourceEnum::Other;
   bool hasAudio = audioType != MediaSourceEnum::Other;
   DeviceEnumerationType videoEnumerationType = DeviceEnumerationType::Normal;
   DeviceEnumerationType audioEnumerationType = DeviceEnumerationType::Normal;
 
   // Handle loopback and fake requests. For gUM we don't consider resist
   // fingerprinting as users should be prompted anyway.
@@ -2807,169 +2773,203 @@ nsresult MediaManager::GetUserMedia(
        ", videoEnumerationType=%" PRIu8 ", audioEnumerationType=%" PRIu8
        ", askPermission=%s",
        __func__, windowID, static_cast<uint8_t>(videoType),
        static_cast<uint8_t>(audioType),
        static_cast<uint8_t>(videoEnumerationType),
        static_cast<uint8_t>(audioEnumerationType),
        askPermission ? "true" : "false"));
 
-  RefPtr<MediaDeviceSetPromise> p =
-      EnumerateDevicesImpl(windowID, videoType, audioType, MediaSinkEnum::Other,
-                           videoEnumerationType, audioEnumerationType);
   RefPtr<MediaManager> self = this;
-  p->Then(
-      GetCurrentThreadSerialEventTarget(), __func__,
-      [self, onSuccess, onFailure, windowID, c, windowListener, sourceListener,
-       askPermission, prefs, isHTTPS, isHandlingUserInput, callID,
-       principalInfo, isChrome,
-       resistFingerprinting](RefPtr<MediaDeviceSetRefCnt>&& aDevices) mutable {
-        LOG(
-            ("GetUserMedia: post enumeration promise success callback "
-             "starting"));
-
-        // Ensure that our windowID is still good.
-        if (!nsGlobalWindowInner::GetInnerWindowWithId(windowID) ||
-            !self->IsWindowListenerStillActive(windowListener)) {
-          LOG(("GetUserMedia: bad window (%" PRIu64 ") in post enumeration "
-               "success callback!",
-               windowID));
-          return;
-        }
-
-        // Apply any constraints. This modifies the passed-in list.
-        RefPtr<BadConstraintsPromise> p2 =
-            self->SelectSettings(c, isChrome, aDevices);
-
-        p2->Then(
-            GetCurrentThreadSerialEventTarget(), __func__,
-            [self, onSuccess, onFailure, windowID, c, windowListener,
-             sourceListener, askPermission, prefs, isHTTPS, isHandlingUserInput,
-             callID, principalInfo, isChrome, aDevices,
-             resistFingerprinting](const char* badConstraint) mutable {
-              LOG(
-                  ("GetUserMedia: starting post enumeration promise2 success "
-                   "callback!"));
-
-              // Ensure that the window is still good.
-              auto* globalWindow =
-                  nsGlobalWindowInner::GetInnerWindowWithId(windowID);
-              RefPtr<nsPIDOMWindowInner> window =
-                  globalWindow ? globalWindow->AsInner() : nullptr;
-              if (!window ||
-                  !self->IsWindowListenerStillActive(windowListener)) {
-                LOG(("GetUserMedia: bad window (%" PRIu64
-                     ") in post enumeration "
-                     "success callback 2!",
-                     windowID));
-                return;
-              }
-
-              if (badConstraint) {
-                LOG(
-                    ("GetUserMedia: bad constraint found in post enumeration "
-                     "promise2 "
-                     "success callback! Calling error handler!"));
-                nsString constraint;
-                constraint.AssignASCII(badConstraint);
-                RefPtr<MediaStreamError> error = new MediaStreamError(
-                    window, MediaStreamError::Name::OverconstrainedError,
-                    NS_LITERAL_STRING(""), constraint);
-                CallOnError(onFailure, *error);
-                return;
-              }
-              if (!(*aDevices)->Length()) {
-                LOG(
-                    ("GetUserMedia: no devices found in post enumeration "
-                     "promise2 "
-                     "success callback! Calling error handler!"));
-                RefPtr<MediaStreamError> error = new MediaStreamError(
-                    window,
-                    // When privacy.resistFingerprinting = true, no available
-                    // device implies content script is requesting a fake
-                    // device, so report NotAllowedError.
-                    resistFingerprinting
-                        ? MediaStreamError::Name::NotAllowedError
-                        : MediaStreamError::Name::NotFoundError);
-                CallOnError(onFailure, *error);
-                return;
-              }
-
-              nsCOMPtr<nsIMutableArray> devicesCopy =
-                  nsArray::Create();  // before we give up devices below
-              if (!askPermission) {
-                for (auto& device : **aDevices) {
-                  nsresult rv = devicesCopy->AppendElement(device);
-                  if (NS_WARN_IF(NS_FAILED(rv))) {
-                    return;
-                  }
-                }
-              }
-
-              bool focusSource;
-              focusSource = mozilla::Preferences::GetBool(
-                  "media.getusermedia.window.focus_source.enabled", true);
-
-              // Pass callbacks and listeners along to GetUserMediaTask.
-              RefPtr<GetUserMediaTask> task(new GetUserMediaTask(
-                  c, onSuccess, onFailure, windowID, windowListener,
-                  sourceListener, prefs, principalInfo, isChrome,
-                  aDevices->release(), focusSource));
-              // Store the task w/callbacks.
-              self->mActiveCallbacks.Put(callID, task.forget());
-
-              // Add a WindowID cross-reference so OnNavigation can tear things
-              // down
-              nsTArray<nsString>* array;
-              if (!self->mCallIds.Get(windowID, &array)) {
-                array = new nsTArray<nsString>();
-                self->mCallIds.Put(windowID, array);
-              }
-              array->AppendElement(callID);
-
-              nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-              if (!askPermission) {
-                obs->NotifyObservers(devicesCopy,
-                                     "getUserMedia:privileged:allow",
-                                     callID.BeginReading());
-              } else {
-                RefPtr<GetUserMediaRequest> req = new GetUserMediaRequest(
-                    window, callID, c, isHTTPS, isHandlingUserInput);
-                if (!Preferences::GetBool("media.navigator.permission.force") &&
-                    array->Length() > 1) {
-                  // there is at least 1 pending gUM request
-                  // For the scarySources test case, always send the request
-                  self->mPendingGUMRequest.AppendElement(req.forget());
-                } else {
-                  obs->NotifyObservers(req, "getUserMedia:request", nullptr);
-                }
-              }
+  return EnumerateDevicesImpl(windowID, videoType, audioType,
+                              MediaSinkEnum::Other, videoEnumerationType,
+                              audioEnumerationType)
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [self, windowID, c, windowListener, sourceListener, askPermission,
+           prefs, isHTTPS, isHandlingUserInput, callID, principalInfo, isChrome,
+           resistFingerprinting](RefPtr<MediaDeviceSetRefCnt>&& aDevices)
+              -> RefPtr<StreamPromise> {
+            LOG(
+                ("GetUserMedia: post enumeration promise success callback "
+                 "starting"));
+
+            // Ensure that our windowID is still good.
+            auto* globalWindow =
+                nsGlobalWindowInner::GetInnerWindowWithId(windowID);
+            RefPtr<nsPIDOMWindowInner> window =
+                globalWindow ? globalWindow->AsInner() : nullptr;
+            if (!window || !self->IsWindowListenerStillActive(windowListener)) {
+              LOG(("GetUserMedia: bad window (%" PRIu64 ") in post enumeration "
+                   "success callback!",
+                   windowID));
+              return StreamPromise::CreateAndReject(
+                  MakeRefPtr<MediaStreamError>(
+                      window, MediaStreamError::Name::AbortError),
+                  __func__);
+            }
+
+            // Apply any constraints. This modifies the passed-in list.
+            return self->SelectSettings(c, isChrome, aDevices)
+                ->Then(
+                    GetCurrentThreadSerialEventTarget(), __func__,
+                    [self, windowID, c, windowListener, sourceListener,
+                     askPermission, prefs, isHTTPS, isHandlingUserInput, callID,
+                     principalInfo, isChrome, aDevices,
+                     resistFingerprinting](const char* badConstraint) {
+                      LOG(
+                          ("GetUserMedia: starting post enumeration promise2 "
+                           "success "
+                           "callback!"));
+
+                      // Ensure that the window is still good.
+                      auto* globalWindow =
+                          nsGlobalWindowInner::GetInnerWindowWithId(windowID);
+                      RefPtr<nsPIDOMWindowInner> window =
+                          globalWindow ? globalWindow->AsInner() : nullptr;
+                      if (!window ||
+                          !self->IsWindowListenerStillActive(windowListener)) {
+                        LOG(("GetUserMedia: bad window (%" PRIu64
+                             ") in post enumeration "
+                             "success callback 2!",
+                             windowID));
+                        return StreamPromise::CreateAndReject(
+                            MakeRefPtr<MediaStreamError>(
+                                window, MediaStreamError::Name::AbortError),
+                            __func__);
+                      }
+
+                      if (badConstraint) {
+                        LOG(
+                            ("GetUserMedia: bad constraint found in post "
+                             "enumeration promise2 "
+                             "success callback! Calling error handler!"));
+                        nsString constraint;
+                        constraint.AssignASCII(badConstraint);
+                        return StreamPromise::CreateAndReject(
+                            MakeRefPtr<MediaStreamError>(
+                                window,
+                                MediaStreamError::Name::OverconstrainedError,
+                                NS_LITERAL_STRING(""), constraint),
+                            __func__);
+                      }
+                      if (!(*aDevices)->Length()) {
+                        LOG(
+                            ("GetUserMedia: no devices found in post "
+                             "enumeration promise2 "
+                             "success callback! Calling error handler!"));
+                        // When privacy.resistFingerprinting = true, no
+                        // available device implies content script is requesting
+                        // a fake device, so report NotAllowedError.
+                        auto error =
+                            resistFingerprinting
+                                ? MediaStreamError::Name::NotAllowedError
+                                : MediaStreamError::Name::NotFoundError;
+                        return StreamPromise::CreateAndReject(
+                            MakeRefPtr<MediaStreamError>(window, error),
+                            __func__);
+                      }
+
+                      // before we give up devices below
+                      nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create();
+                      if (!askPermission) {
+                        for (auto& device : **aDevices) {
+                          nsresult rv = devicesCopy->AppendElement(device);
+                          if (NS_WARN_IF(NS_FAILED(rv))) {
+                            return StreamPromise::CreateAndReject(
+                                MakeRefPtr<MediaStreamError>(
+                                    window, MediaStreamError::Name::AbortError),
+                                __func__);
+                          }
+                        }
+                      }
+
+                      bool focusSource = mozilla::Preferences::GetBool(
+                          "media.getusermedia.window.focus_source.enabled",
+                          true);
+
+                      // Incremental hack to compile. To be replaced by deeper
+                      // refactoring. MediaManager allows
+                      // "neither-resolve-nor-reject" semantics, so we cannot
+                      // use MozPromiseHolder here.
+                      auto holder = MozPromiseHolder<StreamPromise>();
+                      RefPtr<StreamPromise> p = holder.Ensure(__func__);
+
+                      // Pass callbacks and listeners along to GetUserMediaTask.
+                      auto task = MakeRefPtr<GetUserMediaTask>(
+                          c, std::move(holder), windowID, windowListener,
+                          sourceListener, prefs, principalInfo, isChrome,
+                          aDevices->release(), focusSource);
+
+                      // Store the task w/callbacks.
+                      self->mActiveCallbacks.Put(callID, task.forget());
+
+                      // Add a WindowID cross-reference so OnNavigation can tear
+                      // things down
+                      nsTArray<nsString>* array;
+                      if (!self->mCallIds.Get(windowID, &array)) {
+                        array = new nsTArray<nsString>();
+                        self->mCallIds.Put(windowID, array);
+                      }
+                      array->AppendElement(callID);
+
+                      nsCOMPtr<nsIObserverService> obs =
+                          services::GetObserverService();
+                      if (!askPermission) {
+                        obs->NotifyObservers(devicesCopy,
+                                             "getUserMedia:privileged:allow",
+                                             callID.BeginReading());
+                      } else {
+                        RefPtr<GetUserMediaRequest> req =
+                            new GetUserMediaRequest(window, callID, c, isHTTPS,
+                                                    isHandlingUserInput);
+                        if (!Preferences::GetBool(
+                                "media.navigator.permission.force") &&
+                            array->Length() > 1) {
+                          // there is at least 1 pending gUM request
+                          // For the scarySources test case, always send the
+                          // request
+                          self->mPendingGUMRequest.AppendElement(req.forget());
+                        } else {
+                          obs->NotifyObservers(req, "getUserMedia:request",
+                                               nullptr);
+                        }
+                      }
 
 #ifdef MOZ_WEBRTC
-              EnableWebRtcLog();
+                      EnableWebRtcLog();
 #endif
-            },
-            [onFailure, windowID](nsresult reason) mutable {
-              LOG(
-                  ("GetUserMedia: post enumeration promse2 failure callback "
-                   "called!"));
-              nsPIDOMWindowInner* window =
-                  nsGlobalWindowInner::GetInnerWindowWithId(windowID)
-                      ->AsInner();
-              auto error = MakeRefPtr<MediaStreamError>(
-                  window, MediaStreamError::Name::AbortError);
-              CallOnError(onFailure, *error);
-            });
-      },
-      [onFailure](RefPtr<MediaStreamError>&& reason) mutable {
-        LOG((
-            "GetUserMedia: post enumeration promise failure callback called!"));
-        CallOnError(onFailure, *reason);
-      });
-  return NS_OK;
+                      return p;
+                    },
+                    // SelectSettings failure callback
+                    [windowID](nsresult rv) mutable {
+                      LOG(
+                          ("GetUserMedia: post enumeration promise2 failure "
+                           "callback called!"));
+                      nsPIDOMWindowInner* window =
+                          nsGlobalWindowInner::GetInnerWindowWithId(windowID)
+                              ->AsInner();
+                      return StreamPromise::CreateAndReject(
+                          MakeRefPtr<MediaStreamError>(
+                              window, MediaStreamError::Name::AbortError),
+                          __func__);
+                    });
+          },
+          // EnumerateDevicesImpl failure callback
+          [windowID](RefPtr<MediaStreamError>&& error) {
+            LOG(
+                ("GetUserMedia: post enumeration promise failure callback "
+                 "called!"));
+            nsPIDOMWindowInner* window =
+                nsGlobalWindowInner::GetInnerWindowWithId(windowID)->AsInner();
+            return StreamPromise::CreateAndReject(
+                MakeRefPtr<MediaStreamError>(
+                    window, MediaStreamError::Name::AbortError),
+                __func__);
+          });
 }
 
 /* static */ void MediaManager::AnonymizeDevices(MediaDeviceSet& aDevices,
                                                  const nsACString& aOriginKey) {
   if (!aOriginKey.IsEmpty()) {
     for (RefPtr<MediaDevice>& device : aDevices) {
       nsString id;
       device->GetId(id);
@@ -3084,19 +3084,19 @@ RefPtr<MediaManager::MediaDeviceSetPromi
     return MediaDeviceSetPromise::CreateAndReject(
         MakeRefPtr<MediaStreamError>(window,
                                      MediaStreamError::Name::NotAllowedError),
         __func__);
   }
 
   bool persist = IsActivelyCapturingOrHasAPermission(aWindowId);
 
-  // GetPrincipalKey is an async API that returns a promise.
-  // We use .Then() to pass in a lambda to run back on this same
-  // thread later once GetPrincipalKey resolves. Needed variables are "captured"
+  // GetPrincipalKey is an async API that returns a promise. We use .Then() to
+  // pass in a lambda to run back on this same thread later once
+  // GetPrincipalKey resolves. Needed variables are "captured"
   // (passed by value) safely into the lambda.
   return media::GetPrincipalKey(principalInfo, persist)
       ->Then(
           GetMainThreadSerialEventTarget(), __func__,
           [aWindowId, aVideoInputType, aAudioInputType, aVideoInputEnumType,
            aAudioInputEnumType, aAudioOutputType](
               const nsCString& aOriginKey) -> RefPtr<MediaDeviceSetPromise> {
             MOZ_ASSERT(NS_IsMainThread());
@@ -4135,126 +4135,133 @@ void SourceListener::Activate(SourceMedi
         aVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
             Preferences::GetBool(
                 "media.getusermedia.camera.off_while_disabled.enabled", true),
         MakeRefPtr<SourceTrackListener>(this, kVideoTrack));
     mStream->AddTrackListener(mVideoDeviceState->mListener, kVideoTrack);
   }
 }
 
-RefPtr<SourceListener::InitPromise> SourceListener::InitializeAsync() {
+RefPtr<SourceListener::SourceListenerPromise>
+SourceListener::InitializeAsync() {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   MOZ_DIAGNOSTIC_ASSERT(!mStopped);
 
-  RefPtr<InitPromise> init = MediaManager::PostTask<InitPromise>(
-      __func__,
-      [stream = mStream, principal = GetPrincipalHandle(),
-       audioDevice = mAudioDeviceState ? mAudioDeviceState->mDevice : nullptr,
-       videoDevice = mVideoDeviceState ? mVideoDeviceState->mDevice : nullptr](
-          MozPromiseHolder<InitPromise>& aHolder) {
-        if (audioDevice) {
-          nsresult rv = audioDevice->SetTrack(stream, kAudioTrack, principal);
-          if (NS_SUCCEEDED(rv)) {
-            rv = audioDevice->Start();
-          }
-          if (NS_FAILED(rv)) {
-            nsString log;
-            if (rv == NS_ERROR_NOT_AVAILABLE) {
-              log.AssignLiteral("Concurrent mic process limit.");
-              aHolder.Reject(MakeRefPtr<MediaMgrError>(
-                                 MediaMgrError::Name::NotReadableError, log),
-                             __func__);
-              return;
-            }
-            log.AssignLiteral("Starting audio failed");
-            aHolder.Reject(
-                MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError, log),
-                __func__);
-            return;
-          }
-        }
-
-        if (videoDevice) {
-          nsresult rv = videoDevice->SetTrack(stream, kVideoTrack, principal);
-          if (NS_SUCCEEDED(rv)) {
-            rv = videoDevice->Start();
-          }
-          if (NS_FAILED(rv)) {
-            if (audioDevice) {
-              if (NS_WARN_IF(NS_FAILED(audioDevice->Stop()))) {
-                MOZ_ASSERT_UNREACHABLE("Stopping audio failed");
-              }
-            }
-            nsString log;
-            log.AssignLiteral("Starting video failed");
-            aHolder.Reject(
-                MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError, log),
-                __func__);
-            return;
-          }
-        }
-
-        // Start() queued the tracks to be added synchronously to avoid races
-        stream->FinishAddTracks();
-        LOG(("started all sources"));
-
-        aHolder.Resolve(true, __func__);
-      });
-
-  return init->Then(
-      GetMainThreadSerialEventTarget(), __func__,
-      [self = RefPtr<SourceListener>(this), this]() {
-        if (mStopped) {
-          // We were shut down during the async init
-          return InitPromise::CreateAndResolve(true, __func__);
-        }
-
-        for (DeviceState* state :
-             {mAudioDeviceState.get(), mVideoDeviceState.get()}) {
-          if (!state) {
-            continue;
-          }
-          MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
-          MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
-          MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
-
-          state->mDeviceEnabled = true;
-          state->mTrackEnabled = true;
-          state->mTrackEnabledTime = TimeStamp::Now();
+  return MediaManager::PostTask<SourceListenerPromise>(
+             __func__,
+             [stream = mStream, principal = GetPrincipalHandle(),
+              audioDevice =
+                  mAudioDeviceState ? mAudioDeviceState->mDevice : nullptr,
+              videoDevice =
+                  mVideoDeviceState ? mVideoDeviceState->mDevice : nullptr](
+                 MozPromiseHolder<SourceListenerPromise>& aHolder) {
+               if (audioDevice) {
+                 nsresult rv =
+                     audioDevice->SetTrack(stream, kAudioTrack, principal);
+                 if (NS_SUCCEEDED(rv)) {
+                   rv = audioDevice->Start();
+                 }
+                 if (NS_FAILED(rv)) {
+                   nsString log;
+                   if (rv == NS_ERROR_NOT_AVAILABLE) {
+                     log.AssignLiteral("Concurrent mic process limit.");
+                     aHolder.Reject(
+                         MakeRefPtr<MediaMgrError>(
+                             MediaMgrError::Name::NotReadableError, log),
+                         __func__);
+                     return;
+                   }
+                   log.AssignLiteral("Starting audio failed");
+                   aHolder.Reject(MakeRefPtr<MediaMgrError>(
+                                      MediaMgrError::Name::AbortError, log),
+                                  __func__);
+                   return;
+                 }
+               }
+
+               if (videoDevice) {
+                 nsresult rv =
+                     videoDevice->SetTrack(stream, kVideoTrack, principal);
+                 if (NS_SUCCEEDED(rv)) {
+                   rv = videoDevice->Start();
+                 }
+                 if (NS_FAILED(rv)) {
+                   if (audioDevice) {
+                     if (NS_WARN_IF(NS_FAILED(audioDevice->Stop()))) {
+                       MOZ_ASSERT_UNREACHABLE("Stopping audio failed");
+                     }
+                   }
+                   nsString log;
+                   log.AssignLiteral("Starting video failed");
+                   aHolder.Reject(MakeRefPtr<MediaMgrError>(
+                                      MediaMgrError::Name::AbortError, log),
+                                  __func__);
+                   return;
+                 }
+               }
+
+               // Start() queued the tracks to be added synchronously to avoid
+               // races
+               stream->FinishAddTracks();
+               LOG(("started all sources"));
+
+               aHolder.Resolve(true, __func__);
+             })
+      ->Then(GetMainThreadSerialEventTarget(), __func__,
+             [self = RefPtr<SourceListener>(this), this]() {
+               if (mStopped) {
+                 // We were shut down during the async init
+                 return SourceListenerPromise::CreateAndResolve(true, __func__);
+               }
+
+               for (DeviceState* state :
+                    {mAudioDeviceState.get(), mVideoDeviceState.get()}) {
+                 if (!state) {
+                   continue;
+                 }
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
+
+                 state->mDeviceEnabled = true;
+                 state->mTrackEnabled = true;
+                 state->mTrackEnabledTime = TimeStamp::Now();
 
           if (state->mDevice->GetMediaSource() !=
               MediaSourceEnum::AudioCapture) {
             // For AudioCapture mStream is a dummy stream, so we don't try to
             // enable pulling - there won't be a track to enable it for.
             mStream->SetPullingEnabled(
                 state == mAudioDeviceState.get() ? kAudioTrack : kVideoTrack,
                 true);
           }
-        }
-        return InitPromise::CreateAndResolve(true, __func__);
-      },
-      [self = RefPtr<SourceListener>(this),
-       this](RefPtr<MediaMgrError>&& aResult) {
-        if (mStopped) {
-          return InitPromise::CreateAndReject(std::move(aResult), __func__);
-        }
-
-        for (DeviceState* state :
-             {mAudioDeviceState.get(), mVideoDeviceState.get()}) {
-          if (!state) {
-            continue;
-          }
-          MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
-          MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
-          MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
-
-          state->mStopped = true;
-        }
-        return InitPromise::CreateAndReject(std::move(aResult), __func__);
-      });
+               }
+               return SourceListenerPromise::CreateAndResolve(true, __func__);
+             },
+             [self = RefPtr<SourceListener>(this),
+              this](RefPtr<MediaMgrError>&& aResult) {
+               if (mStopped) {
+                 return SourceListenerPromise::CreateAndReject(
+                     std::move(aResult), __func__);
+               }
+
+               for (DeviceState* state :
+                    {mAudioDeviceState.get(), mVideoDeviceState.get()}) {
+                 if (!state) {
+                   continue;
+                 }
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
+                 MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
+
+                 state->mStopped = true;
+               }
+               return SourceListenerPromise::CreateAndReject(std::move(aResult),
+                                                             __func__);
+             });
 }
 
 void SourceListener::Stop() {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
 
   if (mStopped) {
     return;
   }
@@ -4646,64 +4653,67 @@ CaptureState SourceListener::CapturingSo
 
   if (state.mDeviceEnabled) {
     return CaptureState::Enabled;
   }
 
   return CaptureState::Disabled;
 }
 
-RefPtr<SourceListener::ApplyConstraintsPromise>
+RefPtr<SourceListener::SourceListenerPromise>
 SourceListener::ApplyConstraintsToTrack(
     nsPIDOMWindowInner* aWindow, TrackID aTrackID,
     const MediaTrackConstraints& aConstraints, dom::CallerType aCallerType) {
   MOZ_ASSERT(NS_IsMainThread());
   DeviceState& state = GetDeviceStateFor(aTrackID);
 
   if (mStopped || state.mStopped) {
     LOG(("gUM %s track %d applyConstraints, but source is stopped",
          aTrackID == kAudioTrack ? "audio" : "video", aTrackID));
-    return ApplyConstraintsPromise::CreateAndResolve(false, __func__);
+    return SourceListenerPromise::CreateAndResolve(false, __func__);
   }
 
   MediaManager* mgr = MediaManager::GetIfExists();
   if (!mgr) {
-    return ApplyConstraintsPromise::CreateAndResolve(false, __func__);
+    return SourceListenerPromise::CreateAndResolve(false, __func__);
   }
 
-  return MediaManager::PostTask<ApplyConstraintsPromise>(
-      __func__,
-      [device = state.mDevice, aConstraints,
-       isChrome = aCallerType == dom::CallerType::System](
-          MozPromiseHolder<ApplyConstraintsPromise>& aHolder) mutable {
+  return MediaManager::PostTask<SourceListenerPromise>(
+      __func__, [device = state.mDevice, aConstraints,
+                 isChrome = aCallerType == dom::CallerType::System](
+                    MozPromiseHolder<SourceListenerPromise>& aHolder) mutable {
         MOZ_ASSERT(MediaManager::IsInMediaThread());
         MediaManager* mgr = MediaManager::GetIfExists();
         MOZ_RELEASE_ASSERT(mgr);  // Must exist while media thread is alive
         const char* badConstraint = nullptr;
         nsresult rv =
             device->Reconfigure(aConstraints, mgr->mPrefs, &badConstraint);
-        if (rv == NS_ERROR_INVALID_ARG) {
-          // Reconfigure failed due to constraints
-          if (!badConstraint) {
-            nsTArray<RefPtr<MediaDevice>> devices;
-            devices.AppendElement(device);
-            badConstraint = MediaConstraintsHelper::SelectSettings(
-                NormalizedConstraints(aConstraints), devices, isChrome);
+        if (NS_FAILED(rv)) {
+          if (rv == NS_ERROR_INVALID_ARG) {
+            // Reconfigure failed due to constraints
+            if (!badConstraint) {
+              nsTArray<RefPtr<MediaDevice>> devices;
+              devices.AppendElement(device);
+              badConstraint = MediaConstraintsHelper::SelectSettings(
+                  NormalizedConstraints(aConstraints), devices, isChrome);
+            }
+          } else {
+            // Unexpected. ApplyConstraints* cannot fail with any other error.
+            badConstraint = "";
+            LOG(("ApplyConstraintsToTrack-Task: Unexpected fail %" PRIx32,
+                 static_cast<uint32_t>(rv)));
           }
 
-          aHolder.Reject(Some(NS_ConvertASCIItoUTF16(badConstraint)), __func__);
+          aHolder.Reject(
+              MakeRefPtr<MediaMgrError>(
+                  MediaMgrError::Name::OverconstrainedError,
+                  NS_LITERAL_STRING(""), NS_ConvertASCIItoUTF16(badConstraint)),
+              __func__);
           return;
         }
-
-        if (NS_FAILED(rv)) {
-          // Reconfigure failed unexpectedly
-          aHolder.Reject(Nothing(), __func__);
-          return;
-        }
-
         // Reconfigure was successful
         aHolder.Resolve(false, __func__);
       });
 }
 
 PrincipalHandle SourceListener::GetPrincipalHandle() const {
   return mPrincipalHandle;
 }
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -202,21 +202,29 @@ class MediaManager final : public nsIMed
 
   typedef dom::CallbackObjectHolder<dom::NavigatorUserMediaSuccessCallback,
                                     nsIDOMGetUserMediaSuccessCallback>
       GetUserMediaSuccessCallback;
   typedef dom::CallbackObjectHolder<dom::NavigatorUserMediaErrorCallback,
                                     nsIDOMGetUserMediaErrorCallback>
       GetUserMediaErrorCallback;
 
-  nsresult GetUserMedia(nsPIDOMWindowInner* aWindow,
-                        const dom::MediaStreamConstraints& aConstraints,
-                        GetUserMediaSuccessCallback&& onSuccess,
-                        GetUserMediaErrorCallback&& onError,
-                        dom::CallerType aCallerType);
+  static void CallOnError(const GetUserMediaErrorCallback* aCallback,
+                          dom::MediaStreamError& aError);
+  static void CallOnSuccess(const GetUserMediaSuccessCallback* aCallback,
+                            DOMMediaStream& aStream);
+
+  typedef MozPromise<RefPtr<DOMMediaStream>, RefPtr<dom::MediaStreamError>,
+                     true>
+      StreamPromise;
+
+  RefPtr<StreamPromise> GetUserMedia(
+      nsPIDOMWindowInner* aWindow,
+      const dom::MediaStreamConstraints& aConstraints,
+      dom::CallerType aCallerType);
 
   nsresult GetUserMediaDevices(
       nsPIDOMWindowInner* aWindow,
       const dom::MediaStreamConstraints& aConstraints,
       dom::MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
       uint64_t aInnerWindowID = 0, const nsAString& aCallID = nsString());
 
   nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow,
@@ -289,17 +297,17 @@ class MediaManager final : public nsIMed
 
   RefPtr<MediaDeviceSetPromise> EnumerateDevicesImpl(
       uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType,
       dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
       DeviceEnumerationType aVideoInputEnumType,
       DeviceEnumerationType aAudioInputEnumType);
 
   RefPtr<BadConstraintsPromise> SelectSettings(
-      dom::MediaStreamConstraints& aConstraints, bool aIsChrome,
+      const dom::MediaStreamConstraints& aConstraints, bool aIsChrome,
       const RefPtr<MediaDeviceSetRefCnt>& aSources);
 
   void GetPref(nsIPrefBranch* aBranch, const char* aPref, const char* aData,
                int32_t* aVal);
   void GetPrefBool(nsIPrefBranch* aBranch, const char* aPref, const char* aData,
                    bool* aVal);
   void GetPrefs(nsIPrefBranch* aBranch, const char* aData);
 
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -41,22 +41,22 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Me
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaStreamTrackSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 auto MediaStreamTrackSource::ApplyConstraints(
     nsPIDOMWindowInner* aWindow, const dom::MediaTrackConstraints& aConstraints,
-    CallerType aCallerType) -> already_AddRefed<PledgeVoid> {
-  RefPtr<PledgeVoid> p = new PledgeVoid();
-  p->Reject(new MediaStreamError(aWindow,
-                                 MediaStreamError::Name::OverconstrainedError,
-                                 NS_LITERAL_STRING("")));
-  return p.forget();
+    CallerType aCallerType) -> RefPtr<ApplyConstraintsPromise> {
+  return ApplyConstraintsPromise::CreateAndReject(
+      MakeRefPtr<MediaStreamError>(aWindow,
+                                   MediaStreamError::Name::OverconstrainedError,
+                                   NS_LITERAL_STRING("")),
+      __func__);
 }
 
 /**
  * MSGListener monitors state changes of the media flowing through the
  * MediaStreamGraph.
  *
  *
  * For changes to PrincipalHandle the following applies:
@@ -343,44 +343,48 @@ already_AddRefed<Promise> MediaStreamTra
     nsString str;
     aConstraints.ToJSON(str);
 
     LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with "
                          "constraints %s",
                          this, NS_ConvertUTF16toUTF8(str).get()));
   }
 
-  typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
-
   nsPIDOMWindowInner* window = mOwningStream->GetParentObject();
   nsIGlobalObject* go = window ? window->AsGlobal() : nullptr;
 
   RefPtr<Promise> promise = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   // Forward constraints to the source.
   //
   // After GetSource().ApplyConstraints succeeds (after it's been to
   // media-thread and back), and no sooner, do we set mConstraints to the newly
   // applied values.
 
   // Keep a reference to this, to make sure it's still here when we get back.
-  RefPtr<MediaStreamTrack> that = this;
-  RefPtr<PledgeVoid> p =
-      GetSource().ApplyConstraints(window, aConstraints, aCallerType);
-  p->Then(
-      [this, that, promise, aConstraints](bool& aDummy) mutable {
-        mConstraints = aConstraints;
-        promise->MaybeResolve(false);
-      },
-      [promise](MediaStreamError*& reason) mutable {
-        promise->MaybeReject(reason);
-      });
+  RefPtr<MediaStreamTrack> self(this);
+  GetSource()
+      .ApplyConstraints(window, aConstraints, aCallerType)
+      ->Then(GetCurrentThreadSerialEventTarget(), __func__,
+             [this, self, promise, aConstraints](bool aDummy) {
+               if (NS_FAILED(CheckInnerWindowCorrectness())) {
+                 return;  // Leave Promise pending after navigation by design.
+               }
+               mConstraints = aConstraints;
+               promise->MaybeResolve(false);
+             },
+             [this, self, promise](const RefPtr<MediaStreamError>& reason) {
+               if (NS_FAILED(CheckInnerWindowCorrectness())) {
+                 return;  // Leave Promise pending after navigation by design.
+               }
+               promise->MaybeReject(reason);
+             });
   return promise.forget();
 }
 
 MediaStreamGraph* MediaStreamTrack::Graph() {
   return GetOwnedStream()->Graph();
 }
 
 MediaStreamGraphImpl* MediaStreamTrack::GraphImpl() {
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -150,23 +150,24 @@ class MediaStreamTrackSource : public ns
    * Forwards a photo request to backends that support it. Other backends return
    * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
    * should be used.
    */
   virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+  typedef MozPromise<bool /* aIgnored */, RefPtr<dom::MediaStreamError>, true>
+      ApplyConstraintsPromise;
 
   /**
    * We provide a fallback solution to ApplyConstraints() here.
    * Sources that support ApplyConstraints() will have to override it.
    */
-  virtual already_AddRefed<PledgeVoid> ApplyConstraints(
+  virtual RefPtr<ApplyConstraintsPromise> ApplyConstraints(
       nsPIDOMWindowInner* aWindow,
       const dom::MediaTrackConstraints& aConstraints, CallerType aCallerType);
 
   /**
    * Same for GetSettings (no-op).
    */
   virtual void GetSettings(dom::MediaTrackSettings& aResult){};
 
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp
@@ -671,23 +671,47 @@ void SpeechRecognition::Start(const Opti
     for (const RefPtr<AudioStreamTrack>& track : tracks) {
       if (!track->Ended()) {
         NotifyTrackAdded(track);
         break;
       }
     }
   } else {
     AutoNoJSAPI nojsapi;
-    MediaManager* manager = MediaManager::Get();
-    MediaManager::GetUserMediaSuccessCallback onsuccess(
-        new GetUserMediaSuccessCallback(this));
-    MediaManager::GetUserMediaErrorCallback onerror(
-        new GetUserMediaErrorCallback(this));
-    manager->GetUserMedia(GetOwner(), constraints, std::move(onsuccess),
-                          std::move(onerror), aCallerType);
+    RefPtr<SpeechRecognition> self(this);
+    MediaManager::Get()
+        ->GetUserMedia(GetOwner(), constraints, aCallerType)
+        ->Then(GetCurrentThreadSerialEventTarget(), __func__,
+               [this, self](RefPtr<DOMMediaStream>&& aStream) {
+                 mStream = std::move(aStream);
+                 mStream->RegisterTrackListener(this);
+                 nsTArray<RefPtr<AudioStreamTrack>> tracks;
+                 mStream->GetAudioTracks(tracks);
+                 for (const RefPtr<AudioStreamTrack>& track : tracks) {
+                   if (!track->Ended()) {
+                     NotifyTrackAdded(track);
+                   }
+                 }
+               },
+               [this, self](RefPtr<MediaStreamError>&& error) {
+                 SpeechRecognitionErrorCode errorCode;
+
+                 nsAutoString name;
+                 error->GetName(name);
+                 if (name.EqualsLiteral("NotAllowedError")) {
+                   errorCode = SpeechRecognitionErrorCode::Not_allowed;
+                 } else {
+                   errorCode = SpeechRecognitionErrorCode::Audio_capture;
+                 }
+
+                 nsAutoString message;
+                 error->GetMessage(message);
+                 DispatchError(SpeechRecognition::EVENT_AUDIO_ERROR, errorCode,
+                               message);
+               });
   }
 
   RefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_START);
   NS_DispatchToMainThread(event);
 }
 
 bool SpeechRecognition::SetRecognitionService(ErrorResult& aRv) {
   // See:
@@ -966,58 +990,11 @@ SpeechEvent::SpeechEvent(SpeechRecogniti
 SpeechEvent::~SpeechEvent() { delete mAudioSegment; }
 
 NS_IMETHODIMP
 SpeechEvent::Run() {
   mRecognition->ProcessEvent(this);
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(SpeechRecognition::GetUserMediaSuccessCallback,
-                  nsIDOMGetUserMediaSuccessCallback)
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaSuccessCallback::OnSuccess(
-    nsISupports* aStream) {
-  RefPtr<DOMMediaStream> stream = do_QueryObject(aStream);
-  if (!stream) {
-    return NS_ERROR_NO_INTERFACE;
-  }
-  mRecognition->mStream = stream;
-  mRecognition->mStream->RegisterTrackListener(mRecognition);
-  nsTArray<RefPtr<AudioStreamTrack>> tracks;
-  mRecognition->mStream->GetAudioTracks(tracks);
-  for (const RefPtr<AudioStreamTrack>& track : tracks) {
-    if (!track->Ended()) {
-      mRecognition->NotifyTrackAdded(track);
-    }
-  }
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(SpeechRecognition::GetUserMediaErrorCallback,
-                  nsIDOMGetUserMediaErrorCallback)
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaErrorCallback::OnError(nsISupports* aError) {
-  RefPtr<MediaStreamError> error = do_QueryObject(aError);
-  if (!error) {
-    return NS_OK;
-  }
-  SpeechRecognitionErrorCode errorCode;
-
-  nsAutoString name;
-  error->GetName(name);
-  if (name.EqualsLiteral("PERMISSION_DENIED")) {
-    errorCode = SpeechRecognitionErrorCode::Not_allowed;
-  } else {
-    errorCode = SpeechRecognitionErrorCode::Audio_capture;
-  }
-
-  nsAutoString message;
-  error->GetMessage(message);
-  mRecognition->DispatchError(SpeechRecognition::EVENT_AUDIO_ERROR, errorCode,
-                              message);
-  return NS_OK;
-}
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechRecognition.h
+++ b/dom/media/webspeech/recognition/SpeechRecognition.h
@@ -156,44 +156,16 @@ class SpeechRecognition final : public D
   };
 
   void SetState(FSMState state);
   bool StateBetween(FSMState begin, FSMState end);
 
   bool SetRecognitionService(ErrorResult& aRv);
   bool ValidateAndSetGrammarList(ErrorResult& aRv);
 
-  class GetUserMediaSuccessCallback : public nsIDOMGetUserMediaSuccessCallback {
-   public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIDOMGETUSERMEDIASUCCESSCALLBACK
-
-    explicit GetUserMediaSuccessCallback(SpeechRecognition* aRecognition)
-        : mRecognition(aRecognition) {}
-
-   private:
-    virtual ~GetUserMediaSuccessCallback() {}
-
-    RefPtr<SpeechRecognition> mRecognition;
-  };
-
-  class GetUserMediaErrorCallback : public nsIDOMGetUserMediaErrorCallback {
-   public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIDOMGETUSERMEDIAERRORCALLBACK
-
-    explicit GetUserMediaErrorCallback(SpeechRecognition* aRecognition)
-        : mRecognition(aRecognition) {}
-
-   private:
-    virtual ~GetUserMediaErrorCallback() {}
-
-    RefPtr<SpeechRecognition> mRecognition;
-  };
-
   NS_IMETHOD StartRecording(RefPtr<AudioStreamTrack>& aDOMStream);
   NS_IMETHOD StopRecording();
 
   uint32_t ProcessAudioSegment(AudioSegment* aSegment, TrackRate aTrackRate);
   void NotifyError(SpeechEvent* aEvent);
 
   void ProcessEvent(SpeechEvent* aEvent);
   void Transition(SpeechEvent* aEvent);
--- a/media/webrtc/signaling/src/peerconnection/RemoteTrackSource.h
+++ b/media/webrtc/signaling/src/peerconnection/RemoteTrackSource.h
@@ -16,27 +16,26 @@ public:
   explicit RemoteTrackSource(nsIPrincipal* aPrincipal, const nsString& aLabel)
     : dom::MediaStreamTrackSource(aPrincipal, aLabel) {}
 
   dom::MediaSourceEnum GetMediaSource() const override
   {
     return dom::MediaSourceEnum::Other;
   }
 
-  already_AddRefed<PledgeVoid>
+  RefPtr<ApplyConstraintsPromise>
   ApplyConstraints(nsPIDOMWindowInner* aWindow,
                    const dom::MediaTrackConstraints& aConstraints,
                    dom::CallerType aCallerType) override
   {
-    RefPtr<PledgeVoid> p = new PledgeVoid();
-    p->Reject(
-        new dom::MediaStreamError(aWindow,
-                                  MediaStreamError::Name::OverconstrainedError,
-                                  NS_LITERAL_STRING("")));
-    return p.forget();
+    return ApplyConstraintsPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::OverconstrainedError,
+                                     NS_LITERAL_STRING("")),
+        __func__);
   }
 
   void Stop() override
   {
     // XXX (Bug 1314270): Implement rejection logic if necessary when we have
     //                    clarity in the spec.
   }