From 3c7d446fad5da5ea15e0795cbe1870a242efd1f4 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 20 Jul 2022 01:09:14 -0700 Subject: [PATCH] refactor: unduplicate MediaStreamDevicesController (#34895) * refactor: unduplicate MediaStreamDevicesController * restore old logic for GUM_* request types * lint * gn format * add test for unsupported getDisplayMedia * simplify + comment --- BUILD.gn | 1 + filenames.gni | 2 - patches/chromium/.patches | 1 + ...ecks_in_mediastreamdevicescontroller.patch | 88 +++++++ .../media/media_capture_devices_dispatcher.cc | 95 ------- .../media/media_capture_devices_dispatcher.h | 45 +--- .../media/media_stream_devices_controller.cc | 234 ------------------ .../media/media_stream_devices_controller.h | 49 ---- .../browser/web_contents_permission_helper.cc | 100 +++++++- spec-main/chromium-spec.ts | 8 + 10 files changed, 196 insertions(+), 427 deletions(-) create mode 100644 patches/chromium/short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch delete mode 100644 shell/browser/media/media_stream_devices_controller.cc delete mode 100644 shell/browser/media/media_stream_devices_controller.h diff --git a/BUILD.gn b/BUILD.gn index b33a93c72a05..407bdedffcca 100644 --- a/BUILD.gn +++ b/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", diff --git a/filenames.gni b/filenames.gni index 5b9d3cc721db..df58773b49c7 100644 --- a/filenames.gni +++ b/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", diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 1d5849b4368a..0e6fdb164db0 100644 --- a/patches/chromium/.patches +++ b/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 diff --git a/patches/chromium/short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch b/patches/chromium/short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch new file mode 100644 index 000000000000..82c2e5920e0f --- /dev/null +++ b/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 +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 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; + } + diff --git a/shell/browser/media/media_capture_devices_dispatcher.cc b/shell/browser/media/media_capture_devices_dispatcher.cc index 33ad8ebe0d6b..3ebafc583510 100644 --- a/shell/browser/media/media_capture_devices_dispatcher.cc +++ b/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::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() {} diff --git a/shell/browser/media/media_capture_devices_dispatcher.h b/shell/browser/media/media_capture_devices_dispatcher.h index 6b9dd746c633..cb513230aa5d 100644 --- a/shell/browser/media/media_capture_devices_dispatcher.h +++ b/shell/browser/media/media_capture_devices_dispatcher.h @@ -8,6 +8,7 @@ #include #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 diff --git a/shell/browser/media/media_stream_devices_controller.cc b/shell/browser/media/media_stream_devices_controller.cc deleted file mode 100644 index c7594bfdd0bc..000000000000 --- a/shell/browser/media/media_stream_devices_controller.cc +++ /dev/null @@ -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 -#include - -#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()); - } -} - -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()); -} - -void MediaStreamDevicesController::Deny( - blink::mojom::MediaStreamRequestResult result) { - std::move(callback_).Run(blink::mojom::StreamDevicesSet(), result, - std::unique_ptr()); -} - -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()); -} - -} // namespace electron diff --git a/shell/browser/media/media_stream_devices_controller.h b/shell/browser/media/media_stream_devices_controller.h deleted file mode 100644 index 780d38e7cb4e..000000000000 --- a/shell/browser/media/media_stream_devices_controller.h +++ /dev/null @@ -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_ diff --git a/shell/browser/web_contents_permission_helper.cc b/shell/browser/web_contents_permission_helper.cc index 1d9ab4683859..49dcfc1a1c6c 100644 --- a/shell/browser/web_contents_permission_helper.cc +++ b/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 callback, diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 26d0afbca2bb..cac88b927f1a 100644 --- a/spec-main/chromium-spec.ts +++ b/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', () => {