--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2165,16 +2165,39 @@ enum class GetUserMediaSecurityState {
File = 2,
App = 3,
Localhost = 4,
Loop = 5,
Privileged = 6
};
/**
+ * This function is used in getUserMedia when privacy.resistFingerprinting is true.
+ * Only mediaSource of audio/video constraint will be kept.
+ */
+static void
+ReduceConstraint(
+ mozilla::dom::OwningBooleanOrMediaTrackConstraints& aConstraint) {
+ // Not requesting stream.
+ if (!IsOn(aConstraint)) {
+ return;
+ }
+
+ // It looks like {audio: true}, do nothing.
+ if (!aConstraint.IsMediaTrackConstraints()) {
+ return;
+ }
+
+ // Keep mediaSource, ignore all other constraints.
+ auto& c = aConstraint.GetAsMediaTrackConstraints();
+ nsString mediaSource = c.mMediaSource;
+ 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(nsPIDOMWindowInner* aWindow,
const MediaStreamConstraints& aConstraintsPassedIn,
nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
@@ -2275,16 +2298,23 @@ MediaManager::GetUserMedia(nsPIDOMWindow
// 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;
}
+ const bool resistFingerprinting = nsContentUtils::ResistFingerprinting(aCallerType);
+
+ if (resistFingerprinting) {
+ ReduceConstraint(c.mVideo);
+ ReduceConstraint(c.mAudio);
+ }
+
if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
c.mVideo.SetAsBoolean() = false;
}
MediaSourceEnum videoType = MediaSourceEnum::Other; // none
MediaSourceEnum audioType = MediaSourceEnum::Other; // none
if (c.mVideo.IsMediaTrackConstraints()) {
@@ -2504,31 +2534,31 @@ MediaManager::GetUserMedia(nsPIDOMWindow
(!privileged || Preferences::GetBool("media.navigator.permission.force")) &&
(!fake || Preferences::GetBool("media.navigator.permission.fake"));
RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
audioType, fake);
RefPtr<MediaManager> self = this;
p->Then([self, onSuccess, onFailure, windowID, c, windowListener,
sourceListener, askPermission, prefs, isHTTPS, callID, principalInfo,
- isChrome](SourceSet*& aDevices) mutable {
+ isChrome, resistFingerprinting](SourceSet*& aDevices) mutable {
// grab result
auto devices = MakeRefPtr<Refcountable<UniquePtr<SourceSet>>>(aDevices);
// Ensure that our windowID is still good.
if (!nsGlobalWindowInner::GetInnerWindowWithId(windowID)) {
return;
}
// Apply any constraints. This modifies the passed-in list.
RefPtr<PledgeChar> p2 = self->SelectSettings(c, isChrome, devices);
p2->Then([self, onSuccess, onFailure, windowID, c,
windowListener, sourceListener, askPermission, prefs, isHTTPS,
- callID, principalInfo, isChrome, devices
+ callID, principalInfo, isChrome, devices, resistFingerprinting
](const char*& badConstraint) mutable {
// Ensure that the captured 'this' pointer and our windowID are still good.
auto* globalWindow = nsGlobalWindowInner::GetInnerWindowWithId(windowID);
RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner()
: nullptr;
if (!MediaManager::Exists() || !window) {
return;
@@ -2542,17 +2572,23 @@ MediaManager::GetUserMedia(nsPIDOMWindow
NS_LITERAL_STRING("OverconstrainedError"),
NS_LITERAL_STRING(""),
constraint);
onFailure->OnError(error);
return;
}
if (!(*devices)->Length()) {
RefPtr<MediaStreamError> error =
- new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
+ new MediaStreamError(
+ window,
+ // When privacy.resistFingerprinting = true, no available
+ // device implies content script is requesting a fake
+ // device, so report NotAllowedError.
+ resistFingerprinting ? NS_LITERAL_STRING("NotAllowedError")
+ : NS_LITERAL_STRING("NotFoundError"));
onFailure->OnError(error);
return;
}
nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create(); // before we give up devices below
if (!askPermission) {
for (auto& device : **devices) {
nsresult rv = devicesCopy->AppendElement(device);