Bug 1372073 - Ignore constraints in getUserMedia. r=arthuredelstein,jib
authorChung-Sheng Fu <cfu@mozilla.com>
Mon, 18 Sep 2017 17:51:51 +0800
changeset 436086 f995ab0441e92a820d19015d8c31a927749761c8
parent 436085 018f84af573b1df08a10fbf8f63859c5def4d385
child 436087 2133a8484ef07a58bcdcd8cae0b60f9eb819b035
push id117
push userfmarier@mozilla.com
push dateTue, 28 Nov 2017 20:17:16 +0000
reviewersarthuredelstein, jib
bugs1372073
milestone59.0a1
Bug 1372073 - Ignore constraints in getUserMedia. r=arthuredelstein,jib MozReview-Commit-ID: KdA9abmg3UA
dom/media/MediaManager.cpp
--- 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);