Bug 1475209 - Have MediaManager::EnumerateDevices() return a promise. r=achronop,jya
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 30 Nov 2018 05:14:05 +0000
changeset 505355 a616c269bbccf381c45e6909417819103179a67a
parent 505354 46c936ce7efed56027d7770690f94ee31f415d7a
child 505356 116803bacea74c7164b1cd9be3d10d3c185c09b7
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersachronop, jya
bugs1475209
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 1475209 - Have MediaManager::EnumerateDevices() return a promise. r=achronop,jya Differential Revision: https://phabricator.services.mozilla.com/D8009
dom/media/MediaDevices.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -43,114 +43,23 @@ class FuzzTimerCallBack final : public n
   }
 
  private:
   nsCOMPtr<MediaDevices> mMediaDevices;
 };
 
 NS_IMPL_ISUPPORTS(FuzzTimerCallBack, nsITimerCallback, nsINamed)
 
-class MediaDevices::EnumDevResolver
-    : public nsIGetUserMediaDevicesSuccessCallback {
- public:
-  NS_DECL_ISUPPORTS
-
-  EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
-      : mPromise(aPromise), mWindowId(aWindowId) {}
-
-  NS_IMETHOD
-  OnSuccess(nsIVariant* aDevices) override {
-    // Create array for nsIMediaDevice
-    nsTArray<nsCOMPtr<nsIMediaDevice>> devices;
-    // Contain the fumes
-    {
-      uint16_t vtype = aDevices->GetDataType();
-      if (vtype != nsIDataType::VTYPE_EMPTY_ARRAY) {
-        nsIID elementIID;
-        uint16_t elementType;
-        void* rawArray;
-        uint32_t arrayLen;
-        nsresult rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen,
-                                           &rawArray);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (elementType != nsIDataType::VTYPE_INTERFACE) {
-          free(rawArray);
-          return NS_ERROR_FAILURE;
-        }
-
-        nsISupports** supportsArray = reinterpret_cast<nsISupports**>(rawArray);
-        for (uint32_t i = 0; i < arrayLen; ++i) {
-          nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
-          devices.AppendElement(device);
-          NS_IF_RELEASE(
-              supportsArray[i]);  // explicitly decrease refcount for rawptr
-        }
-        free(rawArray);  // explicitly free memory from nsIVariant::GetAsArray
-      }
-    }
-    nsTArray<RefPtr<MediaDeviceInfo>> infos;
-    for (auto& device : devices) {
-      MediaDeviceKind kind = static_cast<MediaDevice*>(device.get())->mKind;
-      MOZ_ASSERT(kind == dom::MediaDeviceKind::Audioinput ||
-                 kind == dom::MediaDeviceKind::Videoinput ||
-                 kind == dom::MediaDeviceKind::Audiooutput);
-      nsString id;
-      nsString name;
-      device->GetId(id);
-      // Include name only if page currently has a gUM stream active or
-      // persistent permissions (audio or video) have been granted
-      if (MediaManager::Get()->IsActivelyCapturingOrHasAPermission(mWindowId) ||
-          Preferences::GetBool("media.navigator.permission.disabled", false)) {
-        device->GetName(name);
-      }
-      RefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
-      infos.AppendElement(info);
-    }
-    mPromise->MaybeResolve(infos);
-    return NS_OK;
-  }
-
- private:
-  virtual ~EnumDevResolver() {}
-  RefPtr<Promise> mPromise;
-  uint64_t mWindowId;
-};
-
-class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback {
- public:
-  NS_DECL_ISUPPORTS
-
-  explicit GumRejecter(Promise* aPromise) : mPromise(aPromise) {}
-
-  NS_IMETHOD
-  OnError(nsISupports* aError) override {
-    RefPtr<MediaStreamError> error = do_QueryObject(aError);
-    if (!error) {
-      return NS_ERROR_FAILURE;
-    }
-    mPromise->MaybeReject(error);
-    return NS_OK;
-  }
-
- private:
-  virtual ~GumRejecter() {}
-  RefPtr<Promise> mPromise;
-};
-
 MediaDevices::~MediaDevices() {
   MediaManager* mediamanager = MediaManager::GetIfExists();
   if (mediamanager) {
     mediamanager->RemoveDeviceChangeCallback(this);
   }
 }
 
-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);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   RefPtr<MediaDevices> self(this);
@@ -169,25 +78,56 @@ already_AddRefed<Promise> MediaDevices::
                }
                p->MaybeReject(error);
              });
   return p.forget();
 }
 
 already_AddRefed<Promise> MediaDevices::EnumerateDevices(CallerType aCallerType,
                                                          ErrorResult& aRv) {
+  MOZ_ASSERT(NS_IsMainThread());
   RefPtr<Promise> p = Promise::Create(GetParentObject(), aRv);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-
-  RefPtr<EnumDevResolver> resolver =
-      new EnumDevResolver(p, GetOwner()->WindowID());
-  RefPtr<GumRejecter> rejecter = new GumRejecter(p);
-
-  aRv = MediaManager::Get()->EnumerateDevices(GetOwner(), resolver, rejecter,
-                                              aCallerType);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+  RefPtr<MediaDevices> self(this);
+  MediaManager::Get()
+      ->EnumerateDevices(GetOwner(), aCallerType)
+      ->Then(GetCurrentThreadSerialEventTarget(), __func__,
+             [this, self,
+              p](RefPtr<MediaManager::MediaDeviceSetRefCnt>&& aDevices) {
+               if (NS_FAILED(CheckInnerWindowCorrectness())) {
+                 return;  // Leave Promise pending after navigation by design.
+               }
+               auto windowId = GetOwner()->WindowID();
+               nsTArray<RefPtr<MediaDeviceInfo>> infos;
+               for (auto& device : **aDevices) {
+                 MOZ_ASSERT(device->mKind == dom::MediaDeviceKind::Audioinput ||
+                            device->mKind == dom::MediaDeviceKind::Videoinput ||
+                            device->mKind == dom::MediaDeviceKind::Audiooutput);
+                 // Include name only if page currently has a gUM stream active
+                 // or persistent permissions (audio or video) have been granted
+                 nsString label;
+                 if (MediaManager::Get()->IsActivelyCapturingOrHasAPermission(
+                         windowId) ||
+                     Preferences::GetBool("media.navigator.permission.disabled",
+                                          false)) {
+                   label = device->mName;
+                 }
+                 infos.AppendElement(MakeRefPtr<MediaDeviceInfo>(
+                     device->mID, device->mKind, label));
+               }
+               p->MaybeResolve(std::move(infos));
+             },
+             [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();
 }
 
 NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN(MediaDevices)
   NS_INTERFACE_MAP_ENTRY(MediaDevices)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -212,17 +212,16 @@ using dom::MediaTrackConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using dom::OwningStringOrStringSequence;
 using dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters;
 using dom::Promise;
 using dom::Sequence;
 using media::NewRunnableFrom;
 using media::NewTaskFrom;
-using media::Pledge;
 using media::Refcountable;
 
 static Atomic<bool> sHasShutdown;
 
 class SourceTrackListener;
 
 struct DeviceState {
   DeviceState(const RefPtr<MediaDevice>& aDevice, bool aOffWhileDisabled,
@@ -1512,22 +1511,16 @@ static void GetMediaDevices(MediaEngine*
       for (auto& device : devices) {
         LOG(("%s: appending device=%s", __func__,
              NS_ConvertUTF16toUTF8(device->mName).get()));
       }
     }
   }
 }
 
-// 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(
     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.
 
@@ -3158,17 +3151,17 @@ RefPtr<MediaManager::MediaDeviceSetPromi
                             nsGlobalWindowInner::GetInnerWindowWithId(aWindowId)
                                 ->AsInner();
                         return MediaDeviceSetPromise::CreateAndReject(
                             MakeRefPtr<MediaStreamError>(
                                 window, MediaStreamError::Name::AbortError),
                             __func__);
                       }
 
-                      MediaManager_AnonymizeDevices(**aDevices, aOriginKey);
+                      MediaManager::AnonymizeDevices(**aDevices, aOriginKey);
                       return MediaDeviceSetPromise::CreateAndResolve(
                           std::move(aDevices), __func__);
                     },
                     [](RefPtr<MediaStreamError>&& reason) {
                       return MediaDeviceSetPromise::CreateAndReject(
                           std::move(reason), __func__);
                     });
           },
@@ -3180,24 +3173,26 @@ RefPtr<MediaManager::MediaDeviceSetPromi
                 nsGlobalWindowInner::GetInnerWindowWithId(aWindowId)->AsInner();
             return MediaDeviceSetPromise::CreateAndReject(
                 MakeRefPtr<MediaStreamError>(
                     window, MediaStreamError::Name::AbortError),
                 __func__);
           });
 }
 
-nsresult MediaManager::EnumerateDevices(
-    nsPIDOMWindowInner* aWindow,
-    nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
-    nsIDOMGetUserMediaErrorCallback* aOnFailure, dom::CallerType aCallerType) {
+RefPtr<MediaManager::MediaDeviceSetPromise> MediaManager::EnumerateDevices(
+    nsPIDOMWindowInner* aWindow, dom::CallerType aCallerType) {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_TRUE(!sHasShutdown, NS_ERROR_FAILURE);
-  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
+  if (sHasShutdown) {
+    return MediaDeviceSetPromise::CreateAndReject(
+        MakeRefPtr<MediaStreamError>(aWindow,
+                                     MediaStreamError::Name::AbortError,
+                                     NS_LITERAL_STRING("In shutdown")),
+        __func__);
+  }
   uint64_t windowId = aWindow->WindowID();
 
   nsIPrincipal* principal = aWindow->GetExtantDoc()->NodePrincipal();
 
   RefPtr<GetUserMediaWindowListener> windowListener =
       GetWindowListener(windowId);
   if (windowListener) {
     PrincipalHandle existingPrincipalHandle =
@@ -3243,43 +3238,36 @@ nsresult MediaManager::EnumerateDevices(
       audioEnumerationType = DeviceEnumerationType::Fake;
     }
   }
 
   MediaSinkEnum audioOutputType = MediaSinkEnum::Other;
   if (Preferences::GetBool("media.setsinkid.enabled")) {
     audioOutputType = MediaSinkEnum::Speaker;
   }
-  RefPtr<MediaDeviceSetPromise> p = EnumerateDevicesImpl(
-      windowId, MediaSourceEnum::Camera, MediaSourceEnum::Microphone,
-      audioOutputType, videoEnumerationType, audioEnumerationType);
-  p->Then(GetCurrentThreadSerialEventTarget(), __func__,
-          [onSuccess, windowListener,
-           sourceListener](RefPtr<MediaDeviceSetRefCnt>&& aDevices) mutable {
+  return EnumerateDevicesImpl(windowId, MediaSourceEnum::Camera,
+                              MediaSourceEnum::Microphone, audioOutputType,
+                              videoEnumerationType, audioEnumerationType)
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [windowListener,
+           sourceListener](RefPtr<MediaDeviceSetRefCnt>&& aDevices) {
             DebugOnly<bool> rv = windowListener->Remove(sourceListener);
             MOZ_ASSERT(rv);
-            nsCOMPtr<nsIWritableVariant> array =
-                MediaManager_ToJSArray(**aDevices);
-            onSuccess->OnSuccess(array);
+            return MediaDeviceSetPromise::CreateAndResolve(std::move(aDevices),
+                                                           __func__);
           },
-          [onFailure, windowListener, sourceListener,
-           windowId](RefPtr<MediaStreamError>&& reason) mutable {
-            MediaManager* mgr = MediaManager::GetIfExists();
-            if (!mgr || !mgr->IsWindowStillActive(windowId)) {
-              // If an error happened, like navigate away
-              // leave the promise pending.
-              return;
-            }
+          [windowListener, sourceListener](RefPtr<MediaStreamError>&& error) {
             // 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.
-            windowListener->Remove(sourceListener);
-            onFailure->OnError(reason);
+            Unused << windowListener->Remove(sourceListener);
+            return MediaDeviceSetPromise::CreateAndReject(std::move(error),
+                                                          __func__);
           });
-  return NS_OK;
 }
 
 RefPtr<SinkInfoPromise> MediaManager::GetSinkDevice(nsPIDOMWindowInner* aWindow,
                                                     const nsString& aDeviceId) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   // We have to add the window id here because enumerate methods
@@ -3356,17 +3344,17 @@ nsresult MediaManager::GetUserMediaDevic
     return NS_ERROR_UNEXPECTED;
   }
 
   for (auto& callID : *callIDs) {
     RefPtr<GetUserMediaTask> task;
     if (!aCallID.Length() || aCallID == callID) {
       if (mActiveCallbacks.Get(callID, getter_AddRefs(task))) {
         nsCOMPtr<nsIWritableVariant> array =
-            MediaManager_ToJSArray(*task->mMediaDeviceSet);
+            MediaManager::ToJSArray(*task->mMediaDeviceSet);
         aOnSuccess.Call(array);
         return NS_OK;
       }
     }
   }
   return NS_ERROR_UNEXPECTED;
 }
 
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -207,35 +207,39 @@ class MediaManager final : public nsIMed
                                     nsIDOMGetUserMediaErrorCallback>
       GetUserMediaErrorCallback;
 
   static void CallOnError(const GetUserMediaErrorCallback* aCallback,
                           dom::MediaStreamError& aError);
   static void CallOnSuccess(const GetUserMediaSuccessCallback* aCallback,
                             DOMMediaStream& aStream);
 
+  typedef nsTArray<RefPtr<MediaDevice>> MediaDeviceSet;
+  typedef media::Refcountable<UniquePtr<MediaDeviceSet>> MediaDeviceSetRefCnt;
+
   typedef MozPromise<RefPtr<DOMMediaStream>, RefPtr<dom::MediaStreamError>,
                      true>
       StreamPromise;
+  typedef MozPromise<RefPtr<MediaDeviceSetRefCnt>,
+                     RefPtr<dom::MediaStreamError>, true>
+      MediaDeviceSetPromise;
+  typedef MozPromise<const char*, nsresult, false> BadConstraintsPromise;
 
   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,
-                            nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
-                            nsIDOMGetUserMediaErrorCallback* aOnFailure,
-                            dom::CallerType aCallerType);
+  RefPtr<MediaDeviceSetPromise> EnumerateDevices(nsPIDOMWindowInner* aWindow,
+                                                 dom::CallerType aCallerType);
 
   nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow,
                             dom::Promise& aPromise);
 
   // Get the sink that corresponds to the given device id.
   // It is resposible to check if an application is
   // authorized to play audio through the requested device.
   // The returned promise will be resolved with the device
@@ -254,19 +258,16 @@ class MediaManager final : public nsIMed
   RefPtr<SinkInfoPromise> GetSinkDevice(nsPIDOMWindowInner* aWindow,
                                         const nsString& aDeviceId);
 
   void OnNavigation(uint64_t aWindowID);
   bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
-  typedef nsTArray<RefPtr<MediaDevice>> MediaDeviceSet;
-  typedef media::Refcountable<UniquePtr<MediaDeviceSet>> MediaDeviceSetRefCnt;
-
   virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
   virtual void OnDeviceChange() override;
 
  private:
   static nsresult GenerateUUID(nsAString& aResult);
   static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
 
  public:  // TODO: make private once we upgrade to GCC 4.8+ on linux.
@@ -278,21 +279,16 @@ 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) */
   };
 
-  typedef MozPromise<RefPtr<MediaDeviceSetRefCnt>,
-                     RefPtr<dom::MediaStreamError>, true>
-      MediaDeviceSetPromise;
-  typedef MozPromise<const char*, nsresult, false> BadConstraintsPromise;
-
   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(