blob: e2c845e22e641183e5812f9ae98e698fd0e49f56 [file] [log] [blame]
// Copyright 2018 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 file.
#include "chrome/browser/media/webrtc/desktop_capture_devices_util.h"
#include <string>
#include <utility>
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/ui/screen_capture_notification_ui.h"
#include "chrome/browser/ui/tab_sharing/tab_sharing_ui.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "media/audio/audio_device_description.h"
#include "media/mojo/mojom/capture_handle.mojom.h"
#include "media/mojo/mojom/display_media_information.mojom.h"
#include "third_party/blink/public/mojom/media/capture_handle_config.mojom.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
// TODO(crbug.com/1208868): Eliminate code duplication with
// capture_handle_manager.cc.
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->GetMainFrame() != captured->GetMainFrame()) {
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->GetMainFrame()->GetLastCommittedOrigin();
}
result->capture_handle = captured_config.capture_handle;
return result;
}
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));
}
std::u16string GetStopSharingUIString(
const std::u16string& application_title,
const std::u16string& registered_extension_name,
bool capture_audio,
content::DesktopMediaID::Type capture_type) {
if (!capture_audio) {
if (application_title == registered_extension_name) {
switch (capture_type) {
case content::DesktopMediaID::TYPE_SCREEN:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, application_title);
case content::DesktopMediaID::TYPE_WINDOW:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_WINDOW_CAPTURE_NOTIFICATION_TEXT, application_title);
case content::DesktopMediaID::TYPE_WEB_CONTENTS:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_TAB_CAPTURE_NOTIFICATION_TEXT, application_title);
case content::DesktopMediaID::TYPE_NONE:
NOTREACHED();
}
} else {
switch (capture_type) {
case content::DesktopMediaID::TYPE_SCREEN:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
registered_extension_name, application_title);
case content::DesktopMediaID::TYPE_WINDOW:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_WINDOW_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
registered_extension_name, application_title);
case content::DesktopMediaID::TYPE_WEB_CONTENTS:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_TAB_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
registered_extension_name, application_title);
case content::DesktopMediaID::TYPE_NONE:
NOTREACHED();
}
}
} else { // The case with audio
if (application_title == registered_extension_name) {
switch (capture_type) {
case content::DesktopMediaID::TYPE_SCREEN:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_SCREEN_CAPTURE_WITH_AUDIO_NOTIFICATION_TEXT,
application_title);
case content::DesktopMediaID::TYPE_WEB_CONTENTS:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_TAB_CAPTURE_WITH_AUDIO_NOTIFICATION_TEXT,
application_title);
case content::DesktopMediaID::TYPE_NONE:
case content::DesktopMediaID::TYPE_WINDOW:
NOTREACHED();
}
} else {
switch (capture_type) {
case content::DesktopMediaID::TYPE_SCREEN:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_SCREEN_CAPTURE_WITH_AUDIO_NOTIFICATION_TEXT_DELEGATED,
registered_extension_name, application_title);
case content::DesktopMediaID::TYPE_WEB_CONTENTS:
return l10n_util::GetStringFUTF16(
IDS_MEDIA_TAB_CAPTURE_WITH_AUDIO_NOTIFICATION_TEXT_DELEGATED,
registered_extension_name, application_title);
case content::DesktopMediaID::TYPE_NONE:
case content::DesktopMediaID::TYPE_WINDOW:
NOTREACHED();
}
}
}
return std::u16string();
}
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 |getCurrentBrowsingContextMedia| 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->GetMainFrame()->GetProcess()->GetID() ==
media_id.web_contents_id.render_process_id &&
web_contents->GetMainFrame()->GetRoutingID() ==
media_id.web_contents_id.main_render_frame_id) {
return "current-";
}
return std::string();
}
} // namespace
std::unique_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture(
content::WebContents* web_contents,
const url::Origin& capturer_origin,
blink::MediaStreamDevices* devices,
const content::DesktopMediaID& media_id,
blink::mojom::MediaStreamType devices_video_type,
blink::mojom::MediaStreamType devices_audio_type,
bool capture_audio,
bool disable_local_echo,
bool display_notification,
const std::u16string& application_title,
const std::u16string& registered_extension_name) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DVLOG(2) << __func__ << ": media_id " << media_id.ToString()
<< ", capture_audio " << capture_audio << ", disable_local_echo "
<< disable_local_echo << ", display_notification "
<< display_notification << ", application_title "
<< application_title << ", extension_name "
<< registered_extension_name;
// Add selected desktop source to the list.
const std::string device_id = media_id.ToString();
const std::string device_name =
DeviceNamePrefix(web_contents, devices_video_type, media_id) + device_id;
auto device =
blink::MediaStreamDevice(devices_video_type, device_id, device_name);
device.display_media_info = DesktopMediaIDToDisplayMediaInformation(
web_contents, capturer_origin, media_id);
devices->push_back(device);
if (capture_audio) {
if (media_id.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) {
content::WebContentsMediaCaptureId web_id = media_id.web_contents_id;
web_id.disable_local_echo = disable_local_echo;
devices->push_back(blink::MediaStreamDevice(
devices_audio_type, web_id.ToString(), "Tab audio"));
} else if (disable_local_echo) {
// Use the special loopback device ID for system audio capture.
devices->push_back(blink::MediaStreamDevice(
devices_audio_type,
media::AudioDeviceDescription::kLoopbackWithMuteDeviceId,
"System Audio"));
} else {
// Use the special loopback device ID for system audio capture.
devices->push_back(blink::MediaStreamDevice(
devices_audio_type,
media::AudioDeviceDescription::kLoopbackInputDeviceId,
"System Audio"));
}
}
// If required, register to display the notification for stream capture.
std::unique_ptr<MediaStreamUI> notification_ui;
if (display_notification) {
if (media_id.type == content::DesktopMediaID::TYPE_WEB_CONTENTS &&
base::FeatureList::IsEnabled(
features::kDesktopCaptureTabSharingInfobar)) {
content::GlobalRenderFrameHostId capturer_id;
if (web_contents && web_contents->GetMainFrame()) {
capturer_id = web_contents->GetMainFrame()->GetGlobalId();
}
notification_ui =
TabSharingUI::Create(capturer_id, media_id, application_title);
} else {
notification_ui = ScreenCaptureNotificationUI::Create(
GetStopSharingUIString(application_title, registered_extension_name,
capture_audio, media_id.type));
}
}
return MediaCaptureDevicesDispatcher::GetInstance()
->GetMediaStreamCaptureIndicator()
->RegisterMediaStream(web_contents, *devices, std::move(notification_ui),
application_title);
}