| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "base/unguessable_token.h" |
| #include "build/build_config.h" |
| #include "content/browser/media/media_devices_util.h" |
| #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" |
| #include "content/browser/renderer_host/media/mock_video_capture_provider.h" |
| #include "content/browser/renderer_host/media/video_capture_manager.h" |
| #include "content/common/features.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/media_observer.h" |
| #include "content/public/browser/media_request_state.h" |
| #include "content/public/common/buildflags.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/mock_captured_surface_controller.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/test/test_web_contents.h" |
| #include "media/audio/application_loopback_device_helper.h" |
| #include "media/audio/audio_device_description.h" |
| #include "media/audio/audio_system_impl.h" |
| #include "media/audio/fake_audio_log_factory.h" |
| #include "media/audio/test_audio_thread.h" |
| #include "media/base/media_switches.h" |
| #include "media/capture/content/screen_enumerator.h" |
| #include "media/capture/mojom/video_capture_types.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.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.h" |
| #include "ui/gfx/native_widget_types.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| #if defined(USE_ALSA) |
| #include "media/audio/alsa/audio_manager_alsa.h" |
| #elif BUILDFLAG(IS_ANDROID) |
| #include "media/audio/android/audio_manager_android.h" |
| #elif BUILDFLAG(IS_MAC) |
| #include "media/audio/mac/audio_manager_mac.h" |
| #elif BUILDFLAG(IS_WIN) |
| #include "media/audio/win/audio_manager_win.h" |
| #else |
| #include "media/audio/fake_audio_manager.h" |
| #endif |
| |
| using ::blink::mojom::CapturedSurfaceControlResult; |
| using ::blink::mojom::MediaStreamType; |
| using ::blink::mojom::StreamSelectionInfo; |
| using ::blink::mojom::StreamSelectionInfoPtr; |
| using ::testing::_; |
| using testing::ElementsAre; |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| using ::blink::mojom::CapturedWheelAction; |
| using ::blink::mojom::CapturedWheelActionPtr; |
| using ::blink::mojom::ZoomLevelAction; |
| using CapturedSurfaceControllerFactoryCallback = |
| ::content::MediaStreamManager::CapturedSurfaceControllerFactoryCallback; |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| |
| using DeviceStoppedCallback = |
| ::content::MediaStreamManager::DeviceStoppedCallback; |
| |
| namespace content { |
| |
| #if defined(USE_ALSA) |
| typedef media::AudioManagerAlsa AudioManagerPlatform; |
| #elif BUILDFLAG(IS_MAC) |
| typedef media::AudioManagerMac AudioManagerPlatform; |
| #elif BUILDFLAG(IS_WIN) |
| typedef media::AudioManagerWin AudioManagerPlatform; |
| #elif BUILDFLAG(IS_ANDROID) |
| typedef media::AudioManagerAndroid AudioManagerPlatform; |
| #else |
| typedef media::FakeAudioManager AudioManagerPlatform; |
| #endif |
| |
| namespace { |
| |
| const char kFakeDeviceIdPrefix[] = "fake_device_id_"; |
| const GlobalRenderFrameHostId kRenderFrameHostId{1, 2}; |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| enum class CapturedSurfaceControlAPI { |
| kSendWheel, |
| kIncreaseZoomLevel, |
| kDecreaseZoomLevel, |
| kResetZoomLevel, |
| kRequestPermission, |
| }; |
| |
| ZoomLevelAction ToZoomLevelAction(CapturedSurfaceControlAPI input) { |
| switch (input) { |
| case CapturedSurfaceControlAPI::kIncreaseZoomLevel: |
| return ZoomLevelAction::kIncrease; |
| case CapturedSurfaceControlAPI::kDecreaseZoomLevel: |
| return ZoomLevelAction::kDecrease; |
| case CapturedSurfaceControlAPI::kResetZoomLevel: |
| return ZoomLevelAction::kReset; |
| case CapturedSurfaceControlAPI::kSendWheel: |
| case CapturedSurfaceControlAPI::kRequestPermission: |
| break; |
| } |
| NOTREACHED() << "Not a ZoomLevelAction."; |
| } |
| #endif |
| |
| std::string GetAudioInputDeviceId(size_t index) { |
| return std::string(kFakeDeviceIdPrefix) + base::NumberToString(index); |
| } |
| |
| std::string GetAudioInputDeviceName(size_t index) { |
| return std::string("fake_device_name_") + base::NumberToString(index); |
| } |
| |
| // This class mocks the audio manager and overrides some methods to ensure that |
| // we can run our tests on the buildbots. |
| class MockAudioManager : public AudioManagerPlatform { |
| public: |
| MockAudioManager() |
| : AudioManagerPlatform(std::make_unique<media::TestAudioThread>(), |
| &fake_audio_log_factory_), |
| num_output_devices_(2), |
| num_input_devices_(2) {} |
| |
| MockAudioManager(const MockAudioManager&) = delete; |
| MockAudioManager& operator=(const MockAudioManager&) = delete; |
| |
| ~MockAudioManager() override = default; |
| |
| void GetAudioInputDeviceNames( |
| media::AudioDeviceNames* device_names) override { |
| DCHECK(device_names->empty()); |
| |
| // AudioManagers add a default device when there is at least one real device |
| if (num_input_devices_ > 0) |
| device_names->push_back(media::AudioDeviceName::CreateDefault()); |
| |
| for (size_t i = 0; i < num_input_devices_; i++) { |
| device_names->emplace_back( |
| /*device_name=*/GetAudioInputDeviceName(i), |
| /*unique_id=*/GetAudioInputDeviceId(i)); |
| } |
| } |
| |
| void GetAudioOutputDeviceNames( |
| media::AudioDeviceNames* device_names) override { |
| DCHECK(device_names->empty()); |
| |
| // AudioManagers add a default device when there is at least one real device |
| if (num_output_devices_ > 0) |
| device_names->push_back(media::AudioDeviceName::CreateDefault()); |
| |
| for (size_t i = 0; i < num_output_devices_; i++) { |
| device_names->emplace_back( |
| /*device_name=*/std::string("fake_device_name_") + |
| base::NumberToString(i), |
| /*unique_id=*/std::string(kFakeDeviceIdPrefix) + |
| base::NumberToString(i)); |
| } |
| } |
| |
| media::AudioParameters GetOutputStreamParameters( |
| const std::string& device_id) override { |
| return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| media::ChannelLayoutConfig::Stereo(), 48000, |
| 128); |
| } |
| |
| void SetNumAudioOutputDevices(size_t num_devices) { |
| num_output_devices_ = num_devices; |
| } |
| |
| void SetNumAudioInputDevices(size_t num_devices) { |
| num_input_devices_ = num_devices; |
| } |
| |
| private: |
| media::FakeAudioLogFactory fake_audio_log_factory_; |
| size_t num_output_devices_; |
| size_t num_input_devices_; |
| }; |
| |
| class MockMediaObserver : public MediaObserver { |
| public: |
| MOCK_METHOD0(OnAudioCaptureDevicesChanged, void()); |
| MOCK_METHOD0(OnVideoCaptureDevicesChanged, void()); |
| MOCK_METHOD6(OnMediaRequestStateChanged, |
| void(int, |
| int, |
| int, |
| const GURL&, |
| blink::mojom::MediaStreamType, |
| MediaRequestState)); |
| MOCK_METHOD2(OnCreatingAudioStream, void(int, int)); |
| MOCK_METHOD5(OnSetCapturingLinkSecured, |
| void(int, int, int, blink::mojom::MediaStreamType, bool)); |
| }; |
| |
| class ScreenEnumeratorMock : public media::ScreenEnumerator { |
| public: |
| explicit ScreenEnumeratorMock(const size_t* screen_count) |
| : screen_count_(screen_count) {} |
| ~ScreenEnumeratorMock() override = default; |
| |
| void EnumerateScreens( |
| blink::mojom::MediaStreamType stream_type, |
| base::OnceCallback< |
| void(const blink::mojom::StreamDevicesSet& stream_devices_set, |
| blink::mojom::MediaStreamRequestResult result)> screens_callback) |
| const override { |
| blink::mojom::StreamDevicesSet stream_devices_set; |
| for (size_t screen_idx = 0; screen_idx < *screen_count_; ++screen_idx) { |
| stream_devices_set.stream_devices.push_back( |
| blink::mojom::StreamDevices::New( |
| /*audio_device=*/std::nullopt, |
| /*video_device=*/blink::MediaStreamDevice( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| base::StrCat({"id_", base::NumberToString(screen_idx)}), |
| base::StrCat({"name_", base::NumberToString(screen_idx)})))); |
| } |
| std::move(screens_callback) |
| .Run(stream_devices_set, blink::mojom::MediaStreamRequestResult::OK); |
| } |
| |
| private: |
| raw_ptr<const size_t> screen_count_; |
| }; |
| |
| class MediaStreamProviderListenerMock |
| : public content::MediaStreamProviderListener { |
| public: |
| void Opened(blink::mojom::MediaStreamType stream_type, |
| const base::UnguessableToken& capture_session_id) override { |
| capture_session_ids_.push_back(capture_session_id); |
| } |
| |
| void Closed(blink::mojom::MediaStreamType stream_type, |
| const base::UnguessableToken& capture_session_id) override {} |
| |
| void Aborted(blink::mojom::MediaStreamType stream_type, |
| const base::UnguessableToken& capture_session_id) override {} |
| |
| const std::vector<base::UnguessableToken>& capture_session_ids() { |
| return capture_session_ids_; |
| } |
| |
| private: |
| std::vector<base::UnguessableToken> capture_session_ids_; |
| }; |
| |
| class TestBrowserClient : public ContentBrowserClient { |
| public: |
| explicit TestBrowserClient(MediaObserver* media_observer, |
| const size_t* screen_count) |
| : media_observer_(media_observer), screen_count_(screen_count) {} |
| ~TestBrowserClient() override = default; |
| MediaObserver* GetMediaObserver() override { return media_observer_; } |
| std::unique_ptr<media::ScreenEnumerator> CreateScreenEnumerator() |
| const override { |
| return std::make_unique<ScreenEnumeratorMock>(screen_count_); |
| } |
| |
| MOCK_METHOD(void, |
| NotifyMultiCaptureStateChanged, |
| (GlobalRenderFrameHostId render_frame_host_id, |
| const std::string& label, |
| MultiCaptureChanged state), |
| (override)); |
| |
| private: |
| raw_ptr<MediaObserver> media_observer_; |
| raw_ptr<const size_t> screen_count_; |
| }; |
| |
| class MockMediaStreamUIProxy : public FakeMediaStreamUIProxy { |
| public: |
| MockMediaStreamUIProxy() |
| : FakeMediaStreamUIProxy(/*tests_use_fake_render_frame_hosts=*/true) {} |
| void RequestAccess(std::unique_ptr<MediaStreamRequest> request, |
| ResponseCallback response_callback) override { |
| MockRequestAccess(request, response_callback); |
| } |
| |
| MOCK_METHOD2(MockRequestAccess, |
| void(std::unique_ptr<MediaStreamRequest>& request, |
| ResponseCallback& response_callback)); |
| }; |
| |
| class TestMediaStreamDispatcherHost |
| : public blink::mojom::MediaStreamDispatcherHost { |
| void GenerateStreams( |
| int32_t request_id, |
| const blink::StreamControls& controls, |
| bool user_gesture, |
| blink::mojom::StreamSelectionInfoPtr audio_stream_selection_info_ptr, |
| GenerateStreamsCallback callback) override {} |
| void CancelRequest(int32_t request_id) override {} |
| void StopStreamDevice( |
| const std::string& device_id, |
| const std::optional<base::UnguessableToken>& session_id) override {} |
| void OpenDevice(int32_t request_id, |
| const std::string& device_id, |
| blink::mojom::MediaStreamType type, |
| OpenDeviceCallback callback) override {} |
| void CloseDevice(const std::string& label) override {} |
| void SetCapturingLinkSecured( |
| const std::optional<base::UnguessableToken>& session_id, |
| blink::mojom::MediaStreamType type, |
| bool is_secure) override {} |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| void SendWheel(const base::UnguessableToken& device_id, |
| blink::mojom::CapturedWheelActionPtr action) override {} |
| void UpdateZoomLevel(const base::UnguessableToken& device_id, |
| ZoomLevelAction action, |
| UpdateZoomLevelCallback callback) override {} |
| void RequestCapturedSurfaceControlPermission( |
| const base::UnguessableToken& device_id, |
| RequestCapturedSurfaceControlPermissionCallback callback) override {} |
| void FocusCapturedSurface(const std::string& label, bool focus) override {} |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| |
| #if BUILDFLAG(ENABLE_SCREEN_CAPTURE) |
| void ApplySubCaptureTarget(const base::UnguessableToken& device_id, |
| media::mojom::SubCaptureTargetType type, |
| const base::Token& target, |
| uint32_t sub_capture_target_version, |
| ApplySubCaptureTargetCallback callback) override {} |
| #endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE) |
| |
| void GetOpenDevice(int32_t page_request_id, |
| const base::UnguessableToken& session_id, |
| const base::UnguessableToken& transfer_id, |
| GetOpenDeviceCallback callback) override {} |
| void KeepDeviceAliveForTransfer( |
| const base::UnguessableToken& session_id, |
| const base::UnguessableToken& transfer_id, |
| base::OnceCallback<void(bool device_found)> callback) override {} |
| }; |
| |
| class TestVideoCaptureHost : public media::mojom::VideoCaptureHost { |
| void Start(const base::UnguessableToken& device_id, |
| const base::UnguessableToken& session_id, |
| const media::VideoCaptureParams& params, |
| mojo::PendingRemote<media::mojom::VideoCaptureObserver> observer) |
| override {} |
| void Stop(const base::UnguessableToken& device_id) override {} |
| void Pause(const base::UnguessableToken& device_id) override {} |
| void Resume(const base::UnguessableToken& device_id, |
| const base::UnguessableToken& session_id, |
| const media::VideoCaptureParams& params) override {} |
| void RequestRefreshFrame(const base::UnguessableToken& device_id) override {} |
| void ReleaseBuffer(const base::UnguessableToken& device_id, |
| int32_t buffer_id, |
| const media::VideoCaptureFeedback& feedback) override {} |
| void GetDeviceSupportedFormats( |
| const base::UnguessableToken& device_id, |
| const base::UnguessableToken& session_id, |
| GetDeviceSupportedFormatsCallback callback) override {} |
| void GetDeviceFormatsInUse(const base::UnguessableToken& device_id, |
| const base::UnguessableToken& session_id, |
| GetDeviceFormatsInUseCallback callback) override {} |
| void OnLog(const base::UnguessableToken& device_id, |
| const std::string& reason) override {} |
| }; |
| |
| blink::StreamControls GetVideoStreamControls(std::string hmac_device_id) { |
| blink::StreamControls stream_controls{/*request_audio=*/false, |
| /*request_video=*/true}; |
| stream_controls.video.device_ids = {hmac_device_id}; |
| return stream_controls; |
| } |
| |
| blink::StreamControls GetAudioStreamControls(std::string hmac_device_id) { |
| blink::StreamControls stream_controls{/*request_audio=*/true, |
| /*request_video=*/false}; |
| stream_controls.audio.device_ids = {hmac_device_id}; |
| return stream_controls; |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| // Make an arbitrary valid CapturedWheelAction. |
| CapturedWheelActionPtr MakeCapturedWheelActionPtr() { |
| return CapturedWheelAction::New( |
| /*x=*/0, |
| /*y=*/0, |
| /*wheel_delta_x=*/0, |
| /*wheel_delta_y=*/0); |
| } |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| |
| blink::mojom::StreamSelectionInfoPtr NewSearchBySessionId( |
| base::flat_map<std::string, base::UnguessableToken> session_id_map) { |
| return blink::mojom::StreamSelectionInfo::NewSearchBySessionId( |
| blink::mojom::SearchBySessionId::New(session_id_map)); |
| } |
| |
| } // namespace |
| |
| class MediaStreamManagerTest : public ::testing::Test { |
| public: |
| MediaStreamManagerTest() |
| : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) { |
| audio_manager_ = std::make_unique<MockAudioManager>(); |
| audio_system_ = |
| std::make_unique<media::AudioSystemImpl>(audio_manager_.get()); |
| auto video_capture_provider = std::make_unique<MockVideoCaptureProvider>(); |
| video_capture_provider_ = video_capture_provider.get(); |
| media_stream_manager_ = std::make_unique<MediaStreamManager>( |
| audio_system_.get(), std::move(video_capture_provider)); |
| media_observer_ = std::make_unique<MockMediaObserver>(); |
| browser_content_client_ = std::make_unique<TestBrowserClient>( |
| media_observer_.get(), &screen_count_); |
| SetBrowserClientForTesting(browser_content_client_.get()); |
| base::RunLoop().RunUntilIdle(); |
| |
| SetVideoCaptureDevices(/*devices=*/{}); |
| } |
| |
| MediaStreamManagerTest(const MediaStreamManagerTest&) = delete; |
| MediaStreamManagerTest& operator=(const MediaStreamManagerTest&) = delete; |
| |
| ~MediaStreamManagerTest() override { audio_manager_->Shutdown(); } |
| |
| MOCK_METHOD1(Response, void(int index)); |
| void ResponseCallback( |
| int index, |
| const blink::mojom::StreamDevicesSet& stream_devices_set, |
| std::unique_ptr<MediaStreamUIProxy> ui_proxy) { |
| Response(index); |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| } |
| |
| protected: |
| std::string MakeMediaAccessRequest( |
| int index, |
| const GlobalRenderFrameHostId render_frame_host_id = kRenderFrameHostId) { |
| const int requester_id = 1; |
| const int page_request_id = 1; |
| const url::Origin security_origin; |
| MediaStreamManager::MediaAccessRequestCallback callback = |
| base::BindOnce(&MediaStreamManagerTest::ResponseCallback, |
| base::Unretained(this), index); |
| blink::StreamControls controls(true, true); |
| return media_stream_manager_->MakeMediaAccessRequest( |
| render_frame_host_id, requester_id, page_request_id, controls, |
| security_origin, std::move(callback)); |
| } |
| |
| void RequestMultiScreenCapture(size_t screen_count, |
| const base::UnguessableToken& session) { |
| screen_count_ = screen_count; |
| const GlobalRenderFrameHostId render_frame_host_id{0, 0}; |
| const int requester_id = 0; |
| const int page_request_id = 0; |
| blink::StreamControls controls(/*request_audio=*/false, |
| /*request_video=*/true); |
| controls.request_all_screens = true; |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET; |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_OPENING)); |
| stream_provider_listener_ = |
| std::make_unique<MediaStreamProviderListenerMock>(); |
| media_stream_manager_->video_capture_manager_->RegisterListener( |
| stream_provider_listener_.get()); |
| media_stream_manager_->video_capture_manager_->UnregisterListener( |
| media_stream_manager_.get()); |
| media_stream_manager_->GenerateStreams( |
| render_frame_host_id, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), /*user_gesture=*/false, |
| /*audio_stream_selection_info_ptr=*/ |
| NewSearchBySessionId({}), |
| /*generate_stream_callback=*/base::DoNothing(), |
| /*device_stopped_callback=*/base::DoNothing(), |
| /*device_changed_callback=*/base::DoNothing(), |
| /*device_request_state_change_callback=*/base::DoNothing(), |
| /*device_capture_configuration_change_callback=*/base::DoNothing(), |
| /*device_capture_handle_change_callback=*/base::DoNothing(), |
| /*zoom_level_change_callback=*/base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetMediaObserverExpectations(bool app_requested_audio, |
| bool user_shared_audio) { |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_PENDING_APPROVAL)); |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_OPENING)); |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_DONE)); |
| if (app_requested_audio) { |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_PENDING_APPROVAL)); |
| if (user_shared_audio) { |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_OPENING)); |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_DONE)); |
| } |
| } |
| } |
| |
| using VerifyDevicesCallback = |
| base::OnceCallback<void(const blink::MediaStreamDevice& video_device, |
| const blink::MediaStreamDevice& audio_device)>; |
| |
| void RequestAndStopGetDisplayMedia( |
| bool app_requested_audio, |
| bool user_shared_audio, |
| const blink::MediaStreamDevices& additional_devices, |
| VerifyDevicesCallback verify_devices_callback) { |
| DCHECK(app_requested_audio || !user_shared_audio); |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating( |
| [](bool user_shared_audio, |
| const blink::MediaStreamDevices& additional_devices) { |
| auto fake_ui = std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| fake_ui->SetAudioShare(user_shared_audio); |
| // MediaStreamManager automatically adds a default audio and video |
| // device to fake_ui. The additional devices added here take |
| // precedence over those default devices. |
| fake_ui->AddAvailableDevices(additional_devices); |
| return std::unique_ptr<FakeMediaStreamUIProxy>(std::move(fake_ui)); |
| }, |
| user_shared_audio, additional_devices)); |
| |
| blink::StreamControls controls( |
| app_requested_audio /* app_requested_audio */, |
| true /* request_video */); |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; |
| if (app_requested_audio) |
| controls.audio.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE; |
| |
| const int requester_id = 1; |
| const int page_request_id = 1; |
| |
| blink::MediaStreamDevice video_device; |
| blink::MediaStreamDevice audio_device; |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop_, app_requested_audio, |
| /*requst_video=*/true, &audio_device, &video_device, |
| user_shared_audio); |
| base::MockCallback<MediaStreamManager::DeviceStoppedCallback> |
| stopped_callback; |
| MediaStreamManager::DeviceChangedCallback changed_callback; |
| MediaStreamManager::DeviceRequestStateChangeCallback |
| request_state_change_callback; |
| MediaStreamManager::DeviceCaptureConfigurationChangeCallback |
| capture_configuration_change_callback; |
| MediaStreamManager::DeviceCaptureHandleChangeCallback |
| capture_handle_change_callback; |
| MediaStreamManager::ZoomLevelChangeCallback zoom_level_change_callback; |
| |
| SetMediaObserverExpectations(app_requested_audio, user_shared_audio); |
| |
| media_stream_manager_->GenerateStreams( |
| kRenderFrameHostId, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), false /* user_gesture */, |
| StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), stopped_callback.Get(), |
| std::move(changed_callback), std::move(request_state_change_callback), |
| std::move(capture_configuration_change_callback), |
| std::move(capture_handle_change_callback), |
| std::move(zoom_level_change_callback)); |
| run_loop_.Run(); |
| |
| std::move(verify_devices_callback).Run(video_device, audio_device); |
| |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| media_stream_manager_->StopStreamDevice(kRenderFrameHostId, requester_id, |
| video_device.id, |
| video_device.session_id()); |
| blink::MediaStreamDevice device; |
| if (app_requested_audio && user_shared_audio) { |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL(stopped_callback, Run(_, _)) |
| .WillOnce(testing::SaveArg<1>(&device)); |
| } |
| media_stream_manager_->StopStreamDevice(kRenderFrameHostId, requester_id, |
| audio_device.id, |
| audio_device.session_id()); |
| EXPECT_EQ(device.type, |
| app_requested_audio && user_shared_audio |
| ? blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE |
| : blink::mojom::MediaStreamType::NO_SERVICE); |
| } |
| |
| static void GenerateStreamsCallback( |
| base::RunLoop* wait_loop, |
| bool request_audio, |
| bool request_video, |
| blink::MediaStreamDevice* audio_device, |
| blink::MediaStreamDevice* video_device, |
| bool audio_share, |
| blink::mojom::MediaStreamRequestResult result, |
| const std::string& label, |
| blink::mojom::StreamDevicesSetPtr stream_devices_set, |
| bool pan_tilt_zoom_allowed) { |
| // TODO(crbug.com/40216442): Generalize to multiple streams. |
| DCHECK_EQ(stream_devices_set->stream_devices.size(), 1u); |
| if (request_audio && audio_share) { |
| ASSERT_TRUE( |
| stream_devices_set->stream_devices[0]->audio_device.has_value()); |
| *audio_device = |
| stream_devices_set->stream_devices[0]->audio_device.value(); |
| } else { |
| ASSERT_FALSE( |
| stream_devices_set->stream_devices[0]->audio_device.has_value()); |
| } |
| |
| if (request_video) { |
| ASSERT_TRUE( |
| stream_devices_set->stream_devices[0]->video_device.has_value()); |
| *video_device = |
| stream_devices_set->stream_devices[0]->video_device.value(); |
| } else { |
| ASSERT_FALSE( |
| stream_devices_set->stream_devices[0]->video_device.has_value()); |
| } |
| |
| wait_loop->Quit(); |
| } |
| |
| blink::MediaStreamDevice CreateOrSearchAudioDeviceStream( |
| StreamSelectionInfoPtr selection_info, |
| GlobalRenderFrameHostId render_frame_host_id = kRenderFrameHostId, |
| const blink::StreamControls& controls = |
| blink::StreamControls(true /* request_audio */, |
| false /* request_video */), |
| int requester_id = 1, |
| int page_request_id = 1) { |
| base::RunLoop run_loop; |
| blink::MediaStreamDevice audio_device; |
| |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop, |
| /*request_audio=*/true, |
| /*request_video=*/false, &audio_device, |
| /*audio_device=*/nullptr, |
| /*audio_share=*/true); |
| MediaStreamManager::DeviceStoppedCallback stopped_callback; |
| MediaStreamManager::DeviceChangedCallback changed_callback; |
| MediaStreamManager::DeviceRequestStateChangeCallback |
| request_state_change_callback; |
| MediaStreamManager::DeviceCaptureConfigurationChangeCallback |
| capture_configuration_change_callback; |
| MediaStreamManager::DeviceCaptureHandleChangeCallback |
| capture_handle_change_callback; |
| MediaStreamManager::ZoomLevelChangeCallback zoom_level_change_callback; |
| |
| media_stream_manager_->GenerateStreams( |
| render_frame_host_id, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), false /* user_gesture */, |
| std::move(selection_info), std::move(generate_stream_callback), |
| std::move(stopped_callback), std::move(changed_callback), |
| std::move(request_state_change_callback), |
| std::move(capture_configuration_change_callback), |
| std::move(capture_handle_change_callback), |
| std::move(zoom_level_change_callback)); |
| run_loop.Run(); |
| |
| return audio_device; |
| } |
| |
| void SetVideoCaptureDevices( |
| const std::vector<media::VideoCaptureDeviceInfo>& devices) { |
| ON_CALL(*video_capture_provider_, GetDeviceInfosAsync(_)) |
| .WillByDefault( |
| [devices]( |
| VideoCaptureProvider::GetDeviceInfosCallback result_callback) { |
| std::move(result_callback) |
| .Run(media::mojom::DeviceEnumerationResult::kSuccess, |
| devices); |
| }); |
| } |
| |
| std::unique_ptr<MockAudioManager> audio_manager_; |
| std::unique_ptr<media::AudioSystem> audio_system_; |
| |
| // media_stream_manager_ needs to outlive task_environment_ because it is a |
| // CurrentThread::DestructionObserver. audio_manager_ needs to outlive |
| // task_environment_ because it uses the underlying message loop. |
| std::unique_ptr<MediaStreamManager> media_stream_manager_; |
| std::unique_ptr<MockMediaObserver> media_observer_; |
| std::unique_ptr<TestBrowserClient> browser_content_client_; |
| content::BrowserTaskEnvironment task_environment_; |
| raw_ptr<MockVideoCaptureProvider> video_capture_provider_; |
| std::unique_ptr<MediaStreamProviderListenerMock> stream_provider_listener_; |
| size_t screen_count_ = 0; |
| base::RunLoop run_loop_; |
| }; |
| |
| TEST_F(MediaStreamManagerTest, MakeMediaAccessRequest) { |
| MakeMediaAccessRequest(0); |
| EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(_, _, _, _, _, _)) |
| .Times(testing::AtLeast(1)); |
| |
| // Expecting the callback will be triggered and quit the test. |
| EXPECT_CALL(*this, Response(0)); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MakeAndCancelMediaAccessRequest) { |
| std::string label = MakeMediaAccessRequest(0); |
| |
| // Request cancellation notifies closing of all stream types. |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| media_stream_manager_->CancelRequest(label); |
| run_loop_.RunUntilIdle(); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MakeMultipleRequests) { |
| // First request. |
| std::string label1 = MakeMediaAccessRequest(0); |
| |
| // Second request. |
| |
| const GlobalRenderFrameHostId render_frame_host_id{3, 4}; |
| int requester_id = 2; |
| int page_request_id = 2; |
| url::Origin security_origin; |
| blink::StreamControls controls(true, true); |
| MediaStreamManager::MediaAccessRequestCallback callback = base::BindOnce( |
| &MediaStreamManagerTest::ResponseCallback, base::Unretained(this), 1); |
| std::string label2 = media_stream_manager_->MakeMediaAccessRequest( |
| render_frame_host_id, requester_id, page_request_id, controls, |
| security_origin, std::move(callback)); |
| |
| // Expecting the callbackS from requests will be triggered and quit the test. |
| // Note, the callbacks might come in a different order depending on the |
| // value of labels. |
| EXPECT_CALL(*this, Response(0)); |
| EXPECT_CALL(*this, Response(1)); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MakeAndCancelMultipleRequests) { |
| std::string label1 = MakeMediaAccessRequest(0); |
| std::string label2 = MakeMediaAccessRequest(1); |
| |
| // Cancelled request notifies closing of all stream types. |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL(*media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)); |
| |
| media_stream_manager_->CancelRequest(label1); |
| |
| // The request that proceeds sets state to MEDIA_REQUEST_STATE_REQUESTED when |
| // starting a device enumeration, and to MEDIA_REQUEST_STATE_PENDING_APPROVAL |
| // when the enumeration is completed and also when the request is posted to |
| // the UI. |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_REQUESTED)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_REQUESTED)); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_PENDING_APPROVAL)) |
| .Times(2); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_PENDING_APPROVAL)) |
| .Times(2); |
| |
| // Expecting the callback from the second request will be triggered and |
| // quit the test. |
| EXPECT_CALL(*this, Response(1)); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(MediaStreamManagerTest, GenerateSameStreamForAudioDevice) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| true /* tests_use_fake_render_frame_hosts */); |
| })); |
| |
| const int num_call_iterations = 3; |
| |
| // Test that if |info.strategy| has value SEARCH_BY_DEVICE_ID, we only create |
| // a single session for a device. |
| std::set<base::UnguessableToken> session_ids; |
| for (int i = 0; i < num_call_iterations; ++i) { |
| blink::MediaStreamDevice audio_device = CreateOrSearchAudioDeviceStream( |
| blink::mojom::StreamSelectionInfo::NewSearchOnlyByDeviceId({})); |
| |
| EXPECT_EQ(audio_device.id, "default"); |
| EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| audio_device.type); |
| EXPECT_TRUE(audio_device.session_id()); |
| session_ids.insert(audio_device.session_id()); |
| } |
| EXPECT_EQ(session_ids.size(), 1u); |
| } |
| |
| TEST_F(MediaStreamManagerTest, GenerateDifferentStreamsForAudioDevice) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| true /* tests_use_fake_render_frame_hosts */); |
| })); |
| |
| const size_t num_call_iterations = 3; |
| |
| // Test that if |info.strategy| is provided as FORCE_NEW_STREAM, we create a |
| // new stream each time. |
| std::set<base::UnguessableToken> session_ids; |
| for (size_t i = 0; i < num_call_iterations; ++i) { |
| blink::MediaStreamDevice audio_device = |
| CreateOrSearchAudioDeviceStream(NewSearchBySessionId({})); |
| |
| EXPECT_EQ(audio_device.id, "default"); |
| EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| audio_device.type); |
| EXPECT_TRUE(audio_device.session_id()); |
| session_ids.insert(audio_device.session_id()); |
| } |
| EXPECT_EQ(session_ids.size(), num_call_iterations); |
| } |
| |
| TEST_F(MediaStreamManagerTest, GenerateAndReuseStreamForAudioDevice) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| true /* tests_use_fake_render_frame_hosts */); |
| })); |
| |
| const int num_call_iterations = 3; |
| |
| // Test that `StreamSelectionInfo` is provided as `SearchBySessionId` with |
| // the id set to an non-existing ID a new stream is provided and |
| // that if the ID is valid, that the stream is reused. |
| auto token = base::UnguessableToken::Create(); |
| blink::MediaStreamDevice reference_device = |
| CreateOrSearchAudioDeviceStream(NewSearchBySessionId( |
| {{media::AudioDeviceDescription::kDefaultDeviceId, token}})); |
| EXPECT_NE(reference_device.session_id(), token); |
| |
| for (int i = 0; i < num_call_iterations; ++i) { |
| blink::MediaStreamDevice audio_device = |
| CreateOrSearchAudioDeviceStream(NewSearchBySessionId({ |
| {media::AudioDeviceDescription::kDefaultDeviceId, |
| reference_device.session_id()}, |
| {GetAudioInputDeviceId(0), token}, |
| })); |
| EXPECT_EQ(audio_device.id, media::AudioDeviceDescription::kDefaultDeviceId); |
| EXPECT_EQ(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| audio_device.type); |
| EXPECT_TRUE(audio_device.session_id()); |
| EXPECT_EQ(audio_device.session_id(), reference_device.session_id()); |
| } |
| } |
| |
| TEST_F(MediaStreamManagerTest, GetDisplayMediaRequestVideoOnly) { |
| RequestAndStopGetDisplayMedia( |
| /*app_requested_audio=*/false, |
| /*user_shared_audio=*/false, |
| /*additional_devices=*/{}, |
| /*verify_devices_callback=*/ |
| base::BindOnce([](const blink::MediaStreamDevice& video_device, |
| const blink::MediaStreamDevice& audio_device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| video_device.type); |
| EXPECT_EQ(blink::mojom::MediaStreamType::NO_SERVICE, audio_device.type); |
| })); |
| } |
| |
| TEST_F(MediaStreamManagerTest, GetDisplayMediaRequestAudioAndVideo) { |
| RequestAndStopGetDisplayMedia( |
| /*app_requested_audio=*/true, |
| /*user_shared_audio=*/true, |
| /*additional_devices=*/{}, |
| /*verify_devices_callback=*/ |
| base::BindOnce([](const blink::MediaStreamDevice& video_device, |
| const blink::MediaStreamDevice& audio_device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| video_device.type); |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| audio_device.type); |
| })); |
| } |
| |
| // The application requested audio, but the user deselected sharing of audio. |
| TEST_F(MediaStreamManagerTest, |
| GetDisplayMediaRequestAudioAndVideoNoAudioShare) { |
| RequestAndStopGetDisplayMedia( |
| /*app_requested_audio=*/true, |
| /*user_shared_audio=*/false, |
| /*additional_devices=*/{}, |
| /*verify_devices_callback=*/ |
| base::BindOnce([](const blink::MediaStreamDevice& video_device, |
| const blink::MediaStreamDevice& audio_device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| video_device.type); |
| EXPECT_EQ(blink::mojom::MediaStreamType::NO_SERVICE, audio_device.type); |
| })); |
| } |
| |
| TEST_F(MediaStreamManagerTest, |
| GetDisplayMediaRequestApplicationAudioShareIsHashed) { |
| blink::MediaStreamDevice application_loopback_device( |
| blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| media::CreateApplicationLoopbackDeviceId(12345), "Application Capture"); |
| |
| auto salt_and_origin = MediaDeviceSaltAndOrigin::Empty(); |
| const std::string hashed_application_loopback_device_id = |
| GetHMACForRawMediaDeviceID(salt_and_origin, |
| application_loopback_device.id); |
| |
| RequestAndStopGetDisplayMedia( |
| /*app_requested_audio=*/true, |
| /*user_shared_audio=*/true, |
| /*additional_devices=*/ |
| {application_loopback_device}, |
| /*verify_devices_callback=*/ |
| base::BindOnce( |
| [](const std::string hashed_application_loopback_device_id, |
| const blink::MediaStreamDevice& video_device, |
| const blink::MediaStreamDevice& audio_device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| video_device.type); |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE, |
| audio_device.type); |
| EXPECT_EQ(hashed_application_loopback_device_id, audio_device.id); |
| }, |
| hashed_application_loopback_device_id)); |
| } |
| |
| TEST_F(MediaStreamManagerTest, GetDisplayMediaRequestCallsUIProxy) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating( |
| [](base::RunLoop* run_loop) { |
| auto mock_ui = std::make_unique<MockMediaStreamUIProxy>(); |
| EXPECT_CALL(*mock_ui, MockRequestAccess(_, _)) |
| .WillOnce([run_loop](std::unique_ptr<MediaStreamRequest>& request, |
| testing::Unused) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| request->video_type); |
| run_loop->Quit(); |
| }); |
| return std::unique_ptr<FakeMediaStreamUIProxy>(std::move(mock_ui)); |
| }, |
| &run_loop_)); |
| blink::StreamControls controls(false /* request_audio */, |
| true /* request_video */); |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; |
| |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce([](blink::mojom::MediaStreamRequestResult result, |
| const std::string& label, |
| blink::mojom::StreamDevicesSetPtr stream_devices_set, |
| bool pan_tilt_zoom_allowed) {}); |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| MEDIA_REQUEST_STATE_PENDING_APPROVAL)); |
| |
| const GlobalRenderFrameHostId render_frame_host_id{0, 0}; |
| const int requester_id = 0; |
| const int page_request_id = 0; |
| media_stream_manager_->GenerateStreams( |
| render_frame_host_id, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), false /* user_gesture */, |
| StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), |
| MediaStreamManager::DeviceStoppedCallback(), |
| MediaStreamManager::DeviceChangedCallback(), |
| MediaStreamManager::DeviceRequestStateChangeCallback(), |
| MediaStreamManager::DeviceCaptureConfigurationChangeCallback(), |
| MediaStreamManager::DeviceCaptureHandleChangeCallback(), |
| base::DoNothing()); |
| run_loop_.Run(); |
| |
| EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(_, _, _, _, _, _)) |
| .Times(testing::AtLeast(1)); |
| media_stream_manager_->CancelAllRequests(render_frame_host_id, 0); |
| } |
| |
| TEST_F(MediaStreamManagerTest, DesktopCaptureDeviceStopped) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| })); |
| |
| blink::StreamControls controls(false /* request_audio */, |
| true /* request_video */); |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE; |
| |
| const int requester_id = 1; |
| const int page_request_id = 1; |
| |
| blink::MediaStreamDevice video_device; |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop_, |
| /*request_audio=*/false, |
| /*request_video=*/true, /*audio_device=*/nullptr, |
| &video_device, |
| /*audio_share=*/true); |
| MediaStreamManager::DeviceStoppedCallback stopped_callback = |
| base::BindRepeating( |
| [](const std::string& label, const blink::MediaStreamDevice& device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| device.type); |
| EXPECT_NE(DesktopMediaID::TYPE_NONE, |
| DesktopMediaID::Parse(device.id).type); |
| }); |
| MediaStreamManager::DeviceChangedCallback changed_callback; |
| EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(_, _, _, _, _, _)) |
| .Times(testing::AtLeast(1)); |
| MediaStreamManager::DeviceRequestStateChangeCallback |
| request_state_change_callback; |
| MediaStreamManager::DeviceCaptureConfigurationChangeCallback |
| capture_configuration_change_callback; |
| MediaStreamManager::DeviceCaptureHandleChangeCallback |
| capture_handle_change_callback; |
| MediaStreamManager::ZoomLevelChangeCallback zoom_level_change_callback; |
| |
| media_stream_manager_->GenerateStreams( |
| kRenderFrameHostId, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), false /* user_gesture */, |
| StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), std::move(stopped_callback), |
| std::move(changed_callback), std::move(request_state_change_callback), |
| std::move(capture_configuration_change_callback), |
| std::move(capture_handle_change_callback), |
| std::move(zoom_level_change_callback)); |
| run_loop_.Run(); |
| EXPECT_EQ(controls.video.stream_type, video_device.type); |
| EXPECT_NE(DesktopMediaID::TYPE_NONE, |
| DesktopMediaID::Parse(video_device.id).type); |
| |
| // |request_label| is cached in the |device.name| for testing purpose. |
| std::string request_label = video_device.name; |
| media_stream_manager_->StopMediaStreamFromBrowser(request_label); |
| |
| media_stream_manager_->StopStreamDevice(kRenderFrameHostId, requester_id, |
| video_device.id, |
| video_device.session_id()); |
| } |
| |
| TEST_F(MediaStreamManagerTest, DesktopCaptureDeviceChanged) { |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| })); |
| |
| blink::StreamControls controls(false /* request_audio */, |
| true /* request_video */); |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE; |
| const int requester_id = 1; |
| const int page_request_id = 1; |
| |
| blink::MediaStreamDevice video_device; |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop_, |
| /*request_audio=*/false, |
| /*request_video=*/true, /*audio_device=*/nullptr, |
| &video_device, |
| /*audio_share=*/true); |
| MediaStreamManager::DeviceStoppedCallback stopped_callback; |
| MediaStreamManager::DeviceChangedCallback changed_callback = |
| base::BindRepeating( |
| [](blink::MediaStreamDevice* video_device, const std::string& label, |
| const blink::MediaStreamDevice& old_device, |
| const blink::MediaStreamDevice& new_device) { |
| EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| old_device.type); |
| EXPECT_NE(DesktopMediaID::TYPE_NONE, |
| DesktopMediaID::Parse(old_device.id).type); |
| EXPECT_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| new_device.type); |
| EXPECT_NE(DesktopMediaID::TYPE_NONE, |
| DesktopMediaID::Parse(new_device.id).type); |
| *video_device = new_device; |
| }, |
| &video_device); |
| EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(_, _, _, _, _, _)) |
| .Times(testing::AtLeast(1)); |
| MediaStreamManager::DeviceRequestStateChangeCallback |
| request_state_change_callback; |
| MediaStreamManager::DeviceCaptureConfigurationChangeCallback |
| capture_configuration_change_callback; |
| MediaStreamManager::DeviceCaptureHandleChangeCallback |
| capture_handle_change_callback; |
| MediaStreamManager::ZoomLevelChangeCallback zoom_level_change_callback; |
| |
| media_stream_manager_->GenerateStreams( |
| kRenderFrameHostId, requester_id, page_request_id, controls, |
| MediaDeviceSaltAndOrigin::Empty(), false /* user_gesture */, |
| StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), std::move(stopped_callback), |
| std::move(changed_callback), std::move(request_state_change_callback), |
| std::move(capture_configuration_change_callback), |
| std::move(capture_handle_change_callback), |
| std::move(zoom_level_change_callback)); |
| run_loop_.Run(); |
| EXPECT_EQ(controls.video.stream_type, video_device.type); |
| EXPECT_NE(DesktopMediaID::TYPE_NONE, |
| DesktopMediaID::Parse(video_device.id).type); |
| |
| // |request_label| is cached in the |device.name| for testing purpose. |
| std::string request_label = video_device.name; |
| media_stream_manager_->ChangeMediaStreamSourceFromBrowser( |
| request_label, DesktopMediaID(), |
| /*captured_surface_control_active=*/false); |
| |
| // Wait to check callbacks before stopping the device. |
| base::RunLoop().RunUntilIdle(); |
| media_stream_manager_->StopStreamDevice(kRenderFrameHostId, requester_id, |
| video_device.id, |
| video_device.session_id()); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MultiCaptureOnMediaStreamUIWindowId) { |
| std::vector<media::VideoCaptureSessionId> session_ids; |
| VideoCaptureManager::SetDesktopCaptureWindowIdCallback callback = |
| base::BindLambdaForTesting( |
| [&session_ids](const media::VideoCaptureSessionId& session_id, |
| gfx::NativeViewId window_id) { |
| session_ids.push_back(session_id); |
| }); |
| media_stream_manager_->video_capture_manager() |
| ->set_desktop_capture_window_id_callback_for_testing(callback); |
| |
| gfx::NativeViewId native_view_id = 1; |
| blink::mojom::StreamDevicesSetPtr stream_devices_set = |
| blink::mojom::StreamDevicesSet::New(); |
| blink::MediaStreamDevice device_0 = blink::MediaStreamDevice( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, "screen:0:0", |
| "test_device_0"); |
| base::UnguessableToken session_id_0 = base::UnguessableToken::Create(); |
| device_0.set_session_id(session_id_0); |
| blink::MediaStreamDevice device_1 = blink::MediaStreamDevice( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, "screen:1:0", |
| "test_device_1"); |
| base::UnguessableToken session_id_1 = base::UnguessableToken::Create(); |
| device_1.set_session_id(session_id_1); |
| stream_devices_set->stream_devices.emplace_back( |
| blink::mojom::StreamDevices(std::nullopt, device_0).Clone()); |
| stream_devices_set->stream_devices.emplace_back( |
| blink::mojom::StreamDevices(std::nullopt, device_1).Clone()); |
| media_stream_manager_->OnMediaStreamUIWindowId( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE, |
| std::move(stream_devices_set), native_view_id); |
| ASSERT_EQ(2u, session_ids.size()); |
| ASSERT_EQ(session_id_0, session_ids[0]); |
| ASSERT_EQ(session_id_1, session_ids[1]); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MultiCaptureAllDevicesOpened) { |
| base::UnguessableToken session = base::UnguessableToken::Create(); |
| RequestMultiScreenCapture(/*screen_count=*/3u, session); |
| const std::vector<base::UnguessableToken>& session_ids = |
| stream_provider_listener_->capture_session_ids(); |
| EXPECT_EQ(3u, session_ids.size()); |
| |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[0]); |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[1]); |
| |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_DONE)); |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[2]); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MultiCaptureNotAllDevicesOpened) { |
| base::UnguessableToken session = base::UnguessableToken::Create(); |
| RequestMultiScreenCapture(/*screen_count=*/3u, session); |
| |
| const std::vector<base::UnguessableToken>& session_ids = |
| stream_provider_listener_->capture_session_ids(); |
| EXPECT_EQ(3u, session_ids.size()); |
| |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[0]); |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[1]); |
| |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_DONE)) |
| .Times(0); |
| } |
| |
| TEST_F(MediaStreamManagerTest, MultiCaptureIntermediateErrorOnOpening) { |
| base::UnguessableToken session = base::UnguessableToken::Create(); |
| RequestMultiScreenCapture(/*screen_count=*/3u, session); |
| |
| const std::vector<base::UnguessableToken>& session_ids = |
| stream_provider_listener_->capture_session_ids(); |
| EXPECT_EQ(3u, session_ids.size()); |
| |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_ERROR)); |
| |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[0]); |
| media_stream_manager_->SetStateForTesting( |
| /*request_index=*/0u, |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_ERROR); |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[1]); |
| media_stream_manager_->Opened( |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, session_ids[2]); |
| |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET, |
| MEDIA_REQUEST_STATE_DONE)) |
| .Times(0); |
| } |
| |
| TEST_F(MediaStreamManagerTest, RegisterUnregisterHosts) { |
| mojo::Remote<blink::mojom::MediaStreamDispatcherHost> dispatcher_client1; |
| media_stream_manager_->RegisterDispatcherHost( |
| std::make_unique<TestMediaStreamDispatcherHost>(), |
| dispatcher_client1.BindNewPipeAndPassReceiver()); |
| EXPECT_EQ(media_stream_manager_->num_dispatcher_hosts(), 1u); |
| |
| mojo::Remote<media::mojom::VideoCaptureHost> video_capture_client1; |
| media_stream_manager_->RegisterVideoCaptureHost( |
| std::make_unique<TestVideoCaptureHost>(), |
| video_capture_client1.BindNewPipeAndPassReceiver()); |
| EXPECT_EQ(media_stream_manager_->num_video_capture_hosts(), 1u); |
| |
| mojo::Remote<blink::mojom::MediaStreamDispatcherHost> dispatcher_client2; |
| media_stream_manager_->RegisterDispatcherHost( |
| std::make_unique<TestMediaStreamDispatcherHost>(), |
| dispatcher_client2.BindNewPipeAndPassReceiver()); |
| EXPECT_EQ(media_stream_manager_->num_dispatcher_hosts(), 2u); |
| |
| mojo::Remote<media::mojom::VideoCaptureHost> video_capture_client2; |
| media_stream_manager_->RegisterVideoCaptureHost( |
| std::make_unique<TestVideoCaptureHost>(), |
| video_capture_client2.BindNewPipeAndPassReceiver()); |
| EXPECT_EQ(media_stream_manager_->num_video_capture_hosts(), 2u); |
| |
| // Closing a pipe unregisters the corresponding host. |
| dispatcher_client1.reset(); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(media_stream_manager_->num_dispatcher_hosts(), 1u); |
| |
| video_capture_client1.reset(); |
| task_environment_.RunUntilIdle(); |
| EXPECT_EQ(media_stream_manager_->num_video_capture_hosts(), 1u); |
| |
| // Shutting down MediaStreamManager disconnects the remaining pipes. |
| EXPECT_TRUE(dispatcher_client2.is_connected()); |
| EXPECT_TRUE(video_capture_client2.is_connected()); |
| // `video_capture_provider_` is pointing at the MockVideoCaptureProvider |
| // owned by MediaDevicesManager and will be destroyed as part of calling |
| // WillDestroyCurrentMessageLoop. Clearing it here avoids a dangling |
| // pointer. |
| video_capture_provider_ = nullptr; |
| media_stream_manager_->WillDestroyCurrentMessageLoop(); |
| task_environment_.RunUntilIdle(); |
| EXPECT_FALSE(dispatcher_client2.is_connected()); |
| EXPECT_FALSE(video_capture_client2.is_connected()); |
| } |
| |
| class MediaStreamManagerTestWithWebContents : public MediaStreamManagerTest { |
| protected: |
| void SetUp() override { |
| web_contents_ = CreateWebContents(); |
| render_frame_host_ = web_contents_->GetPrimaryMainFrame(); |
| render_frame_host_id_ = render_frame_host_->GetGlobalId(); |
| |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| })); |
| } |
| |
| std::unique_ptr<TestWebContents> CreateWebContents() { |
| return TestWebContents::Create(&browser_context_, |
| SiteInstanceImpl::Create(&browser_context_)); |
| } |
| |
| void GenerateStreams( |
| GlobalRenderFrameHostId render_frame_host_id, |
| const blink::StreamControls& controls, |
| MediaDeviceSaltAndOrigin salt_and_origin, |
| MediaStreamManager::GenerateStreamsCallback generate_stream_cb) { |
| media_stream_manager_->GenerateStreams( |
| render_frame_host_id, /*requester_id=*/0, /*page_request_id=*/0, |
| controls, salt_and_origin, |
| /*user_gesture=*/false, |
| /*audio_stream_selection_info_ptr=*/ |
| NewSearchBySessionId({}), std::move(generate_stream_cb), |
| /*device_stopped_callback=*/base::DoNothing(), |
| /*device_changed_callback=*/base::DoNothing(), |
| /*device_request_state_change_cb*/ base::DoNothing(), |
| /*device_capture_configuration_change_callback=*/base::DoNothing(), |
| /*device_capture_handle_change_callback=*/base::DoNothing(), |
| /*zoom_level_change_callback=*/base::DoNothing()); |
| } |
| |
| std::vector<std::string> GetRawDeviceIdsOpenedForFrame( |
| RenderFrameHost* render_frame_host, |
| blink::mojom::MediaStreamType type) { |
| base::test::TestFuture<std::vector<std::string>> future; |
| media_stream_manager_->GetRawDeviceIdsOpenedForFrame( |
| render_frame_host, type, future.GetCallback()); |
| return future.Take(); |
| } |
| |
| RenderViewHostTestEnabler rvh_test_enabler_; |
| TestBrowserContext browser_context_; |
| std::unique_ptr<TestWebContents> web_contents_; |
| raw_ptr<RenderFrameHost> render_frame_host_; |
| GlobalRenderFrameHostId render_frame_host_id_; |
| }; |
| |
| TEST_F(MediaStreamManagerTestWithWebContents, |
| GetRawDeviceIdsOpenedForFrame_AudioCapture) { |
| const std::string kDevice1RawId = GetAudioInputDeviceId(0); |
| const std::string kDevice2RawId = GetAudioInputDeviceId(1); |
| |
| auto salt_and_origin = MediaDeviceSaltAndOrigin::Empty(); |
| const std::string kDevice1HMACId = |
| GetHMACForRawMediaDeviceID(salt_and_origin, kDevice1RawId); |
| const std::string kDevice2HMACId = |
| GetHMACForRawMediaDeviceID(salt_and_origin, kDevice2RawId); |
| |
| base::test::TestFuture<blink::mojom::MediaStreamRequestResult, |
| const std::string&, blink::mojom::StreamDevicesSetPtr, |
| bool> |
| streams_generated_future; |
| |
| GenerateStreams(render_frame_host_id_, GetAudioStreamControls(kDevice2HMACId), |
| salt_and_origin, streams_generated_future.GetCallback()); |
| |
| ASSERT_EQ(std::get<0>(streams_generated_future.Take()), |
| blink::mojom::MediaStreamRequestResult::OK) |
| << "GenerateStreams() call failed"; |
| |
| // Open device 1 on another render frame id. Device 1 shouldn't be |
| // included in the response from `GetRawDeviceIdsOpenedForFrame()`. |
| GenerateStreams({42, 38}, GetAudioStreamControls(kDevice1HMACId), |
| salt_and_origin, streams_generated_future.GetCallback()); |
| |
| ASSERT_EQ(std::get<0>(streams_generated_future.Take()), |
| blink::mojom::MediaStreamRequestResult::OK) |
| << "GenerateStreams() call failed"; |
| |
| EXPECT_THAT(GetRawDeviceIdsOpenedForFrame( |
| render_frame_host_, |
| blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE), |
| ElementsAre(kDevice2RawId)); |
| |
| // Querying with a different render frame returns nothing. |
| auto other_web_contents = CreateWebContents(); |
| EXPECT_TRUE(GetRawDeviceIdsOpenedForFrame( |
| other_web_contents->GetPrimaryMainFrame(), |
| blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) |
| .empty()); |
| // Querying with a different type returns nothing. |
| EXPECT_TRUE(GetRawDeviceIdsOpenedForFrame( |
| render_frame_host_, |
| blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) |
| .empty()); |
| } |
| |
| TEST_F(MediaStreamManagerTestWithWebContents, |
| GetRawDeviceIdsOpenedForFrame_VideoCapture) { |
| const char kDevice1RawId[] = "device_1"; |
| const char kDevice2RawId[] = "device_2"; |
| |
| std::vector<media::VideoCaptureDeviceInfo> devices{ |
| media::VideoCaptureDeviceInfo{media::VideoCaptureDeviceDescriptor{ |
| /*display_name=*/"Test 1", /*device_id=*/kDevice1RawId}}, |
| media::VideoCaptureDeviceInfo{media::VideoCaptureDeviceDescriptor{ |
| /*display_name=*/"Test 2", /*device_id=*/kDevice2RawId}}, |
| }; |
| SetVideoCaptureDevices(devices); |
| |
| auto salt_and_origin = MediaDeviceSaltAndOrigin::Empty(); |
| const std::string kDevice1HMACId = |
| GetHMACForRawMediaDeviceID(salt_and_origin, kDevice1RawId); |
| const std::string kDevice2HMACId = |
| GetHMACForRawMediaDeviceID(salt_and_origin, kDevice2RawId); |
| |
| base::test::TestFuture<blink::mojom::MediaStreamRequestResult, |
| const std::string&, blink::mojom::StreamDevicesSetPtr, |
| bool> |
| streams_generated_future; |
| |
| GenerateStreams(render_frame_host_id_, GetVideoStreamControls(kDevice2HMACId), |
| salt_and_origin, streams_generated_future.GetCallback()); |
| |
| ASSERT_EQ(std::get<0>(streams_generated_future.Take()), |
| blink::mojom::MediaStreamRequestResult::OK) |
| << "GenerateStreams() call failed"; |
| |
| // Open device 1 on another render frame id. Device 1 shouldn't be |
| // included in the response from `GetRawDeviceIdsOpenedForFrame()`. |
| GenerateStreams({42, 38}, GetVideoStreamControls(kDevice1HMACId), |
| salt_and_origin, streams_generated_future.GetCallback()); |
| |
| ASSERT_EQ(std::get<0>(streams_generated_future.Take()), |
| blink::mojom::MediaStreamRequestResult::OK) |
| << "GenerateStreams() call failed"; |
| |
| EXPECT_THAT(GetRawDeviceIdsOpenedForFrame( |
| render_frame_host_, |
| blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE), |
| ElementsAre(kDevice2RawId)); |
| |
| // Querying with a different render frame returns nothing. |
| auto other_web_contents = CreateWebContents(); |
| EXPECT_TRUE(GetRawDeviceIdsOpenedForFrame( |
| other_web_contents->GetPrimaryMainFrame(), |
| blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) |
| .empty()); |
| // Querying with a different type returns nothing. |
| EXPECT_TRUE(GetRawDeviceIdsOpenedForFrame( |
| render_frame_host_, |
| blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) |
| .empty()); |
| } |
| |
| class MediaStreamManagerTestForTransfers : public MediaStreamManagerTest { |
| public: |
| void CustomSetUp(const char* surface_type = "browser") { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kMediaStreamTrackTransfer); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, |
| base::StringPrintf("display-media-type=%s", surface_type)); |
| media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { |
| return std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| })); |
| } |
| |
| void RequestDeviceCaptureTypeAudioDevice() { |
| // Generate stream on first renderer. |
| original_device_ = |
| CreateOrSearchAudioDeviceStream(NewSearchBySessionId({})); |
| existing_device_session_id_ = original_device_.session_id(); |
| |
| EXPECT_EQ(original_device_.type, |
| blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| EXPECT_NE(transferred_device_.id, original_device_.id); |
| } |
| |
| void RequestDisplayCaptureTypeDevice(bool request_audio = true, |
| bool request_video = true, |
| bool transfer_audio = true) { |
| base::RunLoop run_loop; |
| |
| blink::StreamControls controls(request_audio, request_video); |
| if (request_audio) |
| controls.audio.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE; |
| if (request_video) |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; |
| |
| blink::MediaStreamDevice video_device; |
| blink::MediaStreamDevice audio_device; |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop, request_audio, |
| request_video, &audio_device, &video_device, |
| /*audio_share=*/true); |
| |
| media_stream_manager_->GenerateStreams( |
| render_frame_host_id_, requester_id_, |
| /*page_request_id=*/1, controls, MediaDeviceSaltAndOrigin::Empty(), |
| /*user_gesture=*/false, |
| blink::mojom::StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), |
| /*device_stopped_callback=*/base::DoNothing(), |
| /*device_changed_callback=*/base::DoNothing(), |
| /*device_request_state_change_callback=*/base::DoNothing(), |
| /*device_capture_configuration_change_callback=*/base::DoNothing(), |
| /*device_capture_handle_change_callback=*/base::DoNothing(), |
| /*zoom_level_change_callback=*/base::DoNothing()); |
| run_loop.Run(); |
| |
| original_device_ = transfer_audio ? audio_device : video_device; |
| existing_device_session_id_ = original_device_.session_id(); |
| EXPECT_NE(transferred_device_.id, original_device_.id); |
| } |
| |
| void GetOpenDevice() { |
| MediaStreamManager::GetOpenDeviceCallback get_open_device_cb = |
| base::BindLambdaForTesting( |
| [&](blink::mojom::MediaStreamRequestResult result, |
| blink::mojom::GetOpenDeviceResponsePtr response) { |
| result_ = result; |
| if (response) { |
| transferred_device_ = response->device; |
| } |
| }) |
| .Then(run_loop_.QuitClosure()); |
| |
| // GetOpenDevice is called on second renderer. |
| media_stream_manager_->GetOpenDevice( |
| existing_device_session_id_, transfer_id_, |
| GlobalRenderFrameHostId{3, 4}, /*requester_id=*/2, |
| /*page_request_id=*/2, MediaDeviceSaltAndOrigin::Empty(), |
| std::move(get_open_device_cb), |
| /*device_stopped_callback=*/base::DoNothing(), |
| /*device_changed_callback=*/base::DoNothing(), |
| /*device_request_state_change_callback=*/base::DoNothing(), |
| /*device_capture_configuration_change_callback=*/base::DoNothing(), |
| /*device_capture_handle_change_callback=*/base::DoNothing(), |
| /*zoom_level_change_callback=*/base::DoNothing()); |
| run_loop_.Run(); |
| } |
| |
| bool KeepDeviceAlive() { |
| // Call to KeepDeviceAlive from the first renderer. |
| return media_stream_manager_->KeepDeviceAliveForTransfer( |
| render_frame_host_id_, requester_id_, existing_device_session_id_, |
| transfer_id_); |
| } |
| |
| void StopDevice(bool should_stop = true) { |
| if (!should_stop) { |
| EXPECT_CALL( |
| *media_observer_, |
| OnMediaRequestStateChanged( |
| _, _, _, _, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| MEDIA_REQUEST_STATE_CLOSING)) |
| .Times(0); |
| } |
| |
| // Stop device from the first renderer. |
| media_stream_manager_->StopStreamDevice(render_frame_host_id_, |
| requester_id_, original_device_.id, |
| existing_device_session_id_); |
| } |
| |
| const GlobalRenderFrameHostId render_frame_host_id_ = kRenderFrameHostId; |
| const int requester_id_ = 1; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| base::UnguessableToken existing_device_session_id_ = |
| base::UnguessableToken::Create(); |
| const base::UnguessableToken transfer_id_ = base::UnguessableToken::Create(); |
| blink::MediaStreamDevice original_device_; |
| blink::MediaStreamDevice transferred_device_; |
| std::optional<blink::mojom::MediaStreamRequestResult> result_; |
| }; |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceForScreenCaptureTypeStreamFails) { |
| CustomSetUp(/*surface_type=*/"monitor"); |
| RequestDisplayCaptureTypeDevice(); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceForWindowCaptureTypeStreamFails) { |
| CustomSetUp(/*surface_type=*/"window"); |
| RequestDisplayCaptureTypeDevice(); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceForBrowserCaptureTypeStreamReturnsDevice) { |
| CustomSetUp(/*surface_type=*/"browser"); |
| RequestDisplayCaptureTypeDevice(); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceForDeviceCaptureTypeStreamFails) { |
| CustomSetUp(); |
| RequestDeviceCaptureTypeAudioDevice(); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetDisplayMediaAudioAndVideoAndGetOpenDeviceVideoReturnsDevice) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(/*request_audio=*/true, |
| /*request_video=*/true, |
| /*transfer_audio=*/false); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetDisplayMediaVideoAndGetOpenDeviceVideoReturnsDevice) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(/*request_audio=*/false, |
| /*request_video=*/true, |
| /*transfer_audio=*/false); |
| GetOpenDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceWhenKeepAliveAfterStopDoesNotReturnDevice) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(); |
| StopDevice(); |
| EXPECT_FALSE(KeepDeviceAlive()); |
| GetOpenDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceWhenKeepAliveBeforeStopReturnsDevice) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| StopDevice(); |
| GetOpenDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceWithoutKeepAliveReturnsDeviceButDoesNotStop) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(); |
| GetOpenDevice(); |
| StopDevice(/*should_stop=*/false); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceWithKeepAliveAfterStopReturnsDevice) { |
| CustomSetUp(); |
| RequestDisplayCaptureTypeDevice(); |
| GetOpenDevice(); |
| StopDevice(); |
| EXPECT_TRUE(KeepDeviceAlive()); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); |
| EXPECT_EQ(transferred_device_.id, original_device_.id); |
| EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); |
| } |
| |
| TEST_F(MediaStreamManagerTestForTransfers, |
| GetOpenDeviceForNonExistentDeviceReturnsInvalidState) { |
| CustomSetUp(); |
| GetOpenDevice(); |
| |
| EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| class MediaStreamManagerCapturedSurfaceControlTest |
| : public MediaStreamManagerTest { |
| public: |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, |
| base::StringPrintf("display-media-type=browser")); |
| } |
| |
| void SimulateGetDisplayMedia( |
| GlobalRenderFrameHostId rfhid = GlobalRenderFrameHostId{1, 2}, |
| std::optional<WebContentsMediaCaptureId> captured_tab_id = std::nullopt) { |
| media_stream_manager_->UseFakeUIFactoryForTests( |
| base::BindRepeating([]() { |
| auto fake_ui = std::make_unique<FakeMediaStreamUIProxy>( |
| /*tests_use_fake_render_frame_hosts=*/true); |
| fake_ui->SetAudioShare(true); |
| return std::unique_ptr<FakeMediaStreamUIProxy>(std::move(fake_ui)); |
| }), |
| /*use_for_gum_desktop_capture=*/false, captured_tab_id); |
| |
| SetMediaObserverExpectations(/*app_requested_audio=*/true, |
| /*user_shared_audio=*/true); |
| |
| base::RunLoop run_loop; |
| |
| blink::StreamControls controls(/*request_audio=*/true, |
| /*request_video=*/true); |
| controls.audio.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE; |
| controls.video.stream_type = |
| blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; |
| |
| MediaStreamManager::GenerateStreamsCallback generate_stream_callback = |
| base::BindOnce(GenerateStreamsCallback, &run_loop, |
| /*request_audio=*/true, |
| /*request_video=*/true, &audio_device_, &video_device_, |
| /*audio_share=*/true); |
| |
| media_stream_manager_->GenerateStreams( |
| rfhid, /*requester_id=*/1, |
| /*page_request_id=*/1, controls, MediaDeviceSaltAndOrigin::Empty(), |
| /*user_gesture=*/true, StreamSelectionInfo::NewSearchOnlyByDeviceId({}), |
| std::move(generate_stream_callback), |
| /*device_stopped_callback=*/base::DoNothing(), |
| /*device_changed_callback=*/base::DoNothing(), |
| /*device_request_state_change_callback=*/base::DoNothing(), |
| /*device_capture_configuration_change_callback=*/base::DoNothing(), |
| /*device_capture_handle_change_callback=*/base::DoNothing(), |
| /*zoom_level_change_callback=*/ |
| base::BindRepeating( |
| &MediaStreamManagerCapturedSurfaceControlTest::OnZoomLevelChange, |
| base::Unretained(this))); |
| run_loop.Run(); |
| } |
| |
| void OnZoomLevelChange(const std::string& label, |
| const blink::MediaStreamDevice& device, |
| int zoom_level) { |
| on_zoom_level_change_device_id_ = device.id; |
| on_zoom_level_change_zoom_level_ = zoom_level; |
| } |
| |
| void SetCapturedSurfaceControllerFactory( |
| GlobalRenderFrameHostId gdm_rfhid, |
| WebContentsMediaCaptureId captured_wc_id) { |
| media_stream_manager_->SetCapturedSurfaceControllerFactoryForTesting( |
| base::BindRepeating( |
| [](base::RepeatingCallback<void(int)>* received_zoom_level_callback, |
| GlobalRenderFrameHostId gdm_rfhid, |
| WebContentsMediaCaptureId captured_wc_id, |
| base::RepeatingCallback<void(int)> zoom_level_callback) { |
| *received_zoom_level_callback = zoom_level_callback; |
| auto captured_surface_controller = |
| std::make_unique<MockCapturedSurfaceController>( |
| gdm_rfhid, captured_wc_id); |
| captured_surface_controller->SetSendWheelResponse( |
| CapturedSurfaceControlResult::kSuccess); |
| captured_surface_controller->SetUpdateZoomLevelResponse( |
| CapturedSurfaceControlResult::kSuccess); |
| captured_surface_controller->SetRequestPermissionResponse( |
| CapturedSurfaceControlResult::kSuccess); |
| return base::WrapUnique<CapturedSurfaceController>( |
| captured_surface_controller.release()); |
| }, |
| &received_zoom_level_callback_)); |
| } |
| |
| base::OnceCallback<void(CapturedSurfaceControlResult)> MakeCallback() { |
| return base::BindOnce( |
| [](std::optional<CapturedSurfaceControlResult>* result_opt, |
| CapturedSurfaceControlResult result) { |
| CHECK(result_opt); |
| EXPECT_FALSE(result_opt->has_value()); |
| *result_opt = result; |
| }, |
| &result_); |
| } |
| |
| void SendWheel( |
| GlobalRenderFrameHostId gdm_rfhid, |
| std::optional<base::UnguessableToken> session_id = std::nullopt) { |
| media_stream_manager_->SendWheel( |
| gdm_rfhid, session_id.value_or(video_device_.session_id()), |
| MakeCapturedWheelActionPtr(), MakeCallback()); |
| } |
| |
| void UpdateZoomLevel(GlobalRenderFrameHostId gdm_rfhid, |
| std::optional<base::UnguessableToken> session_id, |
| ZoomLevelAction action) { |
| media_stream_manager_->UpdateZoomLevel( |
| gdm_rfhid, session_id.value_or(video_device_.session_id()), action, |
| MakeCallback()); |
| } |
| |
| void RequestPermission( |
| GlobalRenderFrameHostId gdm_rfhid, |
| std::optional<base::UnguessableToken> session_id = std::nullopt) { |
| media_stream_manager_->RequestCapturedSurfaceControlPermission( |
| gdm_rfhid, session_id.value_or(video_device_.session_id()), |
| MakeCallback()); |
| } |
| |
| blink::MediaStreamDevice video_device_; |
| blink::MediaStreamDevice audio_device_; |
| |
| std::optional<CapturedSurfaceControlResult> result_; |
| base::RepeatingCallback<void(int)> received_zoom_level_callback_; |
| std::string on_zoom_level_change_device_id_; |
| std::optional<int> on_zoom_level_change_zoom_level_; |
| }; |
| |
| TEST_F(MediaStreamManagerCapturedSurfaceControlTest, ZoomLevelChangeEvent) { |
| SCOPED_TRACE("ZoomLevelChangeEvent"); |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| received_zoom_level_callback_.Run(90); |
| EXPECT_EQ(on_zoom_level_change_device_id_, video_device_.id); |
| EXPECT_EQ(on_zoom_level_change_zoom_level_, 90); |
| } |
| |
| class MediaStreamManagerCapturedSurfaceControlActionTest |
| : public MediaStreamManagerCapturedSurfaceControlTest, |
| public testing::WithParamInterface<CapturedSurfaceControlAPI> { |
| public: |
| MediaStreamManagerCapturedSurfaceControlActionTest() |
| : tested_api_(GetParam()) {} |
| |
| void RunTestedAction( |
| GlobalRenderFrameHostId gdm_rfhid, |
| std::optional<base::UnguessableToken> session_id = std::nullopt) { |
| switch (tested_api_) { |
| case CapturedSurfaceControlAPI::kSendWheel: { |
| SendWheel(gdm_rfhid, session_id); |
| return; |
| } |
| case CapturedSurfaceControlAPI::kIncreaseZoomLevel: |
| case CapturedSurfaceControlAPI::kDecreaseZoomLevel: |
| case CapturedSurfaceControlAPI::kResetZoomLevel: { |
| UpdateZoomLevel(gdm_rfhid, session_id, ToZoomLevelAction(tested_api_)); |
| return; |
| } |
| case CapturedSurfaceControlAPI::kRequestPermission: { |
| RequestPermission(gdm_rfhid, session_id); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| const CapturedSurfaceControlAPI tested_api_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| MediaStreamManagerCapturedSurfaceControlActionTest, |
| testing::Values(CapturedSurfaceControlAPI::kSendWheel, |
| CapturedSurfaceControlAPI::kIncreaseZoomLevel, |
| CapturedSurfaceControlAPI::kDecreaseZoomLevel, |
| CapturedSurfaceControlAPI::kResetZoomLevel, |
| CapturedSurfaceControlAPI::kRequestPermission)); |
| |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, SuccessfulIfValid) { |
| SCOPED_TRACE("SuccessfulIfValid"); |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(gdm_rfhid); |
| EXPECT_EQ(result_, CapturedSurfaceControlResult::kSuccess); |
| } |
| |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, |
| FailsIfInvalidSessionId) { |
| SCOPED_TRACE("FailsIfInvalidSessionId"); |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(gdm_rfhid, base::UnguessableToken::Create()); |
| EXPECT_EQ(result_, |
| CapturedSurfaceControlResult::kCapturedSurfaceNotFoundError); |
| } |
| |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, |
| FailsIfNotCapturerRfhId) { |
| SCOPED_TRACE("FailsIfNotCapturerRfhId"); |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const GlobalRenderFrameHostId other_rfhid{11, 22}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(other_rfhid); |
| EXPECT_EQ(result_, CapturedSurfaceControlResult::kUnknownError); |
| } |
| |
| // This test is currently disabled because the code that ensures that Captured |
| // Surface Control APIs are disallowed for self-capture has not yet been |
| // authored. |
| // TODO(crbug.com/41484336): Enable this test. |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, |
| DISABLED_FailsIfSelfCapture) { |
| SCOPED_TRACE("FailsIfSelfCapture"); |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{1, 2}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(gdm_rfhid); |
| // TODO(crbug.com/41485502): Use a dedicated error. |
| EXPECT_EQ(result_, CapturedSurfaceControlResult::kUnknownError); |
| } |
| |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, |
| FailsIfCapturingWindow) { |
| SCOPED_TRACE("FailsIfCapturingWindow"); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, |
| base::StringPrintf("display-media-type=window")); |
| |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(gdm_rfhid); |
| // TODO(crbug.com/41485502): Use a dedicated error. |
| EXPECT_EQ(result_, CapturedSurfaceControlResult::kUnknownError); |
| } |
| |
| TEST_P(MediaStreamManagerCapturedSurfaceControlActionTest, |
| FailsIfCapturingScreen) { |
| SCOPED_TRACE("FailsIfCapturingScreen"); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kUseFakeDeviceForMediaStream, |
| base::StringPrintf("display-media-type=monitor")); |
| |
| const GlobalRenderFrameHostId gdm_rfhid{1, 2}; |
| const WebContentsMediaCaptureId captured_wc_id{3, 4}; |
| |
| SetCapturedSurfaceControllerFactory(gdm_rfhid, captured_wc_id); |
| SimulateGetDisplayMedia(gdm_rfhid, captured_wc_id); |
| |
| RunTestedAction(gdm_rfhid); |
| // TODO(crbug.com/41485502): Use a dedicated error. |
| EXPECT_EQ(result_, CapturedSurfaceControlResult::kUnknownError); |
| } |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) |
| |
| } // namespace content |