blob: b7b974a522dbacca5f3df701660fced527693ae8 [file] [log] [blame]
// Copyright 2017 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/media/key_system_support_impl.h"
#include <string>
#include "base/containers/contains.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_permission_controller.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "media/base/cdm_capability.h"
#include "media/base/key_system_capability.h"
#include "media/base/video_codecs.h"
#include "mojo/public/cpp/bindings/equals_traits.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/system/functions.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
namespace content {
using AudioCodec = media::AudioCodec;
using VideoCodec = media::VideoCodec;
using EncryptionScheme = media::EncryptionScheme;
using CdmSessionType = media::CdmSessionType;
using Robustness = CdmInfo::Robustness;
using base::test::RunOnceCallback;
using media::CdmCapability;
using media::KeySystemCapability;
using testing::_;
using testing::SaveArg;
const char kTestKeySystem[] = "com.example.somesystem";
ACTION_TEMPLATE(PostOnceCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p0)) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(std::get<k>(args)), p0));
}
namespace {
class KeySystemSupportObserverImpl
: public media::mojom::KeySystemSupportObserver {
public:
explicit KeySystemSupportObserverImpl(KeySystemCapabilitiesUpdateCB cb)
: key_system_support_cb_(std::move(cb)) {}
KeySystemSupportObserverImpl(const KeySystemSupportObserverImpl&) = delete;
KeySystemSupportObserverImpl& operator=(const KeySystemSupportObserverImpl&) =
delete;
~KeySystemSupportObserverImpl() override = default;
// media::mojom::KeySystemSupportObserver
void OnKeySystemSupportUpdated(
const KeySystemCapabilities& capabilities) final {
key_system_support_cb_.Run(std::move(capabilities));
}
private:
KeySystemCapabilitiesUpdateCB key_system_support_cb_;
};
CdmCapability TestCdmCapability() {
return CdmCapability(
{AudioCodec::kVorbis}, {{VideoCodec::kVP8, {}}, {VideoCodec::kVP9, {}}},
{EncryptionScheme::kCenc, EncryptionScheme::kCbcs},
{CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense},
base::Version("1.2.3.4"));
}
KeySystemCapabilities TestKeySystemCapabilities(
std::optional<CdmCapability> sw_secure_capability,
std::optional<CdmCapability> hw_secure_capability) {
KeySystemCapabilities key_system_capabilities;
key_system_capabilities[kTestKeySystem] = KeySystemCapability(
sw_secure_capability.has_value()
? media::CdmCapabilityOrStatus(
std::move(sw_secure_capability).value())
: base::unexpected(media::CdmCapabilityQueryStatus::kUnknown),
hw_secure_capability.has_value()
? media::CdmCapabilityOrStatus(
std::move(hw_secure_capability).value())
: base::unexpected(media::CdmCapabilityQueryStatus::kUnknown));
return key_system_capabilities;
}
} // namespace
class KeySystemSupportImplTest : public RenderViewHostTestHarness {
public:
void SetUp() override {
LOG(ERROR) << __func__;
RenderViewHostTestHarness::SetUp();
test_browser_context_ = std::make_unique<content::TestBrowserContext>();
test_browser_context_->SetPermissionControllerForTesting(
std::make_unique<testing::NiceMock<MockPermissionController>>());
KeySystemSupportImpl::GetOrCreateForCurrentDocument(main_rfh())
->SetGetKeySystemCapabilitiesUpdateCbForTesting(get_support_cb_.Get());
KeySystemSupportImpl::GetOrCreateForCurrentDocument(main_rfh())
->Bind(key_system_support_.BindNewPipeAndPassReceiver());
}
void TearDown() override {
test_browser_context_.reset();
RenderViewHostTestHarness::TearDown();
}
void OnKeySystemSupportUpdated(base::OnceClosure done_cb,
KeySystemCapabilities capabilities) {
results_.push_back(std::move(capabilities));
std::move(done_cb).Run();
}
void SetPermissionStatus(blink::mojom::PermissionStatus permission_status) {
auto* mock_permission_controller = static_cast<MockPermissionController*>(
test_browser_context_->GetPermissionController());
ON_CALL(*mock_permission_controller,
RequestPermissionFromCurrentDocument(
main_rfh(),
PermissionRequestDescription(
PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER),
main_rfh()->HasTransientUserActivation()),
_))
.WillByDefault(RunOnceCallback<2>(PermissionResult(
permission_status, PermissionStatusSource::UNSPECIFIED)));
KeySystemSupportImpl::GetForCurrentDocument(main_rfh())
->OnProtectedMediaIdentifierPermissionUpdated(permission_status);
}
protected:
void GetKeySystemSupport() {
DVLOG(1) << __func__;
base::RunLoop run_loop;
mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
&KeySystemSupportImplTest::OnKeySystemSupportUpdated,
base::Unretained(this), run_loop.QuitClosure())),
observer_remote.InitWithNewPipeAndPassReceiver());
key_system_support_->SetObserver(std::move(observer_remote));
run_loop.Run();
}
mojo::Remote<media::mojom::KeySystemSupport> key_system_support_;
base::MockCallback<KeySystemSupportImpl::GetKeySystemCapabilitiesUpdateCB>
get_support_cb_;
// KeySystemSupport update results. It's a list of updates received by an
// observer.
std::vector<KeySystemCapabilities> results_;
std::unique_ptr<content::TestBrowserContext> test_browser_context_;
};
TEST_F(KeySystemSupportImplTest, NoKeySystems) {
EXPECT_CALL(get_support_cb_, Run(_, _))
.WillOnce(RunOnceCallback<1>(KeySystemCapabilities()));
GetKeySystemSupport();
EXPECT_EQ(results_.size(), 1u); // One update
const auto& capabilities = results_[0];
EXPECT_TRUE(capabilities.empty()); // No capabilities
}
TEST_F(KeySystemSupportImplTest, OneUpdate) {
EXPECT_CALL(get_support_cb_, Run(_, _))
.WillOnce(RunOnceCallback<1>(
TestKeySystemCapabilities(TestCdmCapability(), std::nullopt)));
GetKeySystemSupport();
EXPECT_EQ(results_.size(), 1u); // One update
auto& capabilities = results_[0];
ASSERT_TRUE(capabilities.count(kTestKeySystem));
const auto& capability = capabilities[kTestKeySystem];
EXPECT_TRUE(capability.sw_cdm_capability_or_status.has_value());
EXPECT_FALSE(capability.hw_cdm_capability_or_status.has_value());
}
TEST_F(KeySystemSupportImplTest, TwoUpdates) {
KeySystemCapabilitiesUpdateCB callback;
EXPECT_CALL(get_support_cb_, Run(_, _)).WillOnce(SaveArg<1>(&callback));
base::RunLoop run_loop_1;
mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
&KeySystemSupportImplTest::OnKeySystemSupportUpdated,
base::Unretained(this), base::DoNothing())),
observer_remote.InitWithNewPipeAndPassReceiver());
key_system_support_->SetObserver(std::move(observer_remote));
run_loop_1.RunUntilIdle();
// Update twice, one with hardware capability, one without.
base::RunLoop run_loop_2;
callback.Run(TestKeySystemCapabilities(TestCdmCapability(), std::nullopt));
callback.Run(
TestKeySystemCapabilities(TestCdmCapability(), TestCdmCapability()));
run_loop_2.RunUntilIdle();
EXPECT_EQ(results_.size(), 2u); // Two updates
auto& capabilities_1 = results_[0];
ASSERT_TRUE(capabilities_1.count(kTestKeySystem));
const auto& capability_1 = capabilities_1[kTestKeySystem];
EXPECT_TRUE(capability_1.sw_cdm_capability_or_status.has_value());
EXPECT_FALSE(capability_1.hw_cdm_capability_or_status.has_value());
auto& capabilities_2 = results_[1];
ASSERT_TRUE(capabilities_2.count(kTestKeySystem));
const auto& capability_2 = capabilities_2[kTestKeySystem];
EXPECT_TRUE(capability_2.sw_cdm_capability_or_status.has_value());
EXPECT_TRUE(capability_2.hw_cdm_capability_or_status.has_value());
}
TEST_F(KeySystemSupportImplTest, TwoObservers_ObserverAlreadyBound) {
EXPECT_CALL(get_support_cb_, Run(_, _))
.WillOnce(RunOnceCallback<1>(
TestKeySystemCapabilities(TestCdmCapability(), std::nullopt)));
base::RunLoop run_loop;
mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_1_remote;
mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_2_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
&KeySystemSupportImplTest::OnKeySystemSupportUpdated,
base::Unretained(this), base::DoNothing())),
observer_1_remote.InitWithNewPipeAndPassReceiver());
mojo::MakeSelfOwnedReceiver(
std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
&KeySystemSupportImplTest::OnKeySystemSupportUpdated,
base::Unretained(this), run_loop.QuitClosure())),
observer_2_remote.InitWithNewPipeAndPassReceiver());
key_system_support_->SetObserver(std::move(observer_1_remote));
key_system_support_->SetObserver(std::move(observer_2_remote));
mojo::SetDefaultProcessErrorHandler(
base::BindLambdaForTesting([&](const std::string& error) {
EXPECT_EQ(error,
std::string(media::mojom::KeySystemSupportObserver::Name_) +
"::SetObserver observer already bound");
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(KeySystemSupportImplTest, TwoReceivers_SecondReceiverFails) {
// First receiver is bound in SetUp.
// Binding a second receiver should fail gracefully.
base::RunLoop run_loop;
mojo::Remote<media::mojom::KeySystemSupport> key_system_support;
KeySystemSupportImpl::GetOrCreateForCurrentDocument(main_rfh())
->Bind(key_system_support.BindNewPipeAndPassReceiver());
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_FALSE(key_system_support.is_connected());
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(KeySystemSupportImplTest, AllowHWSecureCapability) {
base::RunLoop run_loop_1;
mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
&KeySystemSupportImplTest::OnKeySystemSupportUpdated,
base::Unretained(this), base::DoNothing())),
observer_remote.InitWithNewPipeAndPassReceiver());
key_system_support_->SetObserver(std::move(observer_remote));
// Only windows can disallow hw secure capability.
#if BUILDFLAG(IS_WIN)
EXPECT_CALL(get_support_cb_, Run(testing::IsFalse(), _))
.WillOnce(RunOnceCallback<1>(KeySystemCapabilities()));
#else
EXPECT_CALL(get_support_cb_, Run(testing::IsTrue(), _))
.WillOnce(RunOnceCallback<1>(KeySystemCapabilities()));
#endif
SetPermissionStatus(blink::mojom::PermissionStatus::DENIED);
run_loop_1.RunUntilIdle();
// Switching the permission to GRANTED should change hw secure capability.
base::RunLoop run_loop_2;
EXPECT_CALL(get_support_cb_, Run(testing::IsTrue(), _))
.WillOnce(RunOnceCallback<1>(KeySystemCapabilities()));
SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED);
run_loop_2.RunUntilIdle();
}
} // namespace content