diff options
Diffstat (limited to 'src/core/media_capture_devices_dispatcher.cpp')
-rw-r--r-- | src/core/media_capture_devices_dispatcher.cpp | 226 |
1 files changed, 188 insertions, 38 deletions
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp index cddb5a290..a2840b37a 100644 --- a/src/core/media_capture_devices_dispatcher.cpp +++ b/src/core/media_capture_devices_dispatcher.cpp @@ -12,7 +12,7 @@ #include "web_contents_view_qt.h" #include "web_engine_settings.h" -#include "base/task/post_task.h" +#include "base/strings/strcat.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" @@ -22,6 +22,8 @@ #include "media/audio/audio_device_description.h" #include "media/audio/audio_manager_base.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h" +#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" #if QT_CONFIG(webengine_webrtc) #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" @@ -54,26 +56,166 @@ const blink::MediaStreamDevice *findDeviceWithId(const blink::MediaStreamDevices } // Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: -void getDevicesForDesktopCapture(blink::MediaStreamDevices *devices, +media::mojom::CaptureHandlePtr CreateCaptureHandle(content::WebContents *capturer, + const url::Origin &capturer_origin, + const content::DesktopMediaID &captured_id) +{ + if (capturer_origin.opaque()) + return nullptr; + + content::RenderFrameHost *const captured_rfh = + content::RenderFrameHost::FromID( + captured_id.web_contents_id.render_process_id, + captured_id.web_contents_id.main_render_frame_id); + if (!captured_rfh || !captured_rfh->IsActive()) + return nullptr; + + content::WebContents *const captured = content::WebContents::FromRenderFrameHost(captured_rfh); + if (!captured) + return nullptr; + + const auto &captured_config = captured->GetCaptureHandleConfig(); + if (!captured_config.all_origins_permitted && + std::none_of(captured_config.permitted_origins.begin(), + captured_config.permitted_origins.end(), + [capturer_origin](const url::Origin& permitted_origin) { + return capturer_origin.IsSameOriginWith(permitted_origin); + })) + { + return nullptr; + } + + // Observing CaptureHandle when either the capturing or the captured party + // is incognito is disallowed, except for self-capture. + if (capturer->GetPrimaryMainFrame() != captured->GetPrimaryMainFrame()) { + if (capturer->GetBrowserContext()->IsOffTheRecord() || + captured->GetBrowserContext()->IsOffTheRecord()) { + return nullptr; + } + } + + if (!captured_config.expose_origin && captured_config.capture_handle.empty()) + return nullptr; + + auto result = media::mojom::CaptureHandle::New(); + if (captured_config.expose_origin) + result->origin = captured->GetPrimaryMainFrame()->GetLastCommittedOrigin(); + + result->capture_handle = captured_config.capture_handle; + + return result; +} + +// Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: +media::mojom::DisplayMediaInformationPtr DesktopMediaIDToDisplayMediaInformation(content::WebContents *capturer, + const url::Origin &capturer_origin, + const content::DesktopMediaID &media_id) +{ + media::mojom::DisplayCaptureSurfaceType display_surface = media::mojom::DisplayCaptureSurfaceType::MONITOR; + bool logical_surface = true; + media::mojom::CursorCaptureType cursor = media::mojom::CursorCaptureType::NEVER; +#if defined(USE_AURA) + const bool uses_aura = (media_id.window_id != content::DesktopMediaID::kNullId ? true : false); +#else + const bool uses_aura = false; +#endif // defined(USE_AURA) + + media::mojom::CaptureHandlePtr capture_handle; + switch (media_id.type) { + case content::DesktopMediaID::TYPE_SCREEN: + display_surface = media::mojom::DisplayCaptureSurfaceType::MONITOR; + cursor = uses_aura ? media::mojom::CursorCaptureType::MOTION + : media::mojom::CursorCaptureType::ALWAYS; + break; + case content::DesktopMediaID::TYPE_WINDOW: + display_surface = media::mojom::DisplayCaptureSurfaceType::WINDOW; + cursor = uses_aura ? media::mojom::CursorCaptureType::MOTION + : media::mojom::CursorCaptureType::ALWAYS; + break; + case content::DesktopMediaID::TYPE_WEB_CONTENTS: + display_surface = media::mojom::DisplayCaptureSurfaceType::BROWSER; + cursor = media::mojom::CursorCaptureType::MOTION; + capture_handle = CreateCaptureHandle(capturer, capturer_origin, media_id); + break; + case content::DesktopMediaID::TYPE_NONE: + break; + } + + return media::mojom::DisplayMediaInformation::New(display_surface, logical_surface, cursor, std::move(capture_handle)); +} + + +// Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: +std::string DeviceNamePrefix(content::WebContents *web_contents, + blink::mojom::MediaStreamType requested_stream_type, + const content::DesktopMediaID &media_id) +{ + if (!web_contents || requested_stream_type != blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB) { + return std::string(); + } + + // Note that all of these must still be checked, as the explicit-selection + // dialog for DISPLAY_VIDEO_CAPTURE_THIS_TAB could still return something + // other than the current tab - be it a screen, window, or another tab. + if (media_id.type == content::DesktopMediaID::TYPE_WEB_CONTENTS && + web_contents->GetPrimaryMainFrame()->GetProcess()->GetID() == + media_id.web_contents_id.render_process_id && + web_contents->GetPrimaryMainFrame()->GetRoutingID() == + media_id.web_contents_id.main_render_frame_id) { + return "current-"; + } + + return std::string(); +} + +// Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: +std::string DeviceName(content::WebContents *web_contents, + blink::mojom::MediaStreamType requested_stream_type, + const content::DesktopMediaID &media_id) +{ + const std::string prefix = + DeviceNamePrefix(web_contents, requested_stream_type, media_id); + if (media_id.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { + return base::StrCat({prefix, content::kWebContentsCaptureScheme, + base::UnguessableToken::Create().ToString()}); + } else { + // TODO(crbug.com/1252682): MediaStreamTrack.label leaks internal state for + // screen/window + return base::StrCat({prefix, media_id.ToString()}); + } +} + +// Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: +void getDevicesForDesktopCapture(const content::MediaStreamRequest &request, + content::WebContents *web_contents, content::DesktopMediaID mediaId, bool captureAudio, - MediaStreamType videoType, - MediaStreamType audioType) + bool disableLocalEcho, + blink::mojom::StreamDevices &out_devices) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add selected desktop source to the list. - devices->push_back(blink::MediaStreamDevice(videoType, mediaId.ToString(), mediaId.ToString())); + blink::MediaStreamDevice device(request.video_type, mediaId.ToString(), + DeviceName(web_contents, request.video_type, mediaId)); + device.display_media_info = DesktopMediaIDToDisplayMediaInformation( + web_contents, url::Origin::Create(request.security_origin), mediaId); + out_devices.video_device = device; + if (captureAudio) { + DCHECK_NE(request.audio_type, blink::mojom::MediaStreamType::NO_SERVICE); + if (mediaId.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { - devices->push_back( - blink::MediaStreamDevice(audioType, mediaId.ToString(), "Tab audio")); + content::WebContentsMediaCaptureId web_id = mediaId.web_contents_id; + web_id.disable_local_echo = disableLocalEcho; + out_devices.audio_device = blink::MediaStreamDevice(request.audio_type, web_id.ToString(), "Tab audio"); } else { // Use the special loopback device ID for system audio capture. - devices->push_back(blink::MediaStreamDevice( - audioType, - media::AudioDeviceDescription::kLoopbackInputDeviceId, - "System Audio")); + out_devices.audio_device = blink::MediaStreamDevice( + request.audio_type, (disableLocalEcho + ? media::AudioDeviceDescription::kLoopbackWithMuteDeviceId + : media::AudioDeviceDescription::kLoopbackInputDeviceId), + "System Audio"); } } } @@ -201,11 +343,12 @@ WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const co class MediaStreamUIQt : public content::MediaStreamUI { public: - MediaStreamUIQt(content::WebContents *webContents, const blink::MediaStreamDevices &devices) + MediaStreamUIQt(content::WebContents *webContents, const blink::mojom::StreamDevices &devices) : m_delegate(static_cast<WebContentsDelegateQt *>(webContents->GetDelegate())->AsWeakPtr()) , m_devices(devices) { - DCHECK(!m_devices.empty()); + DCHECK(m_devices.audio_device.has_value() || + m_devices.video_device.has_value()); } ~MediaStreamUIQt() override @@ -238,9 +381,10 @@ private: Q_UNUSED(label); Q_UNUSED(media_id); } - + void OnDeviceStoppedForSourceChange(const std::string&, const content::DesktopMediaID&, const content::DesktopMediaID&) override + {} base::WeakPtr<WebContentsDelegateQt> m_delegate; - const blink::MediaStreamDevices m_devices; + const blink::mojom::StreamDevices m_devices; bool m_started = false; base::RepeatingClosure m_onStop; // currently unused }; @@ -262,7 +406,7 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: { DCHECK_CURRENTLY_ON(BrowserThread::UI); - blink::MediaStreamDevices devices; + blink::mojom::StreamDevicesSet deviceSet; auto it = m_pendingRequests.find(webContents); if (it == m_pendingRequests.end() || it->second.empty()) return; @@ -289,20 +433,22 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: if (microphoneRequested || webcamRequested) { switch (request.request_type) { case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY: - getDefaultDevices("", "", microphoneRequested, webcamRequested, &devices); + getDefaultDevices("", "", microphoneRequested, webcamRequested, deviceSet); break; case blink::MEDIA_DEVICE_ACCESS: case blink::MEDIA_DEVICE_UPDATE: case blink::MEDIA_GENERATE_STREAM: case blink::MEDIA_GET_OPEN_DEVICE: getDefaultDevices(request.requested_audio_device_id, request.requested_video_device_id, - microphoneRequested, webcamRequested, &devices); + microphoneRequested, webcamRequested, deviceSet); break; } } else if (desktopVideoRequested) { + deviceSet.stream_devices.emplace_back(blink::mojom::StreamDevices::New()); bool captureAudio = desktopAudioRequested && m_loopbackAudioSupported; - getDevicesForDesktopCapture(&devices, getDefaultScreenId(), captureAudio, - request.video_type, request.audio_type); + blink::mojom::StreamDevices &stream_devices = *deviceSet.stream_devices[0]; + getDevicesForDesktopCapture(request, webContents, getDefaultScreenId(), captureAudio, + request.disable_local_echo, stream_devices); } } @@ -313,17 +459,17 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: // Post a task to process next queued request. It has to be done // asynchronously to make sure that calling infobar is not destroyed until // after this function returns. - base::PostTask(FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, base::BindOnce(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, base::Unretained(this), webContents)); } - if (devices.empty()) - std::move(callback).Run(devices, MediaStreamRequestResult::INVALID_STATE, + if (deviceSet.stream_devices.empty()) + std::move(callback).Run(deviceSet, MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); else - std::move(callback).Run(devices, MediaStreamRequestResult::OK, - std::make_unique<MediaStreamUIQt>(webContents, devices)); + std::move(callback).Run(deviceSet, MediaStreamRequestResult::OK, + std::make_unique<MediaStreamUIQt>(webContents, *deviceSet.stream_devices[0])); } MediaCaptureDevicesDispatcher *MediaCaptureDevicesDispatcher::GetInstance() @@ -358,7 +504,7 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(content::WebConten WebContentsAdapterClient::MediaRequestFlags flags = mediaRequestFlagsForRequest(request); if (!flags) { - std::move(callback).Run(blink::MediaStreamDevices(), MediaStreamRequestResult::NOT_SUPPORTED, std::unique_ptr<content::MediaStreamUI>()); + std::move(callback).Run(blink::mojom::StreamDevicesSet(), MediaStreamRequestResult::NOT_SUPPORTED, std::unique_ptr<content::MediaStreamUI>()); return; } @@ -370,7 +516,7 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(content::WebConten QWebEngineSettings::ScreenCaptureEnabled); const bool originIsSecure = network::IsUrlPotentiallyTrustworthy(request.security_origin); if (!screenCaptureEnabled || !originIsSecure) { - std::move(callback).Run(blink::MediaStreamDevices(), MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); + std::move(callback).Run(blink::mojom::StreamDevicesSet(), MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); return; } @@ -388,7 +534,7 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(content::WebConten void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request, content::MediaResponseCallback callback) { - blink::MediaStreamDevices devices; + blink::mojom::StreamDevicesSet deviceSet; content::WebContents *const web_contents_for_stream = content::WebContents::FromRenderFrameHost( content::RenderFrameHost::FromID(request.render_process_id, request.render_frame_id)); @@ -407,7 +553,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: // Received invalid device id. if (mediaId.type == content::DesktopMediaID::TYPE_NONE) { - std::move(callback).Run(devices, MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); + std::move(callback).Run(deviceSet, MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); return; } @@ -417,14 +563,16 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: bool audioSupported = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && m_loopbackAudioSupported); bool captureAudio = (audioRequested && audioSupported); - getDevicesForDesktopCapture(&devices, mediaId, captureAudio, request.video_type, request.audio_type); + deviceSet.stream_devices.emplace_back(blink::mojom::StreamDevices::New()); + blink::mojom::StreamDevices &stream_devices = *deviceSet.stream_devices[0]; + getDevicesForDesktopCapture(request, webContents, mediaId, captureAudio, request.disable_local_echo, stream_devices); - if (devices.empty()) - std::move(callback).Run(devices, MediaStreamRequestResult::INVALID_STATE, + if (deviceSet.stream_devices.empty()) + std::move(callback).Run(deviceSet, MediaStreamRequestResult::INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); else - std::move(callback).Run(devices, MediaStreamRequestResult::OK, - std::make_unique<MediaStreamUIQt>(webContents, devices)); + std::move(callback).Run(deviceSet, MediaStreamRequestResult::OK, + std::make_unique<MediaStreamUIQt>(webContents, *deviceSet.stream_devices[0])); } void MediaCaptureDevicesDispatcher::enqueueMediaAccessRequest(content::WebContents *webContents, @@ -453,18 +601,20 @@ void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(content::WebConte } void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDeviceId, const std::string &videoDeviceId, - bool audio, bool video, blink::MediaStreamDevices *devices) + bool audio, bool video, blink::mojom::StreamDevicesSet &devicesSet) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(audio || video); + devicesSet.stream_devices.emplace_back(blink::mojom::StreamDevices::New()); + blink::mojom::StreamDevices& devices = *devicesSet.stream_devices[0]; if (audio) { const blink::MediaStreamDevices &audioDevices = content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); const blink::MediaStreamDevice *device = findDeviceWithId(audioDevices, audioDeviceId); if (!device && !audioDevices.empty()) device = &audioDevices.front(); if (device) - devices->push_back(*device); + devices.audio_device = *device; } if (video) { @@ -473,14 +623,14 @@ void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDe if (!device && !videoDevices.empty()) device = &videoDevices.front(); if (device) - devices->push_back(*device); + devices.video_device = *device; } } void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(int render_process_id, int render_frame_id, int page_request_id, const GURL &security_origin, blink::mojom::MediaStreamType stream_type, content::MediaRequestState state) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::PostTask(FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, base::BindOnce(&MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread, base::Unretained(this), render_process_id, render_frame_id, page_request_id, security_origin, stream_type, state)); |