blob: 14f8a1332c01cbfc94c98910c4061d79935e41ca [file] [log] [blame]
// 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()));
}