--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1859,67 +1859,81 @@ MediaManager::EnumerateRawDevices(uint64
MediaSourceEnum aVideoType,
MediaSourceEnum aAudioType,
DeviceEnumerationType aVideoEnumType /* = Normal */,
DeviceEnumerationType aAudioEnumType /* = Normal */)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aVideoType != MediaSourceEnum::Other ||
aAudioType != MediaSourceEnum::Other);
+ // Since the enums can take one of several values, the following asserts rely
+ // on short circuting behavior. E.g. aVideoEnumType != Fake will be true if
+ // the requested device is not fake and thus the assert will pass. However,
+ // if the device is fake, aVideoType == MediaSourceEnum::Camera will be
+ // checked as well, ensuring that fake devices are of the camera type.
+ MOZ_ASSERT(aVideoEnumType != Fake || aVideoType == MediaSourceEnum::Camera,
+ "If fake cams are requested video type should be camera!");
+ MOZ_ASSERT(aVideoEnumType != Loopback || aVideoType == MediaSourceEnum::Camera,
+ "If loopback video is requested video type should be camera!");
+ MOZ_ASSERT(aAudioEnumType != Fake || aAudioType == MediaSourceEnum::Microphone,
+ "If fake mics are requested audio type should be microphone!");
+ MOZ_ASSERT(aAudioEnumType != Loopback || aAudioType == MediaSourceEnum::Microphone,
+ "If loopback audio is requested audio type should be microphone!");
+
RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
uint32_t id = mOutstandingPledges.Append(*p);
- nsAutoCString audioLoopDev, videoLoopDev;
- if (aVideoEnumType != Fake && aAudioEnumType != Fake) {
- // Fake stream not requested. The entire device stack is available.
- // Loop in loopback devices if they are set, and their respective type is
- // requested. This is currently used for automated media tests only.
- if (aVideoType == MediaSourceEnum::Camera) {
- Preferences::GetCString("media.video_loopback_dev", videoLoopDev);
- }
- if (aAudioType == MediaSourceEnum::Microphone) {
- Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
- }
- }
-
bool hasVideo = aVideoType != MediaSourceEnum::Other;
bool hasAudio = aAudioType != MediaSourceEnum::Other;
- bool fakeCams = aVideoEnumType == Fake && aVideoType == MediaSourceEnum::Camera;
- bool fakeMics = aAudioEnumType == Fake && aAudioType == MediaSourceEnum::Microphone;
- bool realDevicesRequested = (!fakeCams && hasVideo) || (!fakeMics && hasAudio);
-
- RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, audioLoopDev,
- videoLoopDev, aVideoType,
- aAudioType, hasVideo, hasAudio,
- fakeCams, fakeMics, realDevicesRequested]() {
+
+ // True of at least one of video or audio is a fake device
+ bool fakeDeviceRequested = (aVideoEnumType == Fake && hasVideo) ||
+ (aAudioEnumType == Fake && hasAudio);
+ // True if at least one of video or audio is a real device
+ bool realDeviceRequested = (aVideoEnumType != Fake && hasVideo) ||
+ (aAudioEnumType != Fake && hasAudio);
+
+ nsAutoCString videoLoopDev, audioLoopDev;
+ if (hasVideo && aVideoEnumType == Loopback) {
+ Preferences::GetCString("media.video_loopback_dev", videoLoopDev);
+ }
+ if (hasAudio && aAudioEnumType == Loopback) {
+ Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
+ }
+
+ RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, aVideoType, aAudioType,
+ aVideoEnumType, aAudioEnumType,
+ videoLoopDev, audioLoopDev,
+ hasVideo, hasAudio, fakeDeviceRequested,
+ realDeviceRequested]() {
// Only enumerate what's asked for, and only fake cams and mics.
RefPtr<MediaEngine> fakeBackend, realBackend;
- if (fakeCams || fakeMics) {
+ if (fakeDeviceRequested) {
fakeBackend = new MediaEngineDefault();
}
- if (realDevicesRequested) {
+ if (realDeviceRequested) {
MediaManager* manager = MediaManager::GetIfExists();
MOZ_RELEASE_ASSERT(manager); // Must exist while media thread is alive
realBackend = manager->GetBackend(aWindowId);
}
auto result = MakeUnique<SourceSet>();
if (hasVideo) {
- nsTArray<RefPtr<MediaDevice>> videos;
- GetSources(fakeCams? fakeBackend : realBackend, aWindowId, aVideoType,
- videos, videoLoopDev.get());
+ SourceSet videos;
+ GetSources(aVideoEnumType == Fake ? fakeBackend : realBackend,
+ aWindowId, aVideoType, videos, videoLoopDev.get());
for (auto& source : videos) {
result->AppendElement(source);
}
}
if (hasAudio) {
- nsTArray<RefPtr<MediaDevice>> audios;
- GetSources(fakeMics? fakeBackend : realBackend, aWindowId, aAudioType,
- audios, audioLoopDev.get());
+ SourceSet audios;
+ GetSources(aAudioEnumType == Fake ? fakeBackend : realBackend,
+ aWindowId, aAudioType, audios, audioLoopDev.get());
for (auto& source : audios) {
result->AppendElement(source);
}
}
NS_DispatchToMainThread(NewRunnableFrom([id, result = Move(result)]() mutable {
MediaManager* mgr = MediaManager::GetIfExists();
if (!mgr) {
return NS_OK;
@@ -1927,17 +1941,17 @@ MediaManager::EnumerateRawDevices(uint64
RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
if (p) {
p->Resolve(result.release());
}
return NS_OK;
}));
});
- if (realDevicesRequested &&
+ 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 =
(aVideoType != MediaSourceEnum::Camera) ? u"audio" :
(aAudioType != MediaSourceEnum::Microphone) ? u"video" :
u"all";
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -2720,33 +2734,57 @@ MediaManager::GetUserMedia(nsPIDOMWindow
// Get list of all devices, with origin-specific device ids.
MediaEnginePrefs prefs = mPrefs;
nsString callID;
rv = GenerateUUID(callID);
NS_ENSURE_SUCCESS(rv, rv);
- bool fake = c.mFake.WasPassed()? c.mFake.Value() :
- Preferences::GetBool("media.navigator.streams.fake");
-
bool hasVideo = videoType != MediaSourceEnum::Other;
bool hasAudio = audioType != MediaSourceEnum::Other;
- bool fakeCams = fake && videoType == MediaSourceEnum::Camera;
- bool fakeMics = fake && audioType == MediaSourceEnum::Microphone;
- bool realDevicesRequested = (!fakeCams && hasVideo) || (!fakeMics && hasAudio);
-
+ DeviceEnumerationType videoEnumerationType = Normal;
+ DeviceEnumerationType audioEnumerationType = Normal;
+
+ // Handle loopback and fake requests. For gUM we don't consider resist
+ // fingerprinting as users should be prompted anyway.
+ bool wantFakes = c.mFake.WasPassed() ? c.mFake.Value() :
+ Preferences::GetBool("media.navigator.streams.fake");
+ nsAutoCString videoLoopDev, audioLoopDev;
+ // Video
+ if (videoType == MediaSourceEnum::Camera) {
+ Preferences::GetCString("media.video_loopback_dev", videoLoopDev);
+ // Loopback prefs take precedence over fake prefs
+ if (!videoLoopDev.IsEmpty()) {
+ videoEnumerationType = Loopback;
+ } else if (wantFakes) {
+ videoEnumerationType = Fake;
+ }
+ }
+ // Audio
+ if (audioType == MediaSourceEnum::Microphone) {
+ Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
+ // Loopback prefs take precedence over fake prefs
+ if (!audioLoopDev.IsEmpty()) {
+ audioEnumerationType = Loopback;
+ } else if (wantFakes) {
+ audioEnumerationType = Fake;
+ }
+ }
+
+ bool realDevicesRequested = (videoEnumerationType != Fake && hasVideo) ||
+ (audioEnumerationType != Fake && hasAudio);
bool askPermission =
(!privileged || Preferences::GetBool("media.navigator.permission.force")) &&
(realDevicesRequested || Preferences::GetBool("media.navigator.permission.fake"));
RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
audioType,
- fake ? Fake : Normal,
- fake ? Fake : Normal);
+ videoEnumerationType,
+ audioEnumerationType);
RefPtr<MediaManager> self = this;
p->Then([self, onSuccess, onFailure, windowID, c, windowListener,
sourceListener, askPermission, prefs, isHTTPS, isHandlingUserInput,
callID, principalInfo, isChrome, resistFingerprinting](SourceSet*& aDevices) mutable {
LOG(("GetUserMedia: post enumeration pledge success callback starting"));
// grab result
auto devices = MakeRefPtr<Refcountable<UniquePtr<SourceSet>>>(aDevices);
@@ -3078,24 +3116,51 @@ MediaManager::EnumerateDevices(nsPIDOMWi
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 fake = Preferences::GetBool("media.navigator.streams.fake") ||
- nsContentUtils::ResistFingerprinting(aCallerType);
+ DeviceEnumerationType videoEnumerationType = Normal;
+ DeviceEnumerationType audioEnumerationType = Normal;
+ bool resistFingerprinting = nsContentUtils::ResistFingerprinting(aCallerType);
+
+ // In order of precedence: resist fingerprinting > loopback > fake pref
+ if (resistFingerprinting) {
+ videoEnumerationType = Fake;
+ audioEnumerationType = Fake;
+ } else {
+ // Handle loopback and fake requests
+ nsAutoCString videoLoopDev, audioLoopDev;
+ bool wantFakes = Preferences::GetBool("media.navigator.streams.fake");
+ // Video
+ Preferences::GetCString("media.video_loopback_dev", videoLoopDev);
+ // Loopback prefs take precedence over fake prefs
+ if (!videoLoopDev.IsEmpty()) {
+ videoEnumerationType = Loopback;
+ } else if (wantFakes) {
+ videoEnumerationType = Fake;
+ }
+ // Audio
+ Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
+ // Loopback prefs take precedence over fake prefs
+ if (!audioLoopDev.IsEmpty()) {
+ audioEnumerationType = Loopback;
+ } else if (wantFakes) {
+ audioEnumerationType = Fake;
+ }
+ }
RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId,
MediaSourceEnum::Camera,
MediaSourceEnum::Microphone,
- fake ? Fake : Normal,
- fake ? Fake : Normal);
+ videoEnumerationType,
+ audioEnumerationType);
p->Then([onSuccess, windowListener, sourceListener](SourceSet*& aDevices) mutable {
UniquePtr<SourceSet> devices(aDevices); // grab result
DebugOnly<bool> rv = windowListener->Remove(sourceListener);
MOZ_ASSERT(rv);
nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
onSuccess->OnSuccess(array);
}, [onFailure, windowListener, sourceListener](MediaStreamError*& reason) mutable {
DebugOnly<bool> rv = windowListener->Remove(sourceListener);