Bug 1512280 - Add out-parameter to EnumerateDevicesImpl(); clearer semantics and flattens things. r=pehrsons
☠☠ backed out by 4ef33177d5ba ☠ ☠
authorJan-Ivar Bruaroey <jib@mozilla.com>
Wed, 12 Dec 2018 04:00:42 +0000
changeset 507314 9463a1a3d790eaba8d61112c8dac961349a3da55
parent 507313 9a68c5f1ca0d0dc38e70e448aa2043c1569d27c3
child 507315 abe93461261604556e83094ebdaf7e86e8faf327
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons
bugs1512280
milestone66.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 1512280 - Add out-parameter to EnumerateDevicesImpl(); clearer semantics and flattens things. r=pehrsons Differential Revision: https://phabricator.services.mozilla.com/D13985
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1743,23 +1743,22 @@ class GetUserMediaRunnableWrapper : publ
 };
 #endif
 
 /**
  * EnumerateRawDevices - Enumerate a list of audio & video devices that
  * satisfy passed-in constraints. List contains raw id's.
  */
 
-RefPtr<MediaManager::MediaDeviceSetPromise> MediaManager::EnumerateRawDevices(
+RefPtr<MediaManager::EnumerateImplPromise> MediaManager::EnumerateRawDevices(
     uint64_t aWindowId, MediaSourceEnum aVideoInputType,
     MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
-    DeviceEnumerationType
-        aVideoInputEnumType /* = DeviceEnumerationType::Normal */,
-    DeviceEnumerationType
-        aAudioInputEnumType /* = DeviceEnumerationType::Normal */) {
+    DeviceEnumerationType aVideoInputEnumType,
+    DeviceEnumerationType aAudioInputEnumType,
+    const RefPtr<MediaDeviceSetRefCnt>& aOutDevices) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aVideoInputType != MediaSourceEnum::Other ||
              aAudioInputType != MediaSourceEnum::Other ||
              aAudioOutputType != MediaSinkEnum::Other);
   // Since the enums can take one of several values, the following asserts rely
   // on short circuting behavior. E.g. aVideoInputEnumType != Fake will be true
   // if the requested device is not fake and thus the assert will pass. However,
   // if the device is fake, aVideoInputType == MediaSourceEnum::Camera will be
@@ -1780,18 +1779,18 @@ RefPtr<MediaManager::MediaDeviceSetPromi
   LOG("%s: aWindowId=%" PRIu64 ", aVideoInputType=%" PRIu8
       ", aAudioInputType=%" PRIu8 ", aVideoInputEnumType=%" PRIu8
       ", aAudioInputEnumType=%" PRIu8,
       __func__, aWindowId, static_cast<uint8_t>(aVideoInputType),
       static_cast<uint8_t>(aAudioInputType),
       static_cast<uint8_t>(aVideoInputEnumType),
       static_cast<uint8_t>(aAudioInputEnumType));
 
-  auto holder = MakeUnique<MozPromiseHolder<MediaDeviceSetPromise>>();
-  RefPtr<MediaDeviceSetPromise> promise = holder->Ensure(__func__);
+  auto holder = MakeUnique<MozPromiseHolder<EnumerateImplPromise>>();
+  RefPtr<EnumerateImplPromise> promise = holder->Ensure(__func__);
 
   bool hasVideo = aVideoInputType != MediaSourceEnum::Other;
   bool hasAudio = aAudioInputType != MediaSourceEnum::Other;
   bool hasAudioOutput = aAudioOutputType == MediaSinkEnum::Speaker;
 
   // True of at least one of video input or audio input is a fake device
   bool fakeDeviceRequested =
       (aVideoInputEnumType == DeviceEnumerationType::Fake && hasVideo) ||
@@ -1811,60 +1810,58 @@ RefPtr<MediaManager::MediaDeviceSetPromi
     Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
   }
 
   RefPtr<Runnable> task = NewTaskFrom([holder = std::move(holder), aWindowId,
                                        aVideoInputType, aAudioInputType,
                                        aVideoInputEnumType, aAudioInputEnumType,
                                        videoLoopDev, audioLoopDev, hasVideo,
                                        hasAudio, hasAudioOutput,
-                                       fakeDeviceRequested,
-                                       realDeviceRequested]() {
+                                       fakeDeviceRequested, realDeviceRequested,
+                                       aOutDevices]() {
     // Only enumerate what's asked for, and only fake cams and mics.
     RefPtr<MediaEngine> fakeBackend, realBackend;
     if (fakeDeviceRequested) {
       fakeBackend = new MediaEngineDefault();
     }
     if (realDeviceRequested) {
       MediaManager* manager = MediaManager::GetIfExists();
       MOZ_RELEASE_ASSERT(manager);  // Must exist while media thread is alive
       realBackend = manager->GetBackend(aWindowId);
     }
 
-    auto result = MakeRefPtr<MediaDeviceSetRefCnt>();
-
     if (hasVideo) {
       MediaDeviceSet videos;
       LOG("EnumerateRawDevices Task: Getting video sources with %s backend",
           aVideoInputEnumType == DeviceEnumerationType::Fake ? "fake" : "real");
       GetMediaDevices(aVideoInputEnumType == DeviceEnumerationType::Fake
                           ? fakeBackend
                           : realBackend,
                       aWindowId, aVideoInputType, videos, videoLoopDev.get());
-      result->AppendElements(videos);
+      aOutDevices->AppendElements(videos);
     }
     if (hasAudio) {
       MediaDeviceSet audios;
       LOG("EnumerateRawDevices Task: Getting audio sources with %s backend",
           aAudioInputEnumType == DeviceEnumerationType::Fake ? "fake" : "real");
       GetMediaDevices(aAudioInputEnumType == DeviceEnumerationType::Fake
                           ? fakeBackend
                           : realBackend,
                       aWindowId, aAudioInputType, audios, audioLoopDev.get());
-      result->AppendElements(audios);
+      aOutDevices->AppendElements(audios);
     }
     if (hasAudioOutput) {
       MediaDeviceSet outputs;
       MOZ_ASSERT(realBackend);
       realBackend->EnumerateDevices(aWindowId, MediaSourceEnum::Other,
                                     MediaSinkEnum::Speaker, &outputs);
-      result->AppendElements(outputs);
+      aOutDevices->AppendElements(outputs);
     }
 
-    holder->Resolve(std::move(result), __func__);
+    holder->Resolve(false, __func__);
   });
 
   if (realDeviceRequested &&
       Preferences::GetBool("media.navigator.permission.device", false)) {
     // Need to ask permission to retrieve list of all devices;
     // notify frontend observer and wait for callback notification to post task.
     const char16_t* const type =
         (aVideoInputType != MediaSourceEnum::Camera)
@@ -2173,29 +2170,31 @@ void MediaManager::OnDeviceChange() {
           return;
         }
         self->DeviceChangeCallback::OnDeviceChange();
 
         // On some Windows machine, if we call EnumerateRawDevices immediately
         // after receiving devicechange event, sometimes we would get outdated
         // devices list.
         PR_Sleep(PR_MillisecondsToInterval(100));
-        self->EnumerateRawDevices(0, MediaSourceEnum::Camera,
-                                  MediaSourceEnum::Microphone,
-                                  MediaSinkEnum::Speaker)
+        auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
+        self->EnumerateRawDevices(
+                0, MediaSourceEnum::Camera, MediaSourceEnum::Microphone,
+                MediaSinkEnum::Speaker, DeviceEnumerationType::Normal,
+                DeviceEnumerationType::Normal, devices)
             ->Then(GetCurrentThreadSerialEventTarget(), __func__,
-                   [self](RefPtr<MediaDeviceSetRefCnt>&& aDevices) {
+                   [self, devices](bool) {
                      MediaManager* mgr = MediaManager::GetIfExists();
                      if (!mgr) {
                        return;
                      }
 
                      nsTArray<nsString> deviceIDs;
 
-                     for (auto& device : *aDevices) {
+                     for (auto& device : *devices) {
                        nsString id;
                        device->GetId(id);
                        id.ReplaceSubstring(NS_LITERAL_STRING("default: "),
                                            NS_LITERAL_STRING(""));
                        if (!deviceIDs.Contains(id)) {
                          deviceIDs.AppendElement(id);
                        }
                      }
@@ -2690,181 +2689,168 @@ RefPtr<MediaManager::StreamPromise> Medi
       ", 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<MediaManager> self = this;
+  auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
   return EnumerateDevicesImpl(windowID, videoType, audioType,
                               MediaSinkEnum::Other, videoEnumerationType,
-                              audioEnumerationType)
+                              audioEnumerationType, devices)
       ->Then(
           GetCurrentThreadSerialEventTarget(), __func__,
-          [self, windowID, c, windowListener, sourceListener, askPermission,
-           prefs, isHTTPS, isHandlingUserInput, callID, principalInfo, isChrome,
-           resistFingerprinting](RefPtr<MediaDeviceSetRefCnt>&& aDevices)
-              -> RefPtr<StreamPromise> {
+          [self, windowID, c, windowListener, sourceListener, callID,
+           principalInfo, isChrome, devices](bool) {
             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(
+              return BadConstraintsPromise::CreateAndReject(
                   MakeRefPtr<MediaMgrError>(MediaMgrError::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) 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 StreamPromise::CreateAndReject(
-                            MakeRefPtr<MediaMgrError>(
-                                MediaMgrError::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<MediaMgrError>(
-                                MediaMgrError::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
-                                         ? MediaMgrError::Name::NotAllowedError
-                                         : MediaMgrError::Name::NotFoundError;
-                        return StreamPromise::CreateAndReject(
-                            MakeRefPtr<MediaMgrError>(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<MediaMgrError>(
-                                    MediaMgrError::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,
-                          std::move(aDevices), 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();
-#endif
-                      return p;
-                    },
-                    [](nsresult rv) {
-                      LOG("GetUserMedia: post enumeration SelectSettings "
-                          "failure callback called!");
-                      return StreamPromise::CreateAndReject(
-                          MakeRefPtr<MediaMgrError>(
-                              MediaMgrError::Name::AbortError),
-                          __func__);
-                    });
+            return self->SelectSettings(c, isChrome, devices);
           },
           [](RefPtr<MediaMgrError>&& aError) {
             LOG("GetUserMedia: post enumeration EnumerateDevicesImpl "
                 "failure callback called!");
+            return BadConstraintsPromise::CreateAndReject(std::move(aError),
+                                                          __func__);
+          })
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [self, windowID, c, windowListener, sourceListener, askPermission,
+           prefs, isHTTPS, isHandlingUserInput, callID, principalInfo, isChrome,
+           devices, 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 StreamPromise::CreateAndReject(
+                  MakeRefPtr<MediaMgrError>(MediaMgrError::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<MediaMgrError>(
+                      MediaMgrError::Name::OverconstrainedError,
+                      NS_LITERAL_STRING(""), constraint),
+                  __func__);
+            }
+            if (!devices->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
+                               ? MediaMgrError::Name::NotAllowedError
+                               : MediaMgrError::Name::NotFoundError;
+              return StreamPromise::CreateAndReject(
+                  MakeRefPtr<MediaMgrError>(error), __func__);
+            }
+
+            // before we give up devices below
+            nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create();
+            if (!askPermission) {
+              for (auto& device : *devices) {
+                nsresult rv = devicesCopy->AppendElement(device);
+                if (NS_WARN_IF(NS_FAILED(rv))) {
+                  return StreamPromise::CreateAndReject(
+                      MakeRefPtr<MediaMgrError>(
+                          MediaMgrError::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, std::move(devices),
+                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();
+#endif
+            return p;
+          },
+          [](RefPtr<MediaMgrError>&& aError) {
+            LOG("GetUserMedia: post enumeration SelectSettings "
+                "failure callback called!");
             return StreamPromise::CreateAndReject(std::move(aError), __func__);
           });
-}
+};
 
 /* static */ void MediaManager::AnonymizeDevices(MediaDeviceSet& aDevices,
                                                  const nsACString& aOriginKey) {
   if (!aOriginKey.IsEmpty()) {
     for (RefPtr<MediaDevice>& device : aDevices) {
       nsString id;
       device->GetId(id);
       nsString rawId(id);
@@ -2939,21 +2925,22 @@ already_AddRefed<nsIWritableVariant> Med
     }
   } else {
     var->SetAsEmptyArray();  // because SetAsArray() fails on zero length
                              // arrays.
   }
   return var.forget();
 }
 
-RefPtr<MediaManager::MediaDeviceSetPromise> MediaManager::EnumerateDevicesImpl(
+RefPtr<MediaManager::EnumerateImplPromise> MediaManager::EnumerateDevicesImpl(
     uint64_t aWindowId, MediaSourceEnum aVideoInputType,
     MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
     DeviceEnumerationType aVideoInputEnumType,
-    DeviceEnumerationType aAudioInputEnumType) {
+    DeviceEnumerationType aAudioInputEnumType,
+    const RefPtr<MediaDeviceSetRefCnt>& aOutDevices) {
   MOZ_ASSERT(NS_IsMainThread());
 
   LOG("%s: aWindowId=%" PRIu64 ", aVideoInputType=%" PRIu8
       ", aAudioInputType=%" PRIu8 ", aVideoInputEnumType=%" PRIu8
       ", aAudioInputEnumType=%" PRIu8,
       __func__, aWindowId, static_cast<uint8_t>(aVideoInputType),
       static_cast<uint8_t>(aAudioInputType),
       static_cast<uint8_t>(aVideoInputEnumType),
@@ -2968,101 +2955,100 @@ RefPtr<MediaManager::MediaDeviceSetPromi
 
   nsCOMPtr<nsIPrincipal> principal =
       nsGlobalWindowInner::Cast(window)->GetPrincipal();
   MOZ_ASSERT(principal);
 
   ipc::PrincipalInfo principalInfo;
   nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return MediaDeviceSetPromise::CreateAndReject(
+    return EnumerateImplPromise::CreateAndReject(
         MakeRefPtr<MediaMgrError>(MediaMgrError::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"
   // (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> {
+           aAudioInputEnumType, aAudioOutputType, aOutDevices](
+              const nsCString& aOriginKey) -> RefPtr<EnumerateImplPromise> {
             MOZ_ASSERT(NS_IsMainThread());
             MediaManager* mgr = MediaManager::GetIfExists();
             MOZ_ASSERT(mgr);
             if (!mgr->IsWindowStillActive(aWindowId)) {
-              return MediaDeviceSetPromise::CreateAndReject(
+              return EnumerateImplPromise::CreateAndReject(
                   MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
                   __func__);
             }
-
             return mgr
                 ->EnumerateRawDevices(aWindowId, aVideoInputType,
                                       aAudioInputType, aAudioOutputType,
-                                      aVideoInputEnumType, aAudioInputEnumType)
+                                      aVideoInputEnumType, aAudioInputEnumType,
+                                      aOutDevices)
                 ->Then(
                     GetMainThreadSerialEventTarget(), __func__,
                     [aWindowId, aOriginKey, aVideoInputEnumType,
-                     aAudioInputEnumType, aVideoInputType,
-                     aAudioInputType](RefPtr<MediaDeviceSetRefCnt>&& aDevices)
-                        -> RefPtr<MediaDeviceSetPromise> {
+                     aAudioInputEnumType, aVideoInputType, aAudioInputType,
+                     aOutDevices](bool) -> RefPtr<EnumerateImplPromise> {
                       // Only run if window is still on our active list.
                       MediaManager* mgr = MediaManager::GetIfExists();
                       if (!mgr || !mgr->IsWindowStillActive(aWindowId)) {
-                        return MediaDeviceSetPromise::CreateAndReject(
+                        return EnumerateImplPromise::CreateAndReject(
                             MakeRefPtr<MediaMgrError>(
                                 MediaMgrError::Name::AbortError),
                             __func__);
                       }
 
                       // If we fetched any real cameras or mics, remove the
                       // "default" part of their IDs.
                       if (aVideoInputType == MediaSourceEnum::Camera &&
                           aAudioInputType == MediaSourceEnum::Microphone &&
                           (aVideoInputEnumType != DeviceEnumerationType::Fake ||
                            aAudioInputEnumType !=
                                DeviceEnumerationType::Fake)) {
                         mgr->mDeviceIDs.Clear();
-                        for (auto& device : *aDevices) {
+                        for (auto& device : *aOutDevices) {
                           nsString id;
                           device->GetId(id);
                           id.ReplaceSubstring(NS_LITERAL_STRING("default: "),
                                               NS_LITERAL_STRING(""));
                           if (!mgr->mDeviceIDs.Contains(id)) {
                             mgr->mDeviceIDs.AppendElement(id);
                           }
                         }
                       }
 
                       if (!mgr->IsWindowStillActive(aWindowId)) {
-                        return MediaDeviceSetPromise::CreateAndReject(
+                        return EnumerateImplPromise::CreateAndReject(
                             MakeRefPtr<MediaMgrError>(
                                 MediaMgrError::Name::AbortError),
                             __func__);
                       }
 
-                      MediaManager::AnonymizeDevices(*aDevices, aOriginKey);
-                      return MediaDeviceSetPromise::CreateAndResolve(
-                          std::move(aDevices), __func__);
+                      MediaManager::AnonymizeDevices(*aOutDevices, aOriginKey);
+                      return EnumerateImplPromise::CreateAndResolve(false,
+                                                                    __func__);
                     },
                     [](RefPtr<MediaMgrError>&& aError) {
-                      return MediaDeviceSetPromise::CreateAndReject(
+                      return EnumerateImplPromise::CreateAndReject(
                           std::move(aError), __func__);
                     });
           },
           [](nsresult rs) {
             NS_WARNING(
                 "EnumerateDevicesImpl failed to get Principal Key. Enumeration "
                 "will not continue.");
-            return MediaDeviceSetPromise::CreateAndReject(
+            return EnumerateImplPromise::CreateAndReject(
                 MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
                 __func__);
           });
 }
 
 RefPtr<MediaManager::MediaDeviceSetPromise> MediaManager::EnumerateDevices(
     nsPIDOMWindowInner* aWindow, dom::CallerType aCallerType) {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3122,26 +3108,27 @@ RefPtr<MediaManager::MediaDeviceSetPromi
       audioEnumerationType = DeviceEnumerationType::Fake;
     }
   }
 
   MediaSinkEnum audioOutputType = MediaSinkEnum::Other;
   if (Preferences::GetBool("media.setsinkid.enabled")) {
     audioOutputType = MediaSinkEnum::Speaker;
   }
+  auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
   return EnumerateDevicesImpl(windowId, MediaSourceEnum::Camera,
                               MediaSourceEnum::Microphone, audioOutputType,
-                              videoEnumerationType, audioEnumerationType)
+                              videoEnumerationType, audioEnumerationType,
+                              devices)
       ->Then(GetCurrentThreadSerialEventTarget(), __func__,
-             [windowListener,
-              sourceListener](RefPtr<MediaDeviceSetRefCnt>&& aDevices) {
+             [windowListener, sourceListener, devices](bool) {
                DebugOnly<bool> rv = windowListener->Remove(sourceListener);
                MOZ_ASSERT(rv);
-               return MediaDeviceSetPromise::CreateAndResolve(
-                   std::move(aDevices), __func__);
+               return MediaDeviceSetPromise::CreateAndResolve(devices,
+                                                              __func__);
              },
              [windowListener, sourceListener](RefPtr<MediaMgrError>&& aError) {
                // This may fail, if a new doc has been set the OnNavigation
                // method should have removed all previous active listeners.
                // Attempt to clean it here, just in case, but ignore the return
                // value.
                Unused << windowListener->Remove(sourceListener);
                return MediaDeviceSetPromise::CreateAndReject(std::move(aError),
@@ -3170,24 +3157,24 @@ RefPtr<SinkInfoPromise> MediaManager::Ge
     AddWindowID(windowId, windowListener);
   }
   // Create an inactive SourceListener to act as a placeholder, so the
   // window listener doesn't clean itself up until we're done.
   RefPtr<SourceListener> sourceListener = new SourceListener();
   windowListener->Register(sourceListener);
 
   bool isSecure = aWindow->IsSecureContext();
-
+  auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
   return EnumerateDevicesImpl(aWindow->WindowID(), MediaSourceEnum::Other,
                               MediaSourceEnum::Other, MediaSinkEnum::Speaker,
                               DeviceEnumerationType::Normal,
-                              DeviceEnumerationType::Normal)
+                              DeviceEnumerationType::Normal, devices)
       ->Then(GetCurrentThreadSerialEventTarget(), __func__,
-             [aDeviceId, isSecure](RefPtr<MediaDeviceSetRefCnt>&& aDevices) {
-               for (RefPtr<MediaDevice>& device : *aDevices) {
+             [aDeviceId, isSecure, devices](bool) {
+               for (RefPtr<MediaDevice>& device : *devices) {
                  if (aDeviceId.IsEmpty() && device->mSinkInfo->Preferred()) {
                    return SinkInfoPromise::CreateAndResolve(device->mSinkInfo,
                                                             __func__);
                  }
                  if (device->mID.Equals(aDeviceId)) {
                    // TODO: Check if the application is authorized to play audio
                    // through this device (Bug 1493982).
                    if (isSecure || device->mSinkInfo->Preferred()) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -212,17 +212,19 @@ class MediaManager final : public nsIMed
 
   typedef nsTArray<RefPtr<MediaDevice>> MediaDeviceSet;
   typedef media::Refcountable<MediaDeviceSet> MediaDeviceSetRefCnt;
 
   typedef MozPromise<RefPtr<DOMMediaStream>, RefPtr<MediaMgrError>, true>
       StreamPromise;
   typedef MozPromise<RefPtr<MediaDeviceSetRefCnt>, RefPtr<MediaMgrError>, true>
       MediaDeviceSetPromise;
-  typedef MozPromise<const char*, nsresult, false> BadConstraintsPromise;
+  typedef MozPromise<bool, RefPtr<MediaMgrError>, true> EnumerateImplPromise;
+  typedef MozPromise<const char*, RefPtr<MediaMgrError>, true>
+      BadConstraintsPromise;
 
   RefPtr<StreamPromise> GetUserMedia(
       nsPIDOMWindowInner* aWindow,
       const dom::MediaStreamConstraints& aConstraints,
       dom::CallerType aCallerType);
 
   nsresult GetUserMediaDevices(
       nsPIDOMWindowInner* aWindow,
@@ -275,28 +277,29 @@ class MediaManager final : public nsIMed
  private:
   enum class DeviceEnumerationType : uint8_t {
     Normal,  // Enumeration should not return loopback or fake devices
     Fake,    // Enumeration should return fake device(s)
     Loopback /* Enumeration should return loopback device(s) (possibly in
              addition to normal devices) */
   };
 
-  RefPtr<MediaDeviceSetPromise> EnumerateRawDevices(
-      uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType,
-      dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
-      DeviceEnumerationType aVideoInputEnumType = DeviceEnumerationType::Normal,
-      DeviceEnumerationType aAudioInputEnumType =
-          DeviceEnumerationType::Normal);
-
-  RefPtr<MediaDeviceSetPromise> EnumerateDevicesImpl(
+  RefPtr<EnumerateImplPromise> EnumerateRawDevices(
       uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType,
       dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
       DeviceEnumerationType aVideoInputEnumType,
-      DeviceEnumerationType aAudioInputEnumType);
+      DeviceEnumerationType aAudioInputEnumType,
+      const RefPtr<MediaDeviceSetRefCnt>& aOutDevices);
+
+  RefPtr<EnumerateImplPromise> EnumerateDevicesImpl(
+      uint64_t aWindowId, dom::MediaSourceEnum aVideoInputType,
+      dom::MediaSourceEnum aAudioInputType, MediaSinkEnum aAudioOutputType,
+      DeviceEnumerationType aVideoInputEnumType,
+      DeviceEnumerationType aAudioInputEnumType,
+      const RefPtr<MediaDeviceSetRefCnt>& aOutDevices);
 
   RefPtr<BadConstraintsPromise> SelectSettings(
       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,