Browse Source

refactor: unduplicate MediaStreamDevicesController (#34895)

* refactor: unduplicate MediaStreamDevicesController

* restore old logic for GUM_* request types

* lint

* gn format

* add test for unsupported getDisplayMedia

* simplify + comment
Jeremy Rose 2 years ago
parent
commit
3c7d446fad

+ 1 - 0
BUILD.gn

@@ -390,6 +390,7 @@ source_set("electron_lib") {
     "//components/user_prefs",
     "//components/viz/host",
     "//components/viz/service",
+    "//components/webrtc",
     "//content/public/browser",
     "//content/public/child",
     "//content/public/gpu",

+ 0 - 2
filenames.gni

@@ -415,8 +415,6 @@ filenames = {
     "shell/browser/media/media_capture_devices_dispatcher.h",
     "shell/browser/media/media_device_id_salt.cc",
     "shell/browser/media/media_device_id_salt.h",
-    "shell/browser/media/media_stream_devices_controller.cc",
-    "shell/browser/media/media_stream_devices_controller.h",
     "shell/browser/microtasks_runner.cc",
     "shell/browser/microtasks_runner.h",
     "shell/browser/native_browser_view.cc",

+ 1 - 0
patches/chromium/.patches

@@ -112,3 +112,4 @@ custom_protocols_plzserviceworker.patch
 feat_filter_out_non-shareable_windows_in_the_current_application_in.patch
 fix_allow_guest_webcontents_to_enter_fullscreen.patch
 disable_freezing_flags_after_init_in_node.patch
+short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch

+ 88 - 0
patches/chromium/short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch

@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jeremy Rose <[email protected]>
+Date: Tue, 12 Jul 2022 16:51:43 -0700
+Subject: short-circuit permissions checks in MediaStreamDevicesController
+
+The //components/permissions architecture is complicated and not that
+widely used in Chromium, and mostly oriented around showing permissions
+UI and/or remembering per-site permissions, which we're not interested
+in.
+
+Since we do a permissions check prior to invoking the
+MediaStreamDevicesController, and don't (yet) provide the ability to set
+granular permissions (e.g. allow video but not audio), just
+short-circuit all the permissions checks in MSDC for now to allow us to
+unduplicate this code.
+
+diff --git a/components/webrtc/media_stream_devices_controller.cc b/components/webrtc/media_stream_devices_controller.cc
+index 353ed84da62b954a90c8d0a886495c0822f30429..1a1162ba960419a4de5eb2839ebc3debadca86d5 100644
+--- a/components/webrtc/media_stream_devices_controller.cc
++++ b/components/webrtc/media_stream_devices_controller.cc
+@@ -93,11 +93,14 @@ void MediaStreamDevicesController::RequestPermissions(
+ 
+   std::vector<blink::PermissionType> permission_types;
+ 
++#if 0
+   permissions::PermissionManager* permission_manager =
+       permissions::PermissionsClient::Get()->GetPermissionManager(
+           web_contents->GetBrowserContext());
++#endif
+ 
+   if (controller->ShouldRequestAudio()) {
++#if 0
+     permissions::PermissionResult permission_status =
+         permission_manager->GetPermissionStatusForCurrentDocument(
+             ContentSettingsType::MEDIASTREAM_MIC, rfh);
+@@ -109,10 +112,12 @@ void MediaStreamDevicesController::RequestPermissions(
+           permissions::PermissionStatusSource::FEATURE_POLICY);
+       return;
+     }
++#endif
+ 
+     permission_types.push_back(blink::PermissionType::AUDIO_CAPTURE);
+   }
+   if (controller->ShouldRequestVideo()) {
++#if 0
+     permissions::PermissionResult permission_status =
+         permission_manager->GetPermissionStatusForCurrentDocument(
+             ContentSettingsType::MEDIASTREAM_CAMERA, rfh);
+@@ -124,6 +129,7 @@ void MediaStreamDevicesController::RequestPermissions(
+           permissions::PermissionStatusSource::FEATURE_POLICY);
+       return;
+     }
++#endif
+ 
+     permission_types.push_back(blink::PermissionType::VIDEO_CAPTURE);
+ 
+@@ -135,6 +141,7 @@ void MediaStreamDevicesController::RequestPermissions(
+     // pan-tilt-zoom permission and there are suitable PTZ capable devices
+     // available.
+     if (request.request_pan_tilt_zoom_permission && has_pan_tilt_zoom_camera) {
++#if 0
+       permission_status =
+           permission_manager->GetPermissionStatusForCurrentDocument(
+               ContentSettingsType::CAMERA_PAN_TILT_ZOOM, rfh);
+@@ -144,6 +151,7 @@ void MediaStreamDevicesController::RequestPermissions(
+         controller->RunCallback(/*blocked_by_permissions_policy=*/false);
+         return;
+       }
++#endif
+ 
+       permission_types.push_back(blink::PermissionType::CAMERA_PAN_TILT_ZOOM);
+     }
+@@ -425,6 +433,7 @@ bool MediaStreamDevicesController::PermissionIsBlockedForReason(
+   if (rfh->GetLastCommittedOrigin().GetURL() != request_.security_origin) {
+     return false;
+   }
++#if 0
+   permissions::PermissionResult result =
+       permissions::PermissionsClient::Get()
+           ->GetPermissionManager(web_contents_->GetBrowserContext())
+@@ -433,6 +442,7 @@ bool MediaStreamDevicesController::PermissionIsBlockedForReason(
+     DCHECK_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+     return true;
+   }
++#endif
+   return false;
+ }
+ 

+ 0 - 95
shell/browser/media/media_capture_devices_dispatcher.cc

@@ -12,23 +12,6 @@ using content::BrowserThread;
 
 namespace electron {
 
-namespace {
-
-// Finds a device in |devices| that has |device_id|, or NULL if not found.
-const blink::MediaStreamDevice* FindDeviceWithId(
-    const blink::MediaStreamDevices& devices,
-    const std::string& device_id) {
-  auto iter = devices.begin();
-  for (; iter != devices.end(); ++iter) {
-    if (iter->id == device_id) {
-      return &(*iter);
-    }
-  }
-  return nullptr;
-}
-
-}  // namespace
-
 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
   return base::Singleton<MediaCaptureDevicesDispatcher>::get();
 }
@@ -41,84 +24,6 @@ MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() {
 
 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() = default;
 
-const blink::MediaStreamDevices&
-MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (is_device_enumeration_disabled_)
-    return test_audio_devices_;
-  return content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
-}
-
-const blink::MediaStreamDevices&
-MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (is_device_enumeration_disabled_)
-    return test_video_devices_;
-  return content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
-}
-
-void MediaCaptureDevicesDispatcher::GetDefaultDevices(
-    bool audio,
-    bool video,
-    blink::mojom::StreamDevices& devices) {  // NOLINT(runtime/references)
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(audio || video);
-
-  if (audio) {
-    const blink::MediaStreamDevice* device = GetFirstAvailableAudioDevice();
-    if (device)
-      devices.audio_device = *device;
-  }
-
-  if (video) {
-    const blink::MediaStreamDevice* device = GetFirstAvailableVideoDevice();
-    if (device)
-      devices.video_device = *device;
-  }
-}
-
-const blink::MediaStreamDevice*
-MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
-    const std::string& requested_audio_device_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  const blink::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
-  const blink::MediaStreamDevice* const device =
-      FindDeviceWithId(audio_devices, requested_audio_device_id);
-  return device;
-}
-
-const blink::MediaStreamDevice*
-MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  const blink::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
-  if (audio_devices.empty())
-    return nullptr;
-  return &(*audio_devices.begin());
-}
-
-const blink::MediaStreamDevice*
-MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
-    const std::string& requested_video_device_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  const blink::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
-  const blink::MediaStreamDevice* const device =
-      FindDeviceWithId(video_devices, requested_video_device_id);
-  return device;
-}
-
-const blink::MediaStreamDevice*
-MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  const blink::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
-  if (video_devices.empty())
-    return nullptr;
-  return &(*video_devices.begin());
-}
-
-void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
-  is_device_enumeration_disabled_ = true;
-}
-
 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {}
 
 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {}

+ 4 - 41
shell/browser/media/media_capture_devices_dispatcher.h

@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/singleton.h"
+#include "components/webrtc/media_stream_device_enumerator_impl.h"
 #include "content/public/browser/media_observer.h"
 #include "content/public/browser/media_stream_request.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
@@ -17,41 +18,12 @@ namespace electron {
 
 // This singleton is used to receive updates about media events from the content
 // layer.
-class MediaCaptureDevicesDispatcher : public content::MediaObserver {
+class MediaCaptureDevicesDispatcher
+    : public content::MediaObserver,
+      public webrtc::MediaStreamDeviceEnumeratorImpl {
  public:
   static MediaCaptureDevicesDispatcher* GetInstance();
 
-  // Methods for observers. Called on UI thread.
-  const blink::MediaStreamDevices& GetAudioCaptureDevices();
-  const blink::MediaStreamDevices& GetVideoCaptureDevices();
-
-  // Helper to get the default devices which can be used by the media request.
-  // Uses the first available devices if the default devices are not available.
-  // If the return list is empty, it means there is no available device on the
-  // OS.
-  // Called on the UI thread.
-  void GetDefaultDevices(
-      bool audio,
-      bool video,
-      blink::mojom::StreamDevices& devices);  // NOLINT(runtime/references)
-
-  // Helpers for picking particular requested devices, identified by raw id.
-  // If the device requested is not available it will return NULL.
-  const blink::MediaStreamDevice* GetRequestedAudioDevice(
-      const std::string& requested_audio_device_id);
-  const blink::MediaStreamDevice* GetRequestedVideoDevice(
-      const std::string& requested_video_device_id);
-
-  // Returns the first available audio or video device, or NULL if no devices
-  // are available.
-  const blink::MediaStreamDevice* GetFirstAvailableAudioDevice();
-  const blink::MediaStreamDevice* GetFirstAvailableVideoDevice();
-
-  // Unittests that do not require actual device enumeration should call this
-  // API on the singleton. It is safe to call this multiple times on the
-  // singleton.
-  void DisableDeviceEnumerationForTesting();
-
   // Overridden from content::MediaObserver:
   void OnAudioCaptureDevicesChanged() override;
   void OnVideoCaptureDevicesChanged() override;
@@ -79,15 +51,6 @@ class MediaCaptureDevicesDispatcher : public content::MediaObserver {
 
   MediaCaptureDevicesDispatcher();
   ~MediaCaptureDevicesDispatcher() override;
-
-  // Only for testing, a list of cached audio capture devices.
-  blink::MediaStreamDevices test_audio_devices_;
-
-  // Only for testing, a list of cached video capture devices.
-  blink::MediaStreamDevices test_video_devices_;
-
-  // Flag used by unittests to disable device enumeration.
-  bool is_device_enumeration_disabled_ = false;
 };
 
 }  // namespace electron

+ 0 - 234
shell/browser/media/media_stream_devices_controller.cc

@@ -1,234 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#include "shell/browser/media/media_stream_devices_controller.h"
-
-#include <memory>
-#include <utility>
-
-#include "content/public/browser/desktop_media_id.h"
-#include "content/public/browser/media_stream_request.h"
-#include "shell/browser/media/media_capture_devices_dispatcher.h"
-
-namespace electron {
-
-namespace {
-
-bool HasAnyAvailableDevice() {
-  const blink::MediaStreamDevices& audio_devices =
-      MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices();
-  const blink::MediaStreamDevices& video_devices =
-      MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
-
-  return !audio_devices.empty() || !video_devices.empty();
-}
-
-}  // namespace
-
-MediaStreamDevicesController::MediaStreamDevicesController(
-    const content::MediaStreamRequest& request,
-    content::MediaResponseCallback callback)
-    : request_(request),
-      callback_(std::move(callback)),
-      // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam
-      // and microphone to avoid popping two infobars.
-      microphone_requested_(
-          request.audio_type ==
-              blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
-          request.request_type == blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY),
-      webcam_requested_(
-          request.video_type ==
-              blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
-          request.request_type == blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY) {}
-
-MediaStreamDevicesController::~MediaStreamDevicesController() {
-  if (!callback_.is_null()) {
-    std::move(callback_).Run(
-        blink::mojom::StreamDevicesSet(),
-        blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN,
-        std::unique_ptr<content::MediaStreamUI>());
-  }
-}
-
-bool MediaStreamDevicesController::TakeAction() {
-  // Do special handling of desktop screen cast.
-  if (request_.audio_type ==
-          blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE ||
-      request_.video_type ==
-          blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
-      request_.audio_type ==
-          blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE ||
-      request_.video_type ==
-          blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
-    HandleUserMediaRequest();
-    return true;
-  }
-
-  // Deny the request if there is no device attached to the OS.
-  if (!HasAnyAvailableDevice()) {
-    Deny(blink::mojom::MediaStreamRequestResult::NO_HARDWARE);
-    return true;
-  }
-
-  Accept();
-  return true;
-}
-
-void MediaStreamDevicesController::Accept() {
-  // Get the default devices for the request.
-  blink::mojom::StreamDevicesSetPtr stream_devices_set =
-      blink::mojom::StreamDevicesSet::New();
-  stream_devices_set->stream_devices.emplace_back(
-      blink::mojom::StreamDevices::New());
-  blink::mojom::StreamDevices& stream_devices =
-      *stream_devices_set->stream_devices[0];
-
-  if (microphone_requested_ || webcam_requested_) {
-    switch (request_.request_type) {
-      case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY: {
-        const blink::MediaStreamDevice* device = nullptr;
-        // For open device request pick the desired device or fall back to the
-        // first available of the given type.
-        if (request_.audio_type ==
-            blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
-          device =
-              MediaCaptureDevicesDispatcher::GetInstance()
-                  ->GetRequestedAudioDevice(request_.requested_audio_device_id);
-          // TODO(wjia): Confirm this is the intended behavior.
-          if (!device) {
-            device = MediaCaptureDevicesDispatcher::GetInstance()
-                         ->GetFirstAvailableAudioDevice();
-          }
-          if (device)
-            stream_devices.audio_device = *device;
-        } else if (request_.video_type ==
-                   blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
-          // Pepper API opens only one device at a time.
-          device =
-              MediaCaptureDevicesDispatcher::GetInstance()
-                  ->GetRequestedVideoDevice(request_.requested_video_device_id);
-          // TODO(wjia): Confirm this is the intended behavior.
-          if (!device) {
-            device = MediaCaptureDevicesDispatcher::GetInstance()
-                         ->GetFirstAvailableVideoDevice();
-          }
-          if (device)
-            stream_devices.video_device = *device;
-        }
-        break;
-      }
-      case blink::MEDIA_GENERATE_STREAM: {
-        bool needs_audio_device = microphone_requested_;
-        bool needs_video_device = webcam_requested_;
-
-        // Get the exact audio or video device if an id is specified.
-        if (!request_.requested_audio_device_id.empty()) {
-          const blink::MediaStreamDevice* audio_device =
-              MediaCaptureDevicesDispatcher::GetInstance()
-                  ->GetRequestedAudioDevice(request_.requested_audio_device_id);
-          if (audio_device) {
-            stream_devices.audio_device = *audio_device;
-            needs_audio_device = false;
-          }
-        }
-        if (!request_.requested_video_device_id.empty()) {
-          const blink::MediaStreamDevice* video_device =
-              MediaCaptureDevicesDispatcher::GetInstance()
-                  ->GetRequestedVideoDevice(request_.requested_video_device_id);
-          if (video_device) {
-            stream_devices.video_device = *video_device;
-            needs_video_device = false;
-          }
-        }
-
-        // If either or both audio and video devices were requested but not
-        // specified by id, get the default devices.
-        if (needs_audio_device || needs_video_device) {
-          MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevices(
-              needs_audio_device, needs_video_device, stream_devices);
-        }
-        break;
-      }
-      case blink::MEDIA_DEVICE_ACCESS: {
-        // Get the default devices for the request.
-        MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevices(
-            microphone_requested_, webcam_requested_, stream_devices);
-        break;
-      }
-      case blink::MEDIA_DEVICE_UPDATE: {
-        NOTREACHED();
-        break;
-      }
-      case blink::MEDIA_GET_OPEN_DEVICE: {
-        // Transferred tracks, that use blink::MEDIA_GET_OPEN_DEVICE type, do
-        // not need to get permissions for blink::mojom::StreamDevices as those
-        // are controlled by the original context.
-        NOTREACHED();
-        break;
-      }
-    }
-  }
-
-  std::move(callback_).Run(*stream_devices_set,
-                           blink::mojom::MediaStreamRequestResult::OK,
-                           std::unique_ptr<content::MediaStreamUI>());
-}
-
-void MediaStreamDevicesController::Deny(
-    blink::mojom::MediaStreamRequestResult result) {
-  std::move(callback_).Run(blink::mojom::StreamDevicesSet(), result,
-                           std::unique_ptr<content::MediaStreamUI>());
-}
-
-void MediaStreamDevicesController::HandleUserMediaRequest() {
-  blink::mojom::StreamDevicesSetPtr stream_devices_set =
-      blink::mojom::StreamDevicesSet::New();
-  stream_devices_set->stream_devices.emplace_back(
-      blink::mojom::StreamDevices::New());
-  blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
-
-  if (request_.audio_type ==
-      blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
-    devices.audio_device = blink::MediaStreamDevice(
-        blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, "", "");
-  }
-  if (request_.video_type ==
-      blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE) {
-    devices.video_device = blink::MediaStreamDevice(
-        blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, "", "");
-  }
-  if (request_.audio_type ==
-      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE) {
-    devices.audio_device = blink::MediaStreamDevice(
-        blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE, "loopback",
-        "System Audio");
-  }
-  if (request_.video_type ==
-      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
-    content::DesktopMediaID screen_id;
-    // If the device id wasn't specified then this is a screen capture request
-    // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
-    if (request_.requested_video_device_id.empty()) {
-      screen_id = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
-                                          -1 /* kFullDesktopScreenId */);
-    } else {
-      screen_id =
-          content::DesktopMediaID::Parse(request_.requested_video_device_id);
-    }
-
-    devices.video_device = blink::MediaStreamDevice(
-        blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
-        screen_id.ToString(), "Screen");
-  }
-
-  bool empty =
-      !devices.audio_device.has_value() && !devices.video_device.has_value();
-  std::move(callback_).Run(
-      *stream_devices_set,
-      empty ? blink::mojom::MediaStreamRequestResult::NO_HARDWARE
-            : blink::mojom::MediaStreamRequestResult::OK,
-      std::unique_ptr<content::MediaStreamUI>());
-}
-
-}  // namespace electron

+ 0 - 49
shell/browser/media/media_stream_devices_controller.h

@@ -1,49 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#ifndef ELECTRON_SHELL_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_
-#define ELECTRON_SHELL_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_
-
-#include "content/public/browser/web_contents_delegate.h"
-#include "third_party/blink/public/common/mediastream/media_stream_request.h"
-
-namespace electron {
-
-class MediaStreamDevicesController {
- public:
-  MediaStreamDevicesController(const content::MediaStreamRequest& request,
-                               content::MediaResponseCallback callback);
-
-  virtual ~MediaStreamDevicesController();
-
-  // disable copy
-  MediaStreamDevicesController(const MediaStreamDevicesController&) = delete;
-  MediaStreamDevicesController& operator=(const MediaStreamDevicesController&) =
-      delete;
-
-  // Accept or deny the request based on the default policy.
-  bool TakeAction();
-
-  // Explicitly accept or deny the request.
-  void Accept();
-  void Deny(blink::mojom::MediaStreamRequestResult result);
-
- private:
-  // Handle the request of desktop or tab screen cast.
-  void HandleUserMediaRequest();
-
-  // The original request for access to devices.
-  const content::MediaStreamRequest request_;
-
-  // The callback that needs to be Run to notify WebRTC of whether access to
-  // audio/video devices was granted or not.
-  content::MediaResponseCallback callback_;
-
-  bool microphone_requested_;
-  bool webcam_requested_;
-};
-
-}  // namespace electron
-
-#endif  // ELECTRON_SHELL_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_

+ 94 - 6
shell/browser/web_contents_permission_helper.cc

@@ -12,7 +12,10 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "shell/browser/electron_permission_manager.h"
-#include "shell/browser/media/media_stream_devices_controller.h"
+// #include "shell/browser/media/media_stream_devices_controller.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/webrtc/media_stream_devices_controller.h"
+#include "shell/browser/media/media_capture_devices_dispatcher.h"
 
 namespace {
 
@@ -33,14 +36,99 @@ namespace electron {
 
 namespace {
 
+// Handles requests for legacy-style `navigator.getUserMedia(...)` calls.
+// This includes desktop capture through the chromeMediaSource /
+// chromeMediaSourceId constraints.
+void HandleUserMediaRequest(const content::MediaStreamRequest& request,
+                            content::MediaResponseCallback callback) {
+  blink::mojom::StreamDevicesSetPtr stream_devices_set =
+      blink::mojom::StreamDevicesSet::New();
+  stream_devices_set->stream_devices.emplace_back(
+      blink::mojom::StreamDevices::New());
+  blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
+
+  if (request.audio_type ==
+      blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
+    devices.audio_device = blink::MediaStreamDevice(
+        blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, "", "");
+  }
+  if (request.video_type ==
+      blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE) {
+    devices.video_device = blink::MediaStreamDevice(
+        blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, "", "");
+  }
+  if (request.audio_type ==
+      blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE) {
+    devices.audio_device = blink::MediaStreamDevice(
+        blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE, "loopback",
+        "System Audio");
+  }
+  if (request.video_type ==
+      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
+    content::DesktopMediaID screen_id;
+    // If the device id wasn't specified then this is a screen capture request
+    // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
+    if (request.requested_video_device_id.empty()) {
+      screen_id = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
+                                          -1 /* kFullDesktopScreenId */);
+    } else {
+      screen_id =
+          content::DesktopMediaID::Parse(request.requested_video_device_id);
+    }
+
+    devices.video_device = blink::MediaStreamDevice(
+        blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
+        screen_id.ToString(), "Screen");
+  }
+
+  bool empty =
+      !devices.audio_device.has_value() && !devices.video_device.has_value();
+  std::move(callback).Run(
+      *stream_devices_set,
+      empty ? blink::mojom::MediaStreamRequestResult::NO_HARDWARE
+            : blink::mojom::MediaStreamRequestResult::OK,
+      nullptr);
+}
+
+void OnMediaStreamRequestResponse(
+    content::MediaResponseCallback callback,
+    const blink::mojom::StreamDevicesSet& stream_devices_set,
+    blink::mojom::MediaStreamRequestResult result,
+    bool blocked_by_permissions_policy,
+    ContentSetting audio_setting,
+    ContentSetting video_setting) {
+  std::move(callback).Run(stream_devices_set, result, nullptr);
+}
+
 void MediaAccessAllowed(const content::MediaStreamRequest& request,
                         content::MediaResponseCallback callback,
                         bool allowed) {
-  MediaStreamDevicesController controller(request, std::move(callback));
-  if (allowed)
-    controller.TakeAction();
-  else
-    controller.Deny(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED);
+  if (allowed) {
+    if (request.video_type ==
+            blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
+        request.audio_type ==
+            blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE ||
+        request.video_type ==
+            blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
+        request.audio_type ==
+            blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE)
+      HandleUserMediaRequest(request, std::move(callback));
+    else if (request.video_type ==
+                 blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
+             request.audio_type ==
+                 blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
+      webrtc::MediaStreamDevicesController::RequestPermissions(
+          request, MediaCaptureDevicesDispatcher::GetInstance(),
+          base::BindOnce(&OnMediaStreamRequestResponse, std::move(callback)));
+    else
+      std::move(callback).Run(
+          blink::mojom::StreamDevicesSet(),
+          blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
+  } else {
+    std::move(callback).Run(
+        blink::mojom::StreamDevicesSet(),
+        blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, nullptr);
+  }
 }
 
 void OnPermissionResponse(base::OnceCallback<void(bool)> callback,

+ 8 - 0
spec-main/chromium-spec.ts

@@ -1037,6 +1037,14 @@ describe('chromium features', () => {
         }).then((stream) => stream.getVideoTracks())`);
       expect(labels.some((l: any) => l)).to.be.true();
     });
+
+    it('fails with "not supported" for getDisplayMedia', async () => {
+      const w = new BrowserWindow({ show: false });
+      w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
+      const { ok, err } = await w.webContents.executeJavaScript('navigator.mediaDevices.getDisplayMedia({video: true}).then(s => ({ok: true}), e => ({ok: false, err: e.message}))');
+      expect(ok).to.be.false();
+      expect(err).to.equal('Not supported');
+    });
   });
 
   describe('window.opener access', () => {