Bug 1474376 - Merge window+screen sharing for content (list fullscreen as option in window-list), and get away from MediaSourceEnum. r=pehrsons,florian
authorJan-Ivar Bruaroey <jib@mozilla.com>
Thu, 27 Dec 2018 23:40:36 +0000
changeset 452019 9a4d3d6e59ae472f58d621b219f1b8488a224f92
parent 452018 623fa9efc4652d7d4d7386b70721b7ff844ca856
child 452020 00b2966950e09aaf2f9376119f79c8094ec52f36
push id35279
push userebalazs@mozilla.com
push dateFri, 28 Dec 2018 09:26:34 +0000
treeherdermozilla-central@a791cbab57a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons, florian
bugs1474376
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 1474376 - Merge window+screen sharing for content (list fullscreen as option in window-list), and get away from MediaSourceEnum. r=pehrsons,florian Differential Revision: https://phabricator.services.mozilla.com/D14941
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/webrtcUI.jsm
dom/media/MediaManager.cpp
dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
dom/media/webrtc/MediaEngineRemoteVideoSource.h
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/media/webrtc/MediaEngineWebRTC.h
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -725,25 +725,19 @@ getUserMedia.shareAudioCapture2.message = Will you allow %S to listen to this tab’s audio?
 # %S will be the 'learn more' link
 getUserMedia.shareScreenWarning.message = Only share screens with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %S
 # LOCALIZATION NOTE (getUserMedia.shareFirefoxWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
 # %1$S is brandShortName (eg. Firefox)
 # %2$S will be the 'learn more' link
 getUserMedia.shareFirefoxWarning.message = Only share %1$S with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %2$S
 # LOCALIZATION NOTE(getUserMedia.shareScreen.learnMoreLabel): NB: inserted via innerHTML, so please don't use <, > or & in this string.
 getUserMedia.shareScreen.learnMoreLabel = Learn More
-getUserMedia.selectWindow.label=Window to share:
-getUserMedia.selectWindow.accesskey=W
-getUserMedia.selectScreen.label=Screen to share:
-getUserMedia.selectScreen.accesskey=S
-getUserMedia.selectApplication.label=Application to share:
-getUserMedia.selectApplication.accesskey=A
-getUserMedia.pickApplication.label = Select Application
-getUserMedia.pickScreen.label = Select Screen
-getUserMedia.pickWindow.label = Select Window
+getUserMedia.selectWindowOrScreen.label=Window or Screen to share:
+getUserMedia.selectWindowOrScreen.accesskey=W
+getUserMedia.pickWindowOrScreen.label = Select Window or Screen
 getUserMedia.shareEntireScreen.label = Entire screen
 # LOCALIZATION NOTE (getUserMedia.shareMonitor.label):
 # %S is screen number (digits 1, 2, etc)
 # Example: Screen 1, Screen 2,..
 getUserMedia.shareMonitor.label = Screen %S
 # LOCALIZATION NOTE (getUserMedia.shareApplicationWindowCount.label):
 # Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -153,17 +153,17 @@ var webrtcUI = {
       return;
     }
     identityBox.click();
   },
 
   updateWarningLabel(aMenuList) {
     let type = aMenuList.selectedItem.getAttribute("devicetype");
     let document = aMenuList.ownerDocument;
-    document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
+    document.getElementById("webRTC-all-windows-shared").hidden = type != "screen";
   },
 
   // Add-ons can override stock permission behavior by doing:
   //
   //   webrtcUI.addPeerConnectionBlocker(function(aParams) {
   //     // new permission checking logic
   //   }));
   //
@@ -620,41 +620,38 @@ function prompt(aBrowser, aRequest) {
         if (!item || item.hasAttribute("disabled")) {
           notificationElement.setAttribute("invalidselection", "true");
         } else {
           notificationElement.removeAttribute("invalidselection");
         }
       }
 
       function listScreenShareDevices(menupopup, devices) {
-        while (menupopup.lastChild)
+        while (menupopup.lastChild) {
           menupopup.removeChild(menupopup.lastChild);
-
-        let type = devices[0].mediaSource;
-        let typeName = type.charAt(0).toUpperCase() + type.substr(1);
-
+        }
         let label = doc.getElementById("webRTC-selectWindow-label");
-        let gumStringId = "getUserMedia.select" + typeName;
+        const gumStringId = "getUserMedia.selectWindowOrScreen";
         label.setAttribute("value",
                            stringBundle.getString(gumStringId + ".label"));
         label.setAttribute("accesskey",
                            stringBundle.getString(gumStringId + ".accesskey"));
 
-        // "Select <type>" is the default because we can't pick a
-        // 'default' window to share.
+        // "Select a Window or Screen" is the default because we can't and don't
+        // want to pick a 'default' window to share (Full screen is "scary").
         addDeviceToList(menupopup,
-                        stringBundle.getString("getUserMedia.pick" + typeName + ".label"),
+                        stringBundle.getString("getUserMedia.pickWindowOrScreen.label"),
                         "-1");
         menupopup.appendChild(doc.createXULElement("menuseparator"));
 
         // Build the list of 'devices'.
         let monitorIndex = 1;
         for (let i = 0; i < devices.length; ++i) {
           let device = devices[i];
-
+          let type = device.mediaSource;
           let name;
           // Building screen list from available screens.
           if (type == "screen") {
             if (device.name == "Primary Monitor") {
               name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
             } else {
               name = stringBundle.getFormattedString("getUserMedia.shareMonitor.label",
                                                      [monitorIndex]);
@@ -668,34 +665,36 @@ function prompt(aBrowser, aRequest) {
               let sepIndex = name.indexOf("\x1e");
               let count = name.slice(0, sepIndex);
               let sawcStringId = "getUserMedia.shareApplicationWindowCount.label";
               name = PluralForm.get(parseInt(count), stringBundle.getString(sawcStringId))
                                .replace("#1", name.slice(sepIndex + 1))
                                .replace("#2", count);
             }
           }
-          let item = addDeviceToList(menupopup, name, i, typeName);
+          let item = addDeviceToList(menupopup, name, i, type);
           item.deviceId = device.id;
+          item.mediaSource = type;
           if (device.scary)
             item.scary = true;
         }
 
         // Always re-select the "No <type>" item.
         doc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
         doc.getElementById("webRTC-all-windows-shared").hidden = true;
 
         menupopup._commandEventListener = event => {
           checkDisabledWindowMenuItem();
           let video = doc.getElementById("webRTC-previewVideo");
           if (video.stream) {
             video.stream.getTracks().forEach(t => t.stop());
             video.stream = null;
           }
 
+          let type = event.target.mediaSource;
           let deviceId = event.target.deviceId;
           if (deviceId == undefined) {
             doc.getElementById("webRTC-preview").hidden = true;
             video.src = null;
             return;
           }
 
           let scary = event.target.scary;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -903,30 +903,16 @@ MediaDevice::MediaDevice(const RefPtr<Me
 }
 
 uint32_t MediaDevice::GetBestFitnessDistance(
     const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
     bool aIsChrome) {
   MOZ_ASSERT(MediaManager::IsInMediaThread());
   MOZ_ASSERT(mSource);
 
-  nsString mediaSource;
-  GetMediaSource(mediaSource);
-
-  // This code is reused for audio, where the mediaSource constraint does
-  // not currently have a function, but because it defaults to "camera" in
-  // webidl, we ignore it for audio here.
-  if (!mediaSource.EqualsASCII("microphone")) {
-    for (const auto& constraint : aConstraintSets) {
-      if (constraint->mMediaSource.mIdeal.find(mediaSource) ==
-          constraint->mMediaSource.mIdeal.end()) {
-        return UINT32_MAX;
-      }
-    }
-  }
   // Forward request to underlying object to interrogate per-mode capabilities.
   // Pass in device's origin-specific id for deviceId constraint comparison.
   const nsString& id = aIsChrome ? mRawID : mID;
   return mSource->GetBestFitnessDistance(aConstraintSets, id);
 }
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName) {
@@ -2482,16 +2468,42 @@ RefPtr<MediaManager::StreamPromise> Medi
                                       MediaSourceEnum::Camera);
       for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
         if (cs.mMediaSource.EqualsASCII(unset)) {
           cs.mMediaSource = vc.mMediaSource;
         }
       }
     }
     if (!privileged) {
+      // Only allow privileged content to explicitly pick full-screen,
+      // application or tabsharing, since these modes are still available for
+      // testing. All others get "Window" (*) sharing.
+      //
+      // *) We overload "Window" with the new default getDisplayMedia spec-
+      // mandated behavior of not influencing user-choice, which we currently
+      // implement as a list containing BOTH windows AND screen(s).
+      //
+      // Notes on why we chose "Window" as the one to overload. Two reasons:
+      //
+      // 1. It's the closest logically & behaviorally (multi-choice, no default)
+      // 2. Screen is still useful in tests (implicit default is entire screen)
+      //
+      // For UX reasons we don't want "Entire Screen" to be the first/default
+      // choice (in our code first=default). It's a "scary" source that comes
+      // with complicated warnings on-top that would be confusing as the first
+      // thing people see, and also deserves to be listed as last resort for
+      // privacy reasons.
+
+      if (videoType == MediaSourceEnum::Screen ||
+          videoType == MediaSourceEnum::Application ||
+          videoType == MediaSourceEnum::Browser) {
+        videoType = MediaSourceEnum::Window;
+        vc.mMediaSource.AssignASCII(
+            EnumToASCII(dom::MediaSourceEnumValues::strings, videoType));
+      }
       // only allow privileged content to set the window id
       if (vc.mBrowserWindow.WasPassed()) {
         vc.mBrowserWindow.Value() = -1;
       }
       if (vc.mAdvanced.WasPassed()) {
         for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
           if (cs.mBrowserWindow.WasPassed()) {
             cs.mBrowserWindow.Value() = -1;
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -28,34 +28,48 @@ extern LazyLogModule gMediaManagerLog;
 using dom::ConstrainLongRange;
 using dom::MediaSourceEnum;
 using dom::MediaTrackConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::MediaTrackSettings;
 using dom::VideoFacingModeEnum;
 
 MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
-    int aIndex, camera::CaptureEngine aCapEngine, MediaSourceEnum aMediaSource,
-    bool aScary)
+    int aIndex, camera::CaptureEngine aCapEngine, bool aScary)
     : mCaptureIndex(aIndex),
-      mMediaSource(aMediaSource),
       mCapEngine(aCapEngine),
       mScary(aScary),
       mMutex("MediaEngineRemoteVideoSource::mMutex"),
       mRescalingBufferPool(/* zero_initialize */ false,
                            /* max_number_of_buffers */ 1),
       mSettingsUpdatedByFrame(MakeAndAddRef<media::Refcountable<AtomicBool>>()),
       mSettings(MakeAndAddRef<media::Refcountable<MediaTrackSettings>>()) {
-  MOZ_ASSERT(aMediaSource != MediaSourceEnum::Other);
   mSettings->mWidth.Construct(0);
   mSettings->mHeight.Construct(0);
   mSettings->mFrameRate.Construct(0);
   Init();
 }
 
+dom::MediaSourceEnum MediaEngineRemoteVideoSource::GetMediaSource() const {
+  switch (mCapEngine) {
+    case camera::BrowserEngine:
+      return MediaSourceEnum::Browser;
+    case camera::CameraEngine:
+      return MediaSourceEnum::Camera;
+    case camera::ScreenEngine:
+      return MediaSourceEnum::Screen;
+    case camera::AppEngine:
+      return MediaSourceEnum::Application;
+    case camera::WinEngine:
+      return MediaSourceEnum::Window;
+    default:
+      MOZ_CRASH();
+  }
+}
+
 void MediaEngineRemoteVideoSource::Init() {
   LOG(__PRETTY_FUNCTION__);
   AssertIsOnOwningThread();
 
   char deviceName[kMaxDeviceNameLength];
   char uniqueId[kMaxUniqueIdLength];
   if (camera::GetChildAndCall(&camera::CamerasChild::GetCaptureDevice,
                               mCapEngine, mCaptureIndex, deviceName,
@@ -292,21 +306,21 @@ nsresult MediaEngineRemoteVideoSource::S
     MutexAutoLock lock(mMutex);
     mState = kStopped;
     return NS_ERROR_FAILURE;
   }
 
   NS_DispatchToMainThread(NS_NewRunnableFunction(
       "MediaEngineRemoteVideoSource::SetLastCapability",
       [settings = mSettings, updated = mSettingsUpdatedByFrame,
-       source = mMediaSource, cap = mCapability]() mutable {
-        switch (source) {
-          case dom::MediaSourceEnum::Screen:
-          case dom::MediaSourceEnum::Window:
-          case dom::MediaSourceEnum::Application:
+       capEngine = mCapEngine, cap = mCapability]() mutable {
+        switch (capEngine) {
+          case camera::ScreenEngine:
+          case camera::WinEngine:
+          case camera::AppEngine:
             // Undo the hack where ideal and max constraints are crammed
             // together in mCapability for consumption by low-level code. We
             // don't actually know the real resolution yet, so report min(ideal,
             // max) for now.
             // TODO: This can be removed in bug 1453269.
             cap.width = std::min(cap.width >> 16, cap.width & 0xffff);
             cap.height = std::min(cap.height >> 16, cap.height & 0xffff);
             break;
@@ -534,20 +548,20 @@ int MediaEngineRemoteVideoSource::Delive
   // dst_max_height
   int32_t dst_width = std::min(
       req_ideal_width > 0 ? req_ideal_width : aProps.width(), dst_max_width);
   int32_t dst_height =
       std::min(req_ideal_height > 0 ? req_ideal_height : aProps.height(),
                dst_max_height);
 
   // Apply scaling for screen sharing, see bug 1453269.
-  switch (mMediaSource) {
-    case MediaSourceEnum::Screen:
-    case MediaSourceEnum::Window:
-    case MediaSourceEnum::Application: {
+  switch (mCapEngine) {
+    case camera::ScreenEngine:
+    case camera::WinEngine:
+    case camera::AppEngine: {
       // scale to average of portrait and landscape
       float scale_width = (float)dst_width / (float)aProps.width();
       float scale_height = (float)dst_height / (float)aProps.height();
       float scale = (scale_width + scale_height) / 2;
       dst_width = (int)(scale * target_width);
       dst_height = (int)(scale * target_height);
 
       // if scaled rectangle exceeds max rectangle, scale to minimum of portrait
@@ -792,20 +806,20 @@ bool MediaEngineRemoteVideoSource::Choos
     if (!aConstraints.mAdvanced.empty()) {
       LOG("Advanced array[%zu]:", aConstraints.mAdvanced.size());
       for (auto& advanced : aConstraints.mAdvanced) {
         MediaConstraintsHelper::LogConstraints(advanced);
       }
     }
   }
 
-  switch (mMediaSource) {
-    case MediaSourceEnum::Screen:
-    case MediaSourceEnum::Window:
-    case MediaSourceEnum::Application: {
+  switch (mCapEngine) {
+    case camera::ScreenEngine:
+    case camera::WinEngine:
+    case camera::AppEngine: {
       FlattenedConstraints c(aConstraints);
       // The actual resolution to constrain around is not easy to find ahead of
       // time (and may in fact change over time), so as a hack, we push ideal
       // and max constraints down to desktop_capture_impl.cc and finish the
       // algorithm there.
       // TODO: This can be removed in bug 1453269.
       aCapability.width =
           (std::min(0xffff, c.mWidth.mIdeal.valueOr(0)) & 0xffff) << 16 |
@@ -822,18 +836,17 @@ bool MediaEngineRemoteVideoSource::Choos
   }
 
   nsTArray<CapabilityCandidate> candidateSet;
   size_t num = NumCapabilities();
   for (size_t i = 0; i < num; i++) {
     candidateSet.AppendElement(CapabilityCandidate(GetCapability(i)));
   }
 
-  if (!mHardcodedCapabilities.IsEmpty() &&
-      mMediaSource == MediaSourceEnum::Camera) {
+  if (!mHardcodedCapabilities.IsEmpty() && mCapEngine == camera::CameraEngine) {
     // We have a hardcoded capability, which means this camera didn't report
     // discrete capabilities. It might still allow a ranged capability, so we
     // add a couple of default candidates based on prefs and constraints.
     // The chosen candidate will be propagated to StartCapture() which will fail
     // for an invalid candidate.
     MOZ_DIAGNOSTIC_ASSERT(mHardcodedCapabilities.Length() == 1);
     MOZ_DIAGNOSTIC_ASSERT(candidateSet.Length() == 1);
     candidateSet.Clear();
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h
@@ -105,24 +105,24 @@ class MediaEngineRemoteVideoSource : pub
   static void TrimLessFitCandidates(nsTArray<CapabilityCandidate>& aSet);
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) const override;
 
  public:
   MediaEngineRemoteVideoSource(int aIndex, camera::CaptureEngine aCapEngine,
-                               dom::MediaSourceEnum aMediaSource, bool aScary);
+                               bool aScary);
 
   // ExternalRenderer
   int DeliverFrame(uint8_t* buffer,
                    const camera::VideoFrameProperties& properties) override;
 
   // MediaEngineSource
-  dom::MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
+  dom::MediaSourceEnum GetMediaSource() const override;
   nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                     const MediaEnginePrefs& aPrefs, const nsString& aDeviceId,
                     const ipc::PrincipalInfo& aPrincipalInfo,
                     AllocationHandle** aOutHandle,
                     const char** aOutBadConstraint) override;
   nsresult Deallocate(const RefPtr<const AllocationHandle>& aHandle) override;
   nsresult SetTrack(const RefPtr<const AllocationHandle>& aHandle,
                     const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
@@ -169,19 +169,17 @@ class MediaEngineRemoteVideoSource : pub
   /**
    * Returns the capability with index `aIndex` for our assigned device.
    *
    * It is an error to call this with `aIndex >= NumCapabilities()`.
    */
   webrtc::CaptureCapability GetCapability(size_t aIndex) const;
 
   int mCaptureIndex;
-  const dom::MediaSourceEnum
-      mMediaSource;  // source of media (camera | application | screen)
-  const camera::CaptureEngine mCapEngine;
+  const camera::CaptureEngine mCapEngine;  // source of media (cam, screen etc)
   const bool mScary;
 
   // mMutex protects certain members on 3 threads:
   // MediaManager, Cameras IPC and MediaStreamGraph.
   Mutex mMutex;
 
   // Current state of this source.
   // Set under mMutex on the owning thread. Accessed under one of the two.
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -42,88 +42,63 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   camera::GetChildAndCall(&camera::CamerasChild::AddDeviceChangeCallback, this);
 }
 
 void MediaEngineWebRTC::SetFakeDeviceChangeEvents() {
   camera::GetChildAndCall(&camera::CamerasChild::SetFakeDeviceChangeEvents);
 }
 
 void MediaEngineWebRTC::EnumerateVideoDevices(
-    uint64_t aWindowId, dom::MediaSourceEnum aMediaSource,
+    uint64_t aWindowId, camera::CaptureEngine aCapEngine,
     nsTArray<RefPtr<MediaDevice>>* aDevices) {
   mMutex.AssertCurrentThreadOwns();
-
-  mozilla::camera::CaptureEngine capEngine = mozilla::camera::InvalidEngine;
-
-  bool scaryKind = false;  // flag sources with cross-origin exploit potential
-
-  switch (aMediaSource) {
-    case dom::MediaSourceEnum::Window:
-      capEngine = mozilla::camera::WinEngine;
-      break;
-    case dom::MediaSourceEnum::Application:
-      capEngine = mozilla::camera::AppEngine;
-      break;
-    case dom::MediaSourceEnum::Screen:
-      capEngine = mozilla::camera::ScreenEngine;
-      scaryKind = true;
-      break;
-    case dom::MediaSourceEnum::Browser:
-      capEngine = mozilla::camera::BrowserEngine;
-      scaryKind = true;
-      break;
-    case dom::MediaSourceEnum::Camera:
-      capEngine = mozilla::camera::CameraEngine;
-      break;
-    default:
-      MOZ_CRASH("No valid video engine");
-      break;
-  }
-
+  // flag sources with cross-origin exploit potential
+  bool scaryKind = (aCapEngine == camera::ScreenEngine ||
+                    aCapEngine == camera::BrowserEngine);
   /*
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
    * Enumeration is not neccessary if GIPS reports the same set of devices
    * for a given instance of the engine. Likewise, if a device was plugged out,
    * mVideoSources must be updated.
    */
   int num;
   num = mozilla::camera::GetChildAndCall(
-      &mozilla::camera::CamerasChild::NumberOfCaptureDevices, capEngine);
+      &mozilla::camera::CamerasChild::NumberOfCaptureDevices, aCapEngine);
 
   for (int i = 0; i < num; i++) {
     char deviceName[MediaEngineSource::kMaxDeviceNameLength];
     char uniqueId[MediaEngineSource::kMaxUniqueIdLength];
     bool scarySource = false;
 
     // paranoia
     deviceName[0] = '\0';
     uniqueId[0] = '\0';
     int error;
 
     error = mozilla::camera::GetChildAndCall(
-        &mozilla::camera::CamerasChild::GetCaptureDevice, capEngine, i,
+        &mozilla::camera::CamerasChild::GetCaptureDevice, aCapEngine, i,
         deviceName, sizeof(deviceName), uniqueId, sizeof(uniqueId),
         &scarySource);
     if (error) {
       LOG(("camera:GetCaptureDevice: Failed %d", error));
       continue;
     }
 #ifdef DEBUG
     LOG(("  Capture Device Index %d, Name %s", i, deviceName));
 
     webrtc::CaptureCapability cap;
     int numCaps = mozilla::camera::GetChildAndCall(
-        &mozilla::camera::CamerasChild::NumberOfCapabilities, capEngine,
+        &mozilla::camera::CamerasChild::NumberOfCapabilities, aCapEngine,
         uniqueId);
     LOG(("Number of Capabilities %d", numCaps));
     for (int j = 0; j < numCaps; j++) {
       if (mozilla::camera::GetChildAndCall(
-              &mozilla::camera::CamerasChild::GetCaptureCapability, capEngine,
+              &mozilla::camera::CamerasChild::GetCaptureCapability, aCapEngine,
               uniqueId, j, cap) != 0) {
         break;
       }
       LOG(("type=%d width=%d height=%d maxFPS=%d",
            static_cast<int>(cap.videoType), cap.width, cap.height, cap.maxFPS));
     }
 #endif
 
@@ -139,26 +114,26 @@ void MediaEngineWebRTC::EnumerateVideoDe
     nsRefPtrHashtable<nsStringHashKey, MediaEngineSource>*
         devicesForThisWindow = mVideoSources.LookupOrAdd(aWindowId);
 
     if (devicesForThisWindow->Get(uuid, getter_AddRefs(vSource)) &&
         vSource->RequiresSharing()) {
       // We've already seen this shared device, just refresh and append.
       static_cast<MediaEngineRemoteVideoSource*>(vSource.get())->Refresh(i);
     } else {
-      vSource = new MediaEngineRemoteVideoSource(i, capEngine, aMediaSource,
+      vSource = new MediaEngineRemoteVideoSource(i, aCapEngine,
                                                  scaryKind || scarySource);
       devicesForThisWindow->Put(uuid, vSource);
     }
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
         vSource, vSource->GetName(), NS_ConvertUTF8toUTF16(vSource->GetUUID()),
         NS_LITERAL_STRING("")));
   }
 
-  if (mHasTabVideoSource || dom::MediaSourceEnum::Browser == aMediaSource) {
+  if (mHasTabVideoSource || aCapEngine == camera::BrowserEngine) {
     RefPtr<MediaEngineSource> tabVideoSource = new MediaEngineTabVideoSource();
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
         tabVideoSource, tabVideoSource->GetName(),
         NS_ConvertUTF8toUTF16(tabVideoSource->GetUUID()),
         NS_LITERAL_STRING("")));
   }
 }
 
@@ -231,21 +206,43 @@ void MediaEngineWebRTC::EnumerateSpeaker
   }
 }
 
 void MediaEngineWebRTC::EnumerateDevices(
     uint64_t aWindowId, dom::MediaSourceEnum aMediaSource,
     MediaSinkEnum aMediaSink, nsTArray<RefPtr<MediaDevice>>* aDevices) {
   MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other ||
              aMediaSink != MediaSinkEnum::Other);
-  // We spawn threads to handle gUM runnables, so we must protect the member
-  // vars
   MutexAutoLock lock(mMutex);
   if (MediaEngineSource::IsVideo(aMediaSource)) {
-    EnumerateVideoDevices(aWindowId, aMediaSource, aDevices);
+    switch (aMediaSource) {
+      case dom::MediaSourceEnum::Window:
+        // Since the mediaSource constraint is deprecated, treat the Window
+        // value as a request for getDisplayMedia-equivalent sharing: Combine
+        // window and fullscreen into a single list of choices. The other values
+        // are still useful for testing.
+        EnumerateVideoDevices(aWindowId, camera::WinEngine, aDevices);
+        EnumerateVideoDevices(aWindowId, camera::ScreenEngine, aDevices);
+        break;
+      case dom::MediaSourceEnum::Application:
+        EnumerateVideoDevices(aWindowId, camera::AppEngine, aDevices);
+        break;
+      case dom::MediaSourceEnum::Screen:
+        EnumerateVideoDevices(aWindowId, camera::ScreenEngine, aDevices);
+        break;
+      case dom::MediaSourceEnum::Browser:
+        EnumerateVideoDevices(aWindowId, camera::BrowserEngine, aDevices);
+        break;
+      case dom::MediaSourceEnum::Camera:
+        EnumerateVideoDevices(aWindowId, camera::CameraEngine, aDevices);
+        break;
+      default:
+        MOZ_CRASH("No valid video source");
+        break;
+    }
   } else if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
         new MediaEngineWebRTCAudioCaptureSource(nullptr);
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
         audioCaptureSource, audioCaptureSource->GetName(),
         NS_ConvertUTF8toUTF16(audioCaptureSource->GetUUID()),
         NS_LITERAL_STRING("")));
   } else if (aMediaSource == dom::MediaSourceEnum::Microphone) {
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -63,17 +63,18 @@ class MediaEngineWebRTC : public MediaEn
   bool SupportsDuplex();
 
   void EnumerateDevices(uint64_t aWindowId, dom::MediaSourceEnum, MediaSinkEnum,
                         nsTArray<RefPtr<MediaDevice>>*) override;
   void ReleaseResourcesForWindow(uint64_t aWindowId) override;
 
  private:
   ~MediaEngineWebRTC() = default;
-  void EnumerateVideoDevices(uint64_t aWindowId, dom::MediaSourceEnum,
+  void EnumerateVideoDevices(uint64_t aWindowId,
+                             camera::CaptureEngine aCapEngine,
                              nsTArray<RefPtr<MediaDevice>>*);
   void EnumerateMicrophoneDevices(uint64_t aWindowId,
                                   nsTArray<RefPtr<MediaDevice>>*);
   void EnumerateSpeakerDevices(uint64_t aWindowId,
                                nsTArray<RefPtr<MediaDevice>>*);
 
   // gUM runnables can e.g. Enumerate from multiple threads
   Mutex mMutex;