blob: 51f89b0074b9d4daf47fa76a3ec1cc8afd3668fb [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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/display_media_access_handler.h"
#include <array>
#include <memory>
#include <string>
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "base/test/with_feature_override.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/navigation_simulator.h"
#include "media/audio/audio_features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.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 BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#endif // BUILDFLAG(IS_CHROMEOS)
#if !BUILDFLAG(IS_ANDROID)
#include "base/test/gmock_expected_support.h"
#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_WIN)
#include "base/process/process.h"
#include "base/test/bind.h"
#include "base/win/message_window.h"
#include "media/audio/application_loopback_device_helper.h"
#endif // BUILDFLAG(IS_WIN)
class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
public:
DisplayMediaAccessHandlerTest() = default;
~DisplayMediaAccessHandlerTest() override = default;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
auto picker_factory = std::make_unique<FakeDesktopMediaPickerFactory>();
picker_factory_ = picker_factory.get();
access_handler_ = std::make_unique<DisplayMediaAccessHandler>(
std::move(picker_factory), false /* display_notification */);
}
content::WebContentsMediaCaptureId GetWebContentsMediaCaptureId() {
return content::WebContentsMediaCaptureId(
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID(),
1);
}
FakeDesktopMediaPickerFactory::TestFlags MakePickerTestFlags(
bool request_audio) {
return FakeDesktopMediaPickerFactory::TestFlags(
{.expect_screens = true,
.expect_windows = true,
.expect_tabs = true,
.expect_current_tab = false,
.expect_audio = request_audio,
.picker_result =
content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId)});
}
content::MediaStreamRequest MakeRequest(bool request_audio) {
return content::MediaStreamRequest(
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
request_audio ? blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE
: blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
}
content::MediaStreamRequest MakeMediaDeviceUpdateRequest(bool request_audio) {
content::MediaStreamRequest request =
MakeRequest(request_audio /* request_audio */);
request.request_type = blink::MEDIA_DEVICE_UPDATE;
request.requested_video_device_ids = {
GetWebContentsMediaCaptureId().ToString()};
return request;
}
content::MediaStreamRequest MakeExcludeSelfBrowserSurfaceRequest(
bool exclude_self_browser_surface) {
content::MediaStreamRequest request = MakeRequest(/*request_audio=*/false);
request.exclude_self_browser_surface = exclude_self_browser_surface;
return request;
}
content::MediaStreamRequest MakeExcludeMonitorTypeSurfacesRequest(
bool exclude_monitor_type_surfaces) {
content::MediaStreamRequest request = MakeRequest(/*request_audio=*/false);
request.exclude_monitor_type_surfaces = exclude_monitor_type_surfaces;
return request;
}
content::MediaResponseCallback MakeCallback(
base::RunLoop* wait_loop,
blink::mojom::MediaStreamRequestResult* request_result,
blink::mojom::StreamDevices& devices_result) {
return base::BindOnce(
[](base::RunLoop* wait_loop,
blink::mojom::MediaStreamRequestResult* request_result,
blink::mojom::StreamDevices* devices_result,
const blink::mojom::StreamDevicesSet& stream_devices_set,
blink::mojom::MediaStreamRequestResult result,
std::unique_ptr<content::MediaStreamUI> ui) {
*request_result = result;
if (result == blink::mojom::MediaStreamRequestResult::OK) {
ASSERT_EQ(stream_devices_set.stream_devices.size(), 1u);
*devices_result = *stream_devices_set.stream_devices[0];
} else {
ASSERT_TRUE(stream_devices_set.stream_devices.empty());
*devices_result = blink::mojom::StreamDevices();
}
wait_loop->Quit();
},
wait_loop, request_result, &devices_result);
}
void HandleRequest(const content::MediaStreamRequest& request,
base::RunLoop* wait_loop,
blink::mojom::MediaStreamRequestResult* request_result,
blink::mojom::StreamDevices& devices_result) {
access_handler_->HandleRequest(
web_contents(), request,
MakeCallback(wait_loop, request_result, devices_result),
nullptr /* extension */);
}
void SetTestFlags(
std::vector<FakeDesktopMediaPickerFactory::TestFlags> test_flags_vector) {
test_flags_ = std::move(test_flags_vector);
picker_factory_->SetTestFlags(&test_flags_[0], test_flags_.size());
}
void ProcessRequest(
base::expected<content::DesktopMediaID,
blink::mojom::MediaStreamRequestResult> response,
blink::mojom::MediaStreamRequestResult* request_result,
blink::mojom::StreamDevices& devices_result,
bool request_audio,
bool expect_result = true,
bool expect_picker = true,
std::optional<content::MediaStreamRequest> request = std::nullopt) {
SetTestFlags({{.expect_screens = true,
.expect_windows = true,
.expect_tabs = true,
.expect_current_tab = false,
.expect_audio = request_audio,
.picker_result = response}});
if (!request.has_value()) {
request = MakeRequest(request_audio);
}
base::RunLoop wait_loop;
content::MediaResponseCallback callback;
if (expect_result) {
callback = MakeCallback(&wait_loop, request_result, devices_result);
} else {
base::MockCallback<content::MediaResponseCallback> mock_callback =
base::MockCallback<content::MediaResponseCallback>();
EXPECT_CALL(mock_callback, Run).Times(0);
callback = mock_callback.Get();
}
access_handler_->HandleRequest(
web_contents(), *request, std::move(callback), nullptr /* extension */);
if (expect_result) {
wait_loop.Run();
} else {
wait_loop.RunUntilIdle();
}
EXPECT_EQ(test_flags_[0].picker_created, expect_picker);
picker_factory_ = nullptr;
access_handler_.reset();
EXPECT_EQ(test_flags_[0].picker_deleted, expect_picker);
}
void NotifyWebContentsDestroyed() {
access_handler_->WebContentsDestroyed(web_contents());
}
bool IsWebContentsExcluded() const {
return picker_factory_->IsWebContentsExcluded();
}
DesktopMediaPicker::Params GetParams() {
return picker_factory_->picker()->GetParams();
}
const DisplayMediaAccessHandler::RequestsQueues& GetRequestQueues() {
return access_handler_->pending_requests_;
}
void ChangeSourceRequestTest(
bool with_audio,
blink::mojom::MediaStreamRequestResult expected_result,
size_t expected_number_of_devices) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
SetTestFlags({MakePickerTestFlags(with_audio /*request_audio*/)});
base::RunLoop wait_loop;
HandleRequest(MakeMediaDeviceUpdateRequest(with_audio /* request_audio */),
&wait_loop, &result, devices);
wait_loop.Run();
EXPECT_FALSE(test_flags_[0].picker_created);
picker_factory_ = nullptr;
access_handler_.reset();
EXPECT_EQ(expected_result, result);
ASSERT_EQ(expected_number_of_devices, blink::CountDevices(devices));
if (expected_number_of_devices >= 1) {
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
}
if (expected_number_of_devices >= 2) {
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
devices.audio_device.value().type);
}
}
std::vector<FakeDesktopMediaPickerFactory::TestFlags> test_flags_;
protected:
// `access_handler` owns `picker_factory` and must outlive it.
std::unique_ptr<DisplayMediaAccessHandler> access_handler_;
raw_ptr<FakeDesktopMediaPickerFactory> picker_factory_;
};
TEST_F(DisplayMediaAccessHandlerTest, PermissionGiven) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId),
&result, devices, false /* request_audio */);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
result);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
EXPECT_TRUE(devices.video_device.value().display_media_info);
}
#if BUILDFLAG(IS_MAC)
TEST_F(DisplayMediaAccessHandlerTest, WindowPermissionGiven) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
content::DesktopMediaID desktop_media_id(content::DesktopMediaID::TYPE_WINDOW,
content::DesktopMediaID::kFakeId);
// Requests with a window_id will skip macOS screen share permission checks.
desktop_media_id.window_id = content::DesktopMediaID::kFakeId;
ProcessRequest(desktop_media_id, &result, devices, false /* request_audio */);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
EXPECT_TRUE(devices.video_device.value().display_media_info);
}
#endif // BUILDFLAG(IS_MAC)
TEST_F(DisplayMediaAccessHandlerTest, PermissionGivenToRequestWithAudio) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
content::DesktopMediaID fake_media_id(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId,
true /* audio_share */);
ProcessRequest(fake_media_id, &result, devices, true /* request_audio */);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
result);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(2u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
EXPECT_TRUE(devices.video_device.value().display_media_info);
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
devices.audio_device.value().type);
EXPECT_TRUE(devices.audio_device.value().input.IsValid());
}
TEST_F(DisplayMediaAccessHandlerTest, PermissionDenied) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(base::unexpected(
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED),
&result, devices, true /* request_audio */);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
EXPECT_EQ(0u, blink::CountDevices(devices));
}
TEST_F(DisplayMediaAccessHandlerTest, MaxLengthDomainAccepted) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("https://" + std::string(255, 'a')), web_contents());
navigation->Commit();
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
content::DesktopMediaID::kFakeId),
&result, devices, false /* request_audio */);
EXPECT_THAT(
result,
testing::AnyOf(
#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/40802122): Fix screen-capture permissions on mac.
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
#endif
blink::mojom::MediaStreamRequestResult::OK));
}
TEST_F(DisplayMediaAccessHandlerTest, OverMaxLengthDomainRejected) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("https://" + std::string(256, 'a')), web_contents());
navigation->Commit();
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
content::DesktopMediaID::kFakeId),
&result, devices, false /* request_audio */);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::INVALID_STATE, result);
}
class DisplayMediaAccessHandlerActiveRfhTest
: public DisplayMediaAccessHandlerTest,
public testing::WithParamInterface<bool> {
public:
DisplayMediaAccessHandlerActiveRfhTest() : active_rfh_(GetParam()) {}
~DisplayMediaAccessHandlerActiveRfhTest() override = default;
void SetUp() override {
DisplayMediaAccessHandlerTest::SetUp();
Navigate("https://a.com");
}
void Navigate(const std::string& url) {
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(GURL(url),
web_contents());
navigation->Commit();
}
void DeactivateMainRfh() {
// Cross-origin navigation will deactivate the previous RFH.
Navigate("https://b.com");
}
protected:
const bool active_rfh_;
};
INSTANTIATE_TEST_SUITE_P(,
DisplayMediaAccessHandlerActiveRfhTest,
testing::Bool());
TEST_P(DisplayMediaAccessHandlerActiveRfhTest, ProcessRequest) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
const content::DesktopMediaID media_id(
content::DesktopMediaID::TYPE_WEB_CONTENTS,
content::DesktopMediaID::kNullId, GetWebContentsMediaCaptureId());
// Lock in the RFH for use after deactivation. (If `!active_rfh_`; otherwise
// it stays active.)
const bool request_audio = false;
content::MediaStreamRequest request = MakeRequest(request_audio);
request.render_process_id =
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
request.render_frame_id =
web_contents()->GetPrimaryMainFrame()->GetRoutingID();
if (!active_rfh_) {
DeactivateMainRfh();
}
ProcessRequest(media_id, &result, devices, request_audio,
/*expect_result=*/true, /*expect_picker=*/active_rfh_,
request);
EXPECT_EQ(result,
active_rfh_
? blink::mojom::MediaStreamRequestResult::OK
: blink::mojom::MediaStreamRequestResult::INVALID_STATE);
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(DisplayMediaAccessHandlerTest, DlpRestricted) {
const content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId);
// Setup Data Leak Prevention restriction.
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_observer(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, CheckScreenShareRestriction)
.WillOnce([](const content::DesktopMediaID& media_id,
const std::u16string& application_title,
base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(/*should_proceed=*/false);
});
blink::mojom::MediaStreamRequestResult result =
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessRequest(media_id, &result, devices, /*request_audio=*/false);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
EXPECT_EQ(0u, blink::CountDevices(devices));
}
TEST_F(DisplayMediaAccessHandlerTest, DlpNotRestricted) {
const content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId);
// Setup Data Leak Prevention restriction.
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_manager(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, CheckScreenShareRestriction)
.WillOnce([](const content::DesktopMediaID& media_id,
const std::u16string& application_title,
base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(/*should_proceed=*/true);
});
blink::mojom::MediaStreamRequestResult result =
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessRequest(media_id, &result, devices, /*request_audio=*/false);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, blink::CountDevices(devices));
}
TEST_F(DisplayMediaAccessHandlerTest, DlpWebContentsDestroyed) {
const content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId);
// Setup Data Leak Prevention restriction.
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_manager(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, CheckScreenShareRestriction)
.WillOnce([&](const content::DesktopMediaID& media_id,
const std::u16string& application_title,
base::OnceCallback<void(bool)> callback) {
DeleteContents();
std::move(callback).Run(/*should_proceed=*/true);
});
blink::mojom::MediaStreamRequestResult result =
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessRequest(media_id, &result, devices, /*request_audio=*/false,
/*expect_result=*/false);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, result);
EXPECT_EQ(0u, blink::CountDevices(devices));
}
#endif // BUILDFLAG(IS_CHROMEOS)
TEST_F(DisplayMediaAccessHandlerTest, UpdateMediaRequestStateWithClosing) {
const int render_process_id =
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
const int render_frame_id =
web_contents()->GetPrimaryMainFrame()->GetRoutingID();
const int page_request_id = 0;
const blink::mojom::MediaStreamType video_stream_type =
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
const blink::mojom::MediaStreamType audio_stream_type =
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
true /* expect_audio */, content::DesktopMediaID(),
true /* cancelled */}});
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback;
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
nullptr /* extension */);
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_EQ(1u, GetRequestQueues().size());
auto queue_it = GetRequestQueues().find(web_contents());
EXPECT_TRUE(queue_it != GetRequestQueues().end());
EXPECT_EQ(1u, queue_it->second.size());
access_handler_->UpdateMediaRequestState(
render_process_id, render_frame_id, page_request_id, video_stream_type,
content::MEDIA_REQUEST_STATE_CLOSING);
EXPECT_EQ(1u, GetRequestQueues().size());
queue_it = GetRequestQueues().find(web_contents());
EXPECT_TRUE(queue_it != GetRequestQueues().end());
EXPECT_EQ(0u, queue_it->second.size());
EXPECT_TRUE(test_flags_[0].picker_deleted);
}
TEST_F(DisplayMediaAccessHandlerTest, CorrectHostAsksForPermissions) {
const int render_process_id =
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
const int render_frame_id =
web_contents()->GetPrimaryMainFrame()->GetRoutingID();
const int page_request_id = 0;
const blink::mojom::MediaStreamType video_stream_type =
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
const blink::mojom::MediaStreamType audio_stream_type =
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
true /* expect_audio */, content::DesktopMediaID(),
true /* cancelled */}});
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback;
content::WebContents* test_web_contents = web_contents();
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("blob:http://127.0.0.1:8000/says: www.google.com"),
test_web_contents);
navigation->Commit();
access_handler_->HandleRequest(test_web_contents, request,
std::move(callback), nullptr /* extension */);
DesktopMediaPicker::Params params = GetParams();
access_handler_->UpdateMediaRequestState(
render_process_id, render_frame_id, page_request_id, video_stream_type,
content::MEDIA_REQUEST_STATE_CLOSING);
EXPECT_EQ(u"http://127.0.0.1:8000", params.app_name);
}
TEST_F(DisplayMediaAccessHandlerTest, CorrectHostAsksForPermissionsNormalURLs) {
const int render_process_id =
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
const int render_frame_id =
web_contents()->GetPrimaryMainFrame()->GetRoutingID();
const int page_request_id = 0;
const blink::mojom::MediaStreamType video_stream_type =
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
const blink::mojom::MediaStreamType audio_stream_type =
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
true /* expect_audio */, content::DesktopMediaID(),
true /* cancelled */}});
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback;
content::WebContents* test_web_contents = web_contents();
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("https://www.google.com"), test_web_contents);
navigation->Commit();
access_handler_->HandleRequest(test_web_contents, request,
std::move(callback), nullptr /* extension */);
DesktopMediaPicker::Params params = GetParams();
access_handler_->UpdateMediaRequestState(
render_process_id, render_frame_id, page_request_id, video_stream_type,
content::MEDIA_REQUEST_STATE_CLOSING);
EXPECT_EQ(u"www.google.com", params.app_name);
}
#if !BUILDFLAG(IS_ANDROID)
TEST_F(DisplayMediaAccessHandlerTest, IsolatedWebAppNameAsksForPermissions) {
data_decoder::test::InProcessDataDecoder in_process_data_decoder;
web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
const std::string app_name("Test IWA Name");
std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> iwa =
web_app::IsolatedWebAppBuilder(
web_app::ManifestBuilder()
.SetName(app_name)
.AddPermissionsPolicyWildcard(
network::mojom::PermissionsPolicyFeature::kDisplayCapture))
.BuildBundle();
iwa->TrustSigningKey();
iwa->FakeInstallPageState(profile());
ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
iwa->Install(profile()));
web_app::SimulateIsolatedWebAppNavigation(web_contents(),
url_info.origin().GetURL());
const int render_process_id =
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
const int render_frame_id =
web_contents()->GetPrimaryMainFrame()->GetRoutingID();
const int page_request_id = 0;
const blink::mojom::MediaStreamType video_stream_type =
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE;
const blink::mojom::MediaStreamType audio_stream_type =
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE;
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
true /* expect_audio */, content::DesktopMediaID(),
true /* cancelled */}});
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback;
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
nullptr /* extension */);
DesktopMediaPicker::Params params = GetParams();
access_handler_->UpdateMediaRequestState(
render_process_id, render_frame_id, page_request_id, video_stream_type,
content::MEDIA_REQUEST_STATE_CLOSING);
EXPECT_EQ(base::UTF8ToUTF16(app_name), params.app_name);
}
#endif // !BUILDFLAG(IS_ANDROID)
TEST_F(DisplayMediaAccessHandlerTest, WebContentsDestroyed) {
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
false /* expect_audio */, content::DesktopMediaID(),
true /* cancelled */}});
content::MediaStreamRequest request(
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback;
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
nullptr /* extension */);
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_EQ(1u, GetRequestQueues().size());
auto queue_it = GetRequestQueues().find(web_contents());
EXPECT_TRUE(queue_it != GetRequestQueues().end());
EXPECT_EQ(1u, queue_it->second.size());
NotifyWebContentsDestroyed();
EXPECT_EQ(0u, GetRequestQueues().size());
}
TEST_F(DisplayMediaAccessHandlerTest, MultipleRequests) {
SetTestFlags({{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
false /* expect_audio */,
content::DesktopMediaID(
content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId) /* selected_source */},
{true /* expect_screens */, true /* expect_windows*/,
true /* expect_tabs */, false /* expect_current_tab */,
false /* expect_audio */,
content::DesktopMediaID(
content::DesktopMediaID::TYPE_WINDOW,
content::DesktopMediaID::kNullId) /* selected_source */}});
const size_t kTestFlagCount = 2;
blink::mojom::MediaStreamRequestResult result;
blink::MediaStreamDevices devices;
std::array<base::RunLoop, kTestFlagCount> wait_loop;
for (size_t i = 0; i < kTestFlagCount; ++i) {
content::MediaStreamRequest request(
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
content::MediaResponseCallback callback = base::BindOnce(
[](base::RunLoop* wait_loop,
blink::mojom::MediaStreamRequestResult* request_result,
blink::MediaStreamDevices* devices_result,
const blink::mojom::StreamDevicesSet& stream_devices_set,
blink::mojom::MediaStreamRequestResult result,
std::unique_ptr<content::MediaStreamUI> ui) {
*request_result = result;
if (result == blink::mojom::MediaStreamRequestResult::OK) {
ASSERT_EQ(stream_devices_set.stream_devices.size(), 1u);
*devices_result =
blink::ToMediaStreamDevicesList(stream_devices_set);
} else {
ASSERT_TRUE(stream_devices_set.stream_devices.empty());
}
wait_loop->Quit();
},
&wait_loop[i], &result, &devices);
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
nullptr /* extension */);
}
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
result);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, devices.size());
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices[0].type);
blink::MediaStreamDevice first_device = devices[0];
EXPECT_TRUE(test_flags_[1].picker_created);
EXPECT_FALSE(test_flags_[1].picker_deleted);
wait_loop[1].Run();
EXPECT_TRUE(test_flags_[1].picker_deleted);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, devices.size());
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices[0].type);
EXPECT_FALSE(devices[0].IsSameDevice(first_device));
}
TEST_F(DisplayMediaAccessHandlerTest,
ChangeSourceWithoutAudioRequestPermissionGiven) {
ChangeSourceRequestTest(
/*with_audio=*/false,
/*expected_result=*/blink::mojom::MediaStreamRequestResult::OK,
/*expected_number_of_devices=*/1u);
}
TEST_F(DisplayMediaAccessHandlerTest,
ChangeSourceWithAudioRequestPermissionGiven) {
blink::MediaStreamDevices devices;
ChangeSourceRequestTest(
/*with_audio=*/true,
/*expected_result=*/blink::mojom::MediaStreamRequestResult::OK,
/*expected_number_of_devices=*/2u);
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(DisplayMediaAccessHandlerTest, ChangeSourceDlpRestricted) {
const content::DesktopMediaID media_id(
content::DesktopMediaID::TYPE_WEB_CONTENTS,
content::DesktopMediaID::kNullId, GetWebContentsMediaCaptureId());
// Setup Data Leak Prevention restriction.
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_observer(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, CheckScreenShareRestriction)
.WillOnce([](const content::DesktopMediaID& media_id,
const std::u16string& application_title,
base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(/*should_proceed=*/false);
});
ChangeSourceRequestTest(
/*with_audio=*/false,
/*expected_result=*/
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
/*expected_number_of_devices=*/0u);
}
TEST_F(DisplayMediaAccessHandlerTest, ChangeSourceDlpNotRestricted) {
const content::DesktopMediaID media_id(
content::DesktopMediaID::TYPE_WEB_CONTENTS,
content::DesktopMediaID::kNullId, GetWebContentsMediaCaptureId());
// Setup Data Leak Prevention restriction.
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_manager(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, CheckScreenShareRestriction)
.WillOnce([](const content::DesktopMediaID& media_id,
const std::u16string& application_title,
base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(/*should_proceed=*/true);
});
ChangeSourceRequestTest(
/*with_audio=*/false,
/*expected_result=*/
blink::mojom::MediaStreamRequestResult::OK,
/*expected_number_of_devices=*/1u);
}
#endif // BUILDFLAG(IS_CHROMEOS)
TEST_F(DisplayMediaAccessHandlerTest, ChangeSourceWithPendingPickerRequest) {
SetTestFlags({MakePickerTestFlags(false /*request_audio*/),
MakePickerTestFlags(false /*request_audio*/)});
std::array<blink::mojom::MediaStreamRequestResult, 2> results;
std::array<blink::mojom::StreamDevices, 2> devices;
std::array<base::RunLoop, 2> wait_loop;
HandleRequest(MakeRequest(false /* request_audio */), &wait_loop[0],
&results[0], devices[0]);
HandleRequest(MakeMediaDeviceUpdateRequest(false /* request_audio */),
&wait_loop[1], &results[1], devices[1]);
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
results[0]);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[0]);
EXPECT_FALSE(test_flags_[1].picker_created);
EXPECT_FALSE(test_flags_[1].picker_deleted);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[1]);
}
TEST_F(DisplayMediaAccessHandlerTest,
ChangeSourcePolicyViolationWithPendingPickerRequest) {
SetTestFlags({MakePickerTestFlags(false /*request_audio*/),
MakePickerTestFlags(false /*request_audio*/)});
std::array<blink::mojom::MediaStreamRequestResult, 2> results;
std::array<blink::mojom::StreamDevices, 2> devices;
std::array<base::RunLoop, 2> wait_loop;
HandleRequest(MakeRequest(false /* request_audio */), &wait_loop[0],
&results[0], devices[0]);
HandleRequest(MakeMediaDeviceUpdateRequest(false /* request_audio */),
&wait_loop[1], &results[1], devices[1]);
// Policy is changed after the requests are received, but before they are
// processed in the call to wait_loop.Run() below.
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
profile->GetPrefs()->SetBoolean(prefs::kScreenCaptureAllowed, false);
wait_loop[0].Run();
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
results[0]);
return;
#endif
EXPECT_FALSE(test_flags_[1].picker_created);
EXPECT_FALSE(test_flags_[1].picker_deleted);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
results[1]);
}
TEST_F(DisplayMediaAccessHandlerTest,
MalformedChangeSourceBetweenPickerRequests) {
SetTestFlags({{MakePickerTestFlags(false /*request_audio*/)}});
std::array<blink::mojom::MediaStreamRequestResult, 3> results;
std::array<blink::mojom::StreamDevices, 3> devices;
std::array<base::RunLoop, 3> wait_loop;
HandleRequest(MakeRequest(false /* request_audio */), &wait_loop[0],
&results[0], devices[0]);
{
content::MediaStreamRequest request =
MakeMediaDeviceUpdateRequest(false /* request_audio */);
request.requested_video_device_ids = {"MALFORMED"};
HandleRequest(request, &wait_loop[1], &results[1], devices[1]);
}
HandleRequest(MakeMediaDeviceUpdateRequest(false /* request_audio */),
&wait_loop[2], &results[2], devices[2]);
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
results[0]);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[0]);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::INVALID_STATE, results[1]);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[2]);
}
TEST_F(DisplayMediaAccessHandlerTest, ScreenWithAudioDefaultsToSystemAudio) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(
content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, 1,
/*audio_share=*/true),
&result, devices, /*request_audio=*/true);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
result);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(2u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device->type);
EXPECT_TRUE(devices.video_device->display_media_info);
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
devices.audio_device.value().type);
EXPECT_EQ("loopback", devices.audio_device->id);
EXPECT_EQ("System Audio", devices.audio_device->name);
}
TEST_F(DisplayMediaAccessHandlerTest, WindowWithAudioDefaultsToSystemAudio) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessRequest(
content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW, 1234,
/*audio_share=*/true),
&result, devices, /*request_audio=*/true);
// TODO(crbug.com/40802122): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
// On macOS, screen capture requires system permissions that are disabled by
// default.
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
result);
return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(2u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device->type);
EXPECT_TRUE(devices.video_device->display_media_info);
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
devices.audio_device.value().type);
EXPECT_EQ("loopback", devices.audio_device->id);
EXPECT_EQ("System Audio", devices.audio_device->name);
}
class DisplayMediaAccessHandlerTestWithSelfBrowserSurface
: public DisplayMediaAccessHandlerTest,
public testing::WithParamInterface<bool> {
public:
DisplayMediaAccessHandlerTestWithSelfBrowserSurface()
: exclude_self_browser_surface_(GetParam()) {}
~DisplayMediaAccessHandlerTestWithSelfBrowserSurface() override = default;
protected:
const bool exclude_self_browser_surface_;
};
INSTANTIATE_TEST_SUITE_P(_,
DisplayMediaAccessHandlerTestWithSelfBrowserSurface,
::testing::Bool());
TEST_P(DisplayMediaAccessHandlerTestWithSelfBrowserSurface,
CheckIsWebContentsExcluded) {
SetTestFlags({{MakePickerTestFlags(/*request_audio=*/false)}});
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
base::RunLoop wait_loop;
HandleRequest(
MakeExcludeSelfBrowserSurfaceRequest(exclude_self_browser_surface_),
&wait_loop, &result, devices);
wait_loop.Run();
EXPECT_EQ(exclude_self_browser_surface_, IsWebContentsExcluded());
}
class DisplayMediaAccessHandlerTestWithMonitorTypeSurfaces
: public DisplayMediaAccessHandlerTest,
public testing::WithParamInterface<bool> {
public:
DisplayMediaAccessHandlerTestWithMonitorTypeSurfaces()
: exclude_monitor_type_surfaces_(GetParam()) {}
~DisplayMediaAccessHandlerTestWithMonitorTypeSurfaces() override = default;
protected:
const bool exclude_monitor_type_surfaces_;
};
INSTANTIATE_TEST_SUITE_P(_,
DisplayMediaAccessHandlerTestWithMonitorTypeSurfaces,
::testing::Bool());
TEST_P(DisplayMediaAccessHandlerTestWithMonitorTypeSurfaces,
CheckMonitorTypeSurfacesAreExcluded) {
SetTestFlags({{/*expect_screens=*/!exclude_monitor_type_surfaces_,
/*expect_windows=*/true,
/*expect_tabs=*/true, /*expect_current_tab=*/false,
/*expect_audio=*/false,
base::unexpected(
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED),
/*cancelled=*/false}});
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
base::RunLoop wait_loop;
HandleRequest(
MakeExcludeMonitorTypeSurfacesRequest(exclude_monitor_type_surfaces_),
&wait_loop, &result, devices);
wait_loop.Run();
}
#if BUILDFLAG(IS_WIN)
class DisplayMediaAccessHandlerWindowAudioCaptureWinTest
: public DisplayMediaAccessHandlerTest,
public testing::WithParamInterface<content::DesktopMediaID::AudioType> {};
TEST_P(DisplayMediaAccessHandlerWindowAudioCaptureWinTest, ValidWindowId) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
// Create a fake window to simulate a valid window ID. Fortunately, we don't
// need to actually show the window, just to have a valid HWND.
base::win::MessageWindow window;
EXPECT_TRUE(window.Create(
base::BindLambdaForTesting([&](UINT message, WPARAM wparam, LPARAM lparam,
LRESULT* result) { return true; })));
const bool audio_share =
GetParam() != content::DesktopMediaID::AudioType::kNone;
content::DesktopMediaID fake_id(content::DesktopMediaID::TYPE_WINDOW,
reinterpret_cast<intptr_t>(window.hwnd()),
audio_share);
fake_id.window_audio_type = GetParam();
ProcessRequest(fake_id, &result, devices, audio_share);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
// If the audio type is `kNone`, we should not get an audio device.
if (GetParam() == content::DesktopMediaID::AudioType::kNone) {
EXPECT_EQ(1u, blink::CountDevices(devices));
} else {
EXPECT_EQ(2u, blink::CountDevices(devices));
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
devices.audio_device.value().type);
}
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
EXPECT_TRUE(devices.video_device.value().display_media_info);
if (GetParam() == content::DesktopMediaID::AudioType::kApplication) {
EXPECT_TRUE(devices.audio_device.value().input.IsValid());
// Unit tests are executed in a child process that also use the same
// executable image (unit_tests.exe) unless the --single-process flag is
// passed. Therefore, the application process ID should match either the
// parent's process PID or the child's process PID.
uint32_t application_process_id =
media::GetApplicationIdFromApplicationLoopbackDeviceId(
devices.audio_device.value().id);
EXPECT_THAT(application_process_id,
testing::AnyOf(base::Process::Current().Pid(),
base::GetParentProcessId(
base::Process::Current().Handle())));
EXPECT_EQ("Application Audio", devices.audio_device->name);
} else if (GetParam() == content::DesktopMediaID::AudioType::kSystem) {
// System audio device ID and name are constant.
EXPECT_EQ("loopback", devices.audio_device->id);
EXPECT_EQ("System Audio", devices.audio_device->name);
}
}
TEST_P(DisplayMediaAccessHandlerWindowAudioCaptureWinTest, InvalidWindowId) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
const bool audio_share =
GetParam() != content::DesktopMediaID::AudioType::kNone;
content::DesktopMediaID fake_id(content::DesktopMediaID::TYPE_WINDOW,
content::DesktopMediaID::kFakeId,
audio_share);
fake_id.window_audio_type = GetParam();
ProcessRequest(fake_id, &result, devices, audio_share);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
// If the window ID is invalid, window audio should not be captured.
if (!audio_share ||
GetParam() == content::DesktopMediaID::AudioType::kApplication) {
EXPECT_EQ(1u, blink::CountDevices(devices));
} else {
EXPECT_EQ(2u, blink::CountDevices(devices));
}
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices.video_device.value().type);
EXPECT_TRUE(devices.video_device.value().display_media_info);
}
INSTANTIATE_TEST_SUITE_P(
,
DisplayMediaAccessHandlerWindowAudioCaptureWinTest,
testing::Values(content::DesktopMediaID::AudioType::kNone,
content::DesktopMediaID::AudioType::kSystem,
content::DesktopMediaID::AudioType::kApplication),
[](const testing::TestParamInfo<content::DesktopMediaID::AudioType>& info) {
switch (info.param) {
case content::DesktopMediaID::AudioType::kNone:
return "NoAudio";
case content::DesktopMediaID::AudioType::kSystem:
return "SystemAudio";
case content::DesktopMediaID::AudioType::kApplication:
return "ApplicationAudio";
}
NOTREACHED();
});
#endif // BUILDFLAG(IS_WIN)