| // Copyright 2020 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/media_capture_devices_dispatcher.h" |
| |
| #include <memory> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/media/media_access_handler.h" |
| #include "chrome/browser/media/prefs/capture_device_ranking.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.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" |
| |
| using testing::_; |
| |
| namespace { |
| |
| class MockMediaAccessHandler : public MediaAccessHandler { |
| public: |
| explicit MockMediaAccessHandler( |
| const blink::mojom::MediaStreamType supported_type) |
| : supported_type_(supported_type) {} |
| |
| bool SupportsStreamType(content::RenderFrameHost* render_frame_host, |
| const blink::mojom::MediaStreamType type, |
| const extensions::Extension* extension) override { |
| return supported_type_ == type; |
| } |
| |
| MOCK_METHOD4(CheckMediaAccessPermission, |
| bool(content::RenderFrameHost* render_frame_host, |
| const url::Origin& security_origin, |
| blink::mojom::MediaStreamType type, |
| const extensions::Extension* extension)); |
| MOCK_METHOD4(HandleRequest, |
| void(content::WebContents* web_contents, |
| const content::MediaStreamRequest& request, |
| content::MediaResponseCallback callback, |
| const extensions::Extension* extension)); |
| MOCK_METHOD4(UpdateVideoScreenCaptureStatus, |
| void(int render_process_id, |
| int render_frame_id, |
| int page_request_id, |
| bool is_secure)); |
| |
| const blink::mojom::MediaStreamType supported_type_; |
| }; |
| |
| blink::MediaStreamDevices CreateFakeDevices( |
| blink::mojom::MediaStreamType type) { |
| blink::MediaStreamDevices devices; |
| devices.reserve(3); |
| for (size_t i = 0; i < devices.capacity(); ++i) { |
| devices.emplace_back(type, "id_" + base::NumberToString(i), |
| "name " + base::NumberToString(i)); |
| } |
| return devices; |
| } |
| |
| std::vector<std::string> GetIds(const blink::MediaStreamDevices& devices, |
| size_t start_index) { |
| CHECK_LT(start_index, devices.size()); |
| std::vector<std::string> device_ids; |
| for (auto it = devices.begin() + start_index; it != devices.end(); ++it) { |
| device_ids.push_back(it->id); |
| } |
| return device_ids; |
| } |
| |
| } // namespace |
| |
| class MediaCaptureDevicesDispatcherTest |
| : public ChromeRenderViewHostTestHarness { |
| public: |
| MediaCaptureDevicesDispatcherTest() = default; |
| ~MediaCaptureDevicesDispatcherTest() override = default; |
| |
| void SetUp() override { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| dispatcher_ = MediaCaptureDevicesDispatcher::GetInstance(); |
| } |
| |
| std::vector<std::unique_ptr<MediaAccessHandler>>& media_access_handlers() { |
| return dispatcher_->media_access_handlers_; |
| } |
| |
| void UpdateVideoScreenCaptureStatus( |
| const blink::mojom::MediaStreamType type) { |
| dispatcher_->UpdateVideoScreenCaptureStatus(0, 0, 0, type, false); |
| } |
| |
| void UpdateAudioDevicePreferenceRanking( |
| const typename blink::MediaStreamDevices::const_iterator |
| preferred_device_iter) { |
| CHECK(profile()->GetPrefs()); |
| media_prefs::UpdateAudioDevicePreferenceRanking( |
| *profile()->GetPrefs(), preferred_device_iter, |
| dispatcher_->GetAudioCaptureDevices()); |
| } |
| |
| void UpdateVideoDevicePreferenceRanking( |
| const typename blink::MediaStreamDevices::const_iterator |
| preferred_device_iter) { |
| CHECK(profile()->GetPrefs()); |
| media_prefs::UpdateVideoDevicePreferenceRanking( |
| *profile()->GetPrefs(), preferred_device_iter, |
| dispatcher_->GetVideoCaptureDevices()); |
| } |
| |
| protected: |
| raw_ptr<MediaCaptureDevicesDispatcher> dispatcher_; |
| }; |
| |
| TEST_F(MediaCaptureDevicesDispatcherTest, |
| DISABLED_LoopsAllMediaAccessHandlers) { |
| media_access_handlers().clear(); |
| |
| // Add two handlers. |
| auto stream_type1 = blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; |
| auto mock1 = std::make_unique<MockMediaAccessHandler>(stream_type1); |
| MockMediaAccessHandler* handler1 = mock1.get(); |
| media_access_handlers().push_back(std::move(mock1)); |
| auto stream_type2 = blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE; |
| auto mock2 = std::make_unique<MockMediaAccessHandler>(stream_type2); |
| MockMediaAccessHandler* handler2 = mock2.get(); |
| media_access_handlers().push_back(std::move(mock2)); |
| |
| // Expect both to be called. |
| EXPECT_CALL(*handler1, UpdateVideoScreenCaptureStatus(_, _, _, _)); |
| UpdateVideoScreenCaptureStatus(stream_type1); |
| EXPECT_CALL(*handler2, UpdateVideoScreenCaptureStatus(_, _, _, _)); |
| UpdateVideoScreenCaptureStatus(stream_type2); |
| } |
| |
| TEST_F(MediaCaptureDevicesDispatcherTest, |
| GetPreferredAudioDeviceForBrowserContext) { |
| const auto kFakeAudioDevices = |
| CreateFakeDevices(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| dispatcher_->SetTestAudioCaptureDevices(kFakeAudioDevices); |
| UpdateAudioDevicePreferenceRanking( |
| dispatcher_->GetAudioCaptureDevices().end() - 1); |
| UpdateAudioDevicePreferenceRanking( |
| dispatcher_->GetAudioCaptureDevices().begin()); |
| // Ranking at this point is [device_0, device_2, device_1]. |
| |
| // Exclude the first device from the eligible list to exercise filtering. |
| const auto eligible_device_ids = GetIds(kFakeAudioDevices, 1); |
| const auto preferred_device = |
| dispatcher_->GetPreferredAudioDeviceForBrowserContext( |
| browser_context(), eligible_device_ids); |
| ASSERT_TRUE(preferred_device->IsSameDevice(kFakeAudioDevices.back())); |
| } |
| |
| TEST_F( |
| MediaCaptureDevicesDispatcherTest, |
| GetPreferredAudioDeviceForBrowserContext_EmptyEligibleDevicesReturnsDefault) { |
| const auto kFakeAudioDevices = |
| CreateFakeDevices(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); |
| dispatcher_->SetTestAudioCaptureDevices(kFakeAudioDevices); |
| UpdateAudioDevicePreferenceRanking( |
| dispatcher_->GetAudioCaptureDevices().end() - 1); |
| UpdateAudioDevicePreferenceRanking( |
| dispatcher_->GetAudioCaptureDevices().begin()); |
| // Ranking at this point is [device_0, device_2, device_1]. |
| |
| // Pass empty eligible devices to disable filtering. |
| const auto preferred_device = |
| dispatcher_->GetPreferredAudioDeviceForBrowserContext(browser_context(), |
| {}); |
| // Preferred device should be the most preferred device in the ranking without |
| // any filtering. That device is device_0. |
| ASSERT_TRUE(preferred_device->IsSameDevice(kFakeAudioDevices.front())); |
| } |
| |
| TEST_F(MediaCaptureDevicesDispatcherTest, |
| GetPreferredVideoDeviceForBrowserContext) { |
| const auto kFakeVideoDevices = |
| CreateFakeDevices(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); |
| dispatcher_->SetTestVideoCaptureDevices(kFakeVideoDevices); |
| UpdateVideoDevicePreferenceRanking( |
| dispatcher_->GetVideoCaptureDevices().end() - 1); |
| UpdateVideoDevicePreferenceRanking( |
| dispatcher_->GetVideoCaptureDevices().begin()); |
| // Ranking at this point is [device_0, device_2, device_1]. |
| |
| // Exclude the first device from the eligible list to exercise filtering. |
| const auto eligible_device_ids = GetIds(kFakeVideoDevices, 1); |
| const auto preferred_device = |
| dispatcher_->GetPreferredVideoDeviceForBrowserContext( |
| browser_context(), eligible_device_ids); |
| ASSERT_TRUE(preferred_device->IsSameDevice(kFakeVideoDevices.back())); |
| } |
| |
| TEST_F( |
| MediaCaptureDevicesDispatcherTest, |
| GetPreferredVideoDeviceForBrowserContext_EmptyEligibleDevicesReturnsDefault) { |
| const auto kFakeVideoDevices = |
| CreateFakeDevices(blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); |
| dispatcher_->SetTestVideoCaptureDevices(kFakeVideoDevices); |
| UpdateVideoDevicePreferenceRanking( |
| dispatcher_->GetVideoCaptureDevices().end() - 1); |
| UpdateVideoDevicePreferenceRanking( |
| dispatcher_->GetVideoCaptureDevices().begin()); |
| // Ranking at this point is [device_0, device_2, device_1]. |
| |
| // Exclude the first device from the eligible list to exercise filtering. |
| const auto preferred_device = |
| dispatcher_->GetPreferredVideoDeviceForBrowserContext(browser_context(), |
| {}); |
| ASSERT_TRUE(preferred_device->IsSameDevice(kFakeVideoDevices.front())); |
| } |