[go: nahoru, domu]

blob: dfb82fa4a71c5d7241f8f63293fdc0d56097fe2e [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <type_traits>
#include "base/test/gtest_util.h"
#include "content/browser/fenced_frame/fenced_frame_config.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/fenced_frame/redacted_fenced_frame_config.h"
#include "third_party/blink/public/common/fenced_frame/redacted_fenced_frame_config_mojom_traits.h"
#include "third_party/blink/public/mojom/fenced_frame/fenced_frame_config.mojom.h"
#include "url/origin.h"
namespace content {
using RedactedFencedFrameConfig = blink::FencedFrame::RedactedFencedFrameConfig;
using RedactedFencedFrameProperties =
blink::FencedFrame::RedactedFencedFrameProperties;
using Entity = content::FencedFrameEntity;
// This macro creates the following test pattern:
// * Redact a config.
// * Check that the desired property was redacted as expected.
// * Serialize and deserialize the redacted config into a copy (using mojom type
// mappings implicitly).
// * Check that the desired property was copied correctly.
//
// Template Arguments:
// `ClassName`: `FencedFrameConfig` or `FencedFrameProperties`
// Which base class to use for this test.
// `RedactedClassName`: `RedactedFencedFrameConfig` or
// `RedactedFencedFrameProperties`
// The redacted version of `ClassName`.
// `TestType`: The type of the value being tested.
// `RedactedTestType`: The type of the redacted value being tested. Sometimes
// the type of variable being stored in the non-redacted and
// its redacted equivalent can differ.
// `UnredactedToRedactedCompare`: The shape of the function that will compare
// an unredacted config value to a redacted
// config value.
// `RedactedToRedactedCompare`: The shape of the function that will compare
// two redacted config values.
//
// Arguments:
// `config`: The FencedFrameConfig or FencedFrameProperties object being tested.
// `property`: A pointer to the class's field to test (e.g. `mapped_url_`)
// `entity`: The entity (kEmbedder or kContent) that the config should be
// redacted for in the test.
// `is_defined`: Whether we expect the property to have a defined value.
// `is_opaque`: Whether we expect the property's defined value to be opaque.
// `unredacted_redacted_equality_fn`: A comparator function that has the
// function signature is_eq(`type`, Redacted`type`). A return value of
// `true` means equal; `false` means not equal.
// `redacted_redacted_equality_fn`: A comparator function that has the function
// signature is_eq(Redacted`type`, Redacted`type`). A return value of `true`
// means equal; `false` means not equal.
template <typename ClassName,
typename RedactedClassName,
typename TestType,
typename RedactedTestType,
typename UnredactedToRedactedCompare,
typename RedactedToRedactedCompare>
void TestPropertyForEntityIsDefinedIsOpaque(
ClassName config,
absl::optional<FencedFrameProperty<TestType>> ClassName::*property,
absl::optional<
blink::FencedFrame::RedactedFencedFrameProperty<RedactedTestType>>
RedactedClassName::*redacted_property,
Entity entity,
bool is_defined,
bool is_opaque,
UnredactedToRedactedCompare unredacted_redacted_equality_fn,
RedactedToRedactedCompare redacted_redacted_equality_fn) {
// Redact the config.
RedactedClassName redacted_config = config.RedactFor(entity);
if (is_defined) {
// If the config has a value for the property, check that the redacted
// version does too.
ASSERT_TRUE((redacted_config.*redacted_property).has_value());
if (is_opaque) {
// If the value should be opaque, check that it is.
ASSERT_FALSE((redacted_config.*redacted_property)
->potentially_opaque_value.has_value());
} else {
// If the value should be transparent, check that it is, and that the
// value was copied correctly.
ASSERT_TRUE((redacted_config.*redacted_property)
->potentially_opaque_value.has_value());
ASSERT_TRUE(unredacted_redacted_equality_fn(
(config.*property)->GetValueIgnoringVisibility(),
(redacted_config.*redacted_property)
->potentially_opaque_value.value()));
}
} else {
// If the config doesn't have a value for the property, check that the
// redacted version also doesn't.
ASSERT_FALSE((redacted_config.*redacted_property).has_value());
}
// Copy the config using mojom serialization/deserialization.
RedactedClassName copy;
// deduce the mojo class being used
using MojoClassName =
std::conditional_t<std::is_same<FencedFrameConfig, ClassName>::value,
blink::mojom::FencedFrameConfig,
blink::mojom::FencedFrameProperties>;
mojo::test::SerializeAndDeserialize<MojoClassName>(redacted_config, copy);
// Check that the value for the property in the copy is the same as the
// original.
if (is_defined) {
ASSERT_TRUE((copy.*redacted_property).has_value());
if (is_opaque) {
ASSERT_FALSE(
(copy.*redacted_property)->potentially_opaque_value.has_value());
} else {
ASSERT_TRUE(
(copy.*redacted_property)->potentially_opaque_value.has_value());
ASSERT_TRUE(redacted_redacted_equality_fn(
(redacted_config.*redacted_property)
->potentially_opaque_value.value(),
(copy.*redacted_property)->potentially_opaque_value.value()));
}
} else {
ASSERT_FALSE((copy.*redacted_property).has_value());
}
}
// This helper function generates several test cases for a given property:
// * An empty config (`property` has no value)
// * A config with `dummy_value` for `property`, opaque to embedder and
// transparent to content.
// * A config with `dummy_value` for `property`, transparent to embedder and
// opaque to content.
//
// Template Arguments:
// `ClassName`: `FencedFrameConfig` or `FencedFrameProperties`
// Which base class to use for this test.
// `RedactedClassName`: `RedactedFencedFrameConfig` or
// `RedactedFencedFrameProperties`
// The redacted version of `ClassName`.
// `TestType`: The type of the value being tested.
// `RedactedTestType`: The type of the redacted value being tested. Sometimes
// the type of variable being stored in the non-redacted and
// its redacted equivalent can differ.
// `UnredactedToRedactedCompare`: The shape of the function that will compare
// an unredacted config value to a redacted
// config value.
// `RedactedToRedactedCompare`: The shape of the function that will compare
// two redacted config values.
//
// Arguments:
// `property`: A pointer to the name of the field to test (e.g. `mapped_url_`)
// `redacted_property`: The redacted equivalent of the `property` field.
// `dummy_value`: A value that can be emplaced into `property`.
// `unredacted_redacted_equality_fn`: A comparator function that has the
// function signature is_eq(`type`, Redacted`type`). A return value of
// `true` means equal; `false` means not equal.
// `redacted_redacted_equality_fn`: A comparator function that has the function
// signature is_eq(Redacted`type`, Redacted`type`). A return value of `true`
// means equal; `false` means not equal.
template <typename ClassName,
typename RedactedClassName,
typename TestType,
typename RedactedTestType,
typename UnredactedToRedactedCompare,
typename RedactedToRedactedCompare>
void TestProperty(
absl::optional<FencedFrameProperty<TestType>> ClassName::*property,
absl::optional<
blink::FencedFrame::RedactedFencedFrameProperty<RedactedTestType>>
RedactedClassName::*redacted_property,
TestType dummy_value,
UnredactedToRedactedCompare unredacted_redacted_equality_fn,
RedactedToRedactedCompare redacted_redacted_equality_fn) {
// Test an empty config
ClassName config;
if constexpr (std::is_same<FencedFrameConfig, ClassName>::value) {
config.urn_uuid_.emplace(GenerateUrnUuid());
}
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kEmbedder, false, false,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kContent, false, false,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
// Test when `property` is opaque to embedder and transparent to content.
(config.*property)
.emplace(dummy_value, VisibilityToEmbedder::kOpaque,
VisibilityToContent::kTransparent);
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kEmbedder, true, true,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kContent, true, false,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
// Test when `property` is transparent to embedder and opaque to content.
(config.*property)
.emplace(dummy_value, VisibilityToEmbedder::kTransparent,
VisibilityToContent::kOpaque);
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kEmbedder, true, false,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
TestPropertyForEntityIsDefinedIsOpaque<ClassName, RedactedClassName, TestType,
RedactedTestType>(
config, property, redacted_property, Entity::kContent, true, true,
unredacted_redacted_equality_fn, redacted_redacted_equality_fn);
}
// Compare equality of two lists of nested configs.
// Only compares the `mapped_url` field for convenience. (We don't need an
// equality operator for configs outside of tests, so it would be wasteful to
// declare it as default in the class declaration.)
#define NESTED_CONFIG_EQ_FN(type1, accessor1, type2, accessor2) \
[](const std::vector<type1>& a, const std::vector<type2>& b) { \
if (a.size() != b.size()) { \
return false; \
} \
for (size_t i = 0; i < a.size(); ++i) { \
if (!a[i].mapped_url_.has_value() && !b[i].mapped_url_.has_value()) { \
continue; \
} else if (a[i].mapped_url_.has_value() && \
b[i].mapped_url_.has_value()) { \
if (a[i].mapped_url_->accessor1 == b[i].mapped_url_->accessor2) { \
continue; \
} \
return false; \
} else { \
return false; \
} \
} \
return true; \
}
// Compare equality of two lists of (urn, nested config) pairs.
// Only compares the `mapped_url` field for convenience.
#define NESTED_URN_CONFIG_PAIR_EQ_FN(type1, accessor1, type2, accessor2) \
[](const std::vector<std::pair<GURL, type1>>& a, \
const std::vector<std::pair<GURL, type2>>& b) { \
if (a.size() != b.size()) { \
return false; \
} \
for (size_t i = 0; i < a.size(); ++i) { \
if (a[i].first != b[i].first) { \
return false; \
} \
if (!a[i].second.mapped_url_.has_value() && \
!b[i].second.mapped_url_.has_value()) { \
continue; \
} else if (a[i].second.mapped_url_.has_value() && \
b[i].second.mapped_url_.has_value()) { \
if (a[i].second.mapped_url_->accessor1 == \
b[i].second.mapped_url_->accessor2) { \
continue; \
} \
return false; \
} else { \
return false; \
} \
} \
return true; \
}
TEST(FencedFrameConfigMojomTraitsTest, ConfigMojomTraitsInternalUrnTest) {
GURL test_url("test_url");
struct TestCase {
GURL urn;
bool pass = false;
} test_cases[] = {
{GURL(), false},
{GURL("https://example.com"), false},
{GURL("data:text/html<h1>MyWebsite"), false},
{GURL("urn:abcd:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"), false},
{GURL("urn:uuid:foo"), false},
{GURL("urn:uuid:f81d4faea7deca11d0aa765a00a0c91e6bf6"), false},
{GURL("urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"), true},
{GenerateUrnUuid(), true},
};
for (const TestCase& test_case : test_cases) {
FencedFrameConfig browser_config(test_case.urn, test_url);
RedactedFencedFrameConfig input_config =
browser_config.RedactFor(FencedFrameEntity::kEmbedder);
RedactedFencedFrameConfig output_config;
if (test_case.pass) {
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameConfig>(
input_config, output_config));
} else {
ASSERT_FALSE(
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameConfig>(
input_config, output_config));
}
}
}
TEST(FencedFrameConfigMojomTraitsTest, ConfigMojomTraitsModeTest) {
std::vector<blink::FencedFrame::DeprecatedFencedFrameMode> modes = {
blink::FencedFrame::DeprecatedFencedFrameMode::kDefault,
blink::FencedFrame::DeprecatedFencedFrameMode::kOpaqueAds,
};
std::vector<FencedFrameEntity> entities = {
FencedFrameEntity::kEmbedder,
FencedFrameEntity::kContent,
};
GURL test_url("test_url");
GURL test_urn = GenerateUrnUuid();
for (blink::FencedFrame::DeprecatedFencedFrameMode& mode : modes) {
FencedFrameConfig browser_config(test_urn, test_url);
browser_config.mode_ = mode;
FencedFrameProperties browser_properties(browser_config);
for (FencedFrameEntity& entity : entities) {
RedactedFencedFrameConfig input_config = browser_config.RedactFor(entity);
ASSERT_TRUE(browser_config.mode_ == input_config.mode());
RedactedFencedFrameConfig output_config;
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameConfig>(
input_config, output_config);
ASSERT_TRUE(input_config.mode() == output_config.mode());
RedactedFencedFrameProperties input_properties =
browser_properties.RedactFor(entity);
ASSERT_TRUE(browser_properties.mode_ == input_properties.mode());
RedactedFencedFrameProperties output_properties;
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameProperties>(
input_properties, output_properties);
ASSERT_TRUE(input_properties.mode() == output_properties.mode());
}
}
}
TEST(FencedFrameConfigMojomTraitsTest, ConfigMojomTraitsNullInternalUrnTest) {
FencedFrameConfig browser_config;
RedactedFencedFrameConfig input_config =
browser_config.RedactFor(FencedFrameEntity::kEmbedder);
RedactedFencedFrameConfig output_config;
EXPECT_DEATH_IF_SUPPORTED(
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameConfig>(
input_config, output_config),
"");
}
TEST(FencedFrameConfigMojomTraitsTest, ConfigMojomTraitsTest) {
GURL test_url("test_url");
// See the above tests for `urn`.
// Test `mapped_url`.
{
auto eq_fn = [](const GURL& a, const GURL& b) { return a == b; };
TestProperty(&FencedFrameConfig::mapped_url_,
&RedactedFencedFrameConfig::mapped_url_, test_url, eq_fn,
eq_fn);
TestProperty(&FencedFrameProperties::mapped_url_,
&RedactedFencedFrameProperties::mapped_url_, test_url, eq_fn,
eq_fn);
}
// Test `container_size` and `content_size`.
{
gfx::Size test_size(100, 200);
auto eq_fn = [](const gfx::Size& a, const gfx::Size& b) { return a == b; };
TestProperty(&FencedFrameConfig::container_size_,
&RedactedFencedFrameConfig::container_size_, test_size, eq_fn,
eq_fn);
TestProperty(&FencedFrameProperties::container_size_,
&RedactedFencedFrameProperties::container_size_, test_size,
eq_fn, eq_fn);
TestProperty(&FencedFrameConfig::content_size_,
&RedactedFencedFrameConfig::content_size_, test_size, eq_fn,
eq_fn);
TestProperty(&FencedFrameProperties::content_size_,
&RedactedFencedFrameProperties::content_size_, test_size,
eq_fn, eq_fn);
}
// Test `deprecated_should_freeze_initial_size`.
{
auto eq_fn = [](const bool a, const bool b) { return a == b; };
TestProperty(
&FencedFrameConfig::deprecated_should_freeze_initial_size_,
&RedactedFencedFrameConfig::deprecated_should_freeze_initial_size_,
true, eq_fn, eq_fn);
TestProperty(
&FencedFrameProperties::deprecated_should_freeze_initial_size_,
&RedactedFencedFrameProperties::deprecated_should_freeze_initial_size_,
true, eq_fn, eq_fn);
TestProperty(
&FencedFrameConfig::deprecated_should_freeze_initial_size_,
&RedactedFencedFrameConfig::deprecated_should_freeze_initial_size_,
false, eq_fn, eq_fn);
TestProperty(
&FencedFrameProperties::deprecated_should_freeze_initial_size_,
&RedactedFencedFrameProperties::deprecated_should_freeze_initial_size_,
false, eq_fn, eq_fn);
}
// Test `ad_auction_data`.
{
AdAuctionData test_ad_auction_data = {url::Origin::Create(test_url),
std::string("test_name")};
auto eq_fn = [](const AdAuctionData& a, const AdAuctionData& b) {
return a.interest_group_owner == b.interest_group_owner &&
a.interest_group_name == b.interest_group_name;
};
TestProperty(&FencedFrameConfig::ad_auction_data_,
&RedactedFencedFrameConfig::ad_auction_data_,
test_ad_auction_data, eq_fn, eq_fn);
TestProperty(&FencedFrameProperties::ad_auction_data_,
&RedactedFencedFrameProperties::ad_auction_data_,
test_ad_auction_data, eq_fn, eq_fn);
}
// Test `nested_configs`.
{
FencedFrameConfig test_nested_config(GenerateUrnUuid(), test_url);
{
std::vector<FencedFrameConfig> test_nested_configs = {test_nested_config};
auto unredacted_redacted_eq_fn = NESTED_CONFIG_EQ_FN(
FencedFrameConfig, GetValueForEntity(Entity::kEmbedder),
RedactedFencedFrameConfig, potentially_opaque_value);
auto redacted_redacted_eq_fn = NESTED_CONFIG_EQ_FN(
RedactedFencedFrameConfig, potentially_opaque_value,
RedactedFencedFrameConfig, potentially_opaque_value);
TestProperty(&FencedFrameConfig::nested_configs_,
&RedactedFencedFrameConfig::nested_configs_,
test_nested_configs, unredacted_redacted_eq_fn,
redacted_redacted_eq_fn);
}
{
GURL test_urn("urn:uuid:abcd");
std::vector<std::pair<GURL, FencedFrameConfig>>
test_nested_urn_config_pairs = {{test_urn, test_nested_config}};
auto unredacted_redacted_eq_fn = NESTED_URN_CONFIG_PAIR_EQ_FN(
FencedFrameConfig, GetValueForEntity(Entity::kEmbedder),
RedactedFencedFrameConfig, potentially_opaque_value);
auto redacted_redacted_eq_fn = NESTED_URN_CONFIG_PAIR_EQ_FN(
RedactedFencedFrameConfig, potentially_opaque_value,
RedactedFencedFrameConfig, potentially_opaque_value);
TestProperty(&FencedFrameProperties::nested_urn_config_pairs_,
&RedactedFencedFrameProperties::nested_urn_config_pairs_,
test_nested_urn_config_pairs, unredacted_redacted_eq_fn,
redacted_redacted_eq_fn);
}
}
// Test `shared_storage_budget_metadata`.
{
SharedStorageBudgetMetadata test_shared_storage_budget_metadata = {
url::Origin::Create(test_url), 0.5, /*top_navigated=*/true};
auto eq_fn = [](const SharedStorageBudgetMetadata& a,
const SharedStorageBudgetMetadata& b) {
return a.origin == b.origin && a.budget_to_charge == b.budget_to_charge &&
a.top_navigated == b.top_navigated;
};
TestProperty(&FencedFrameConfig::shared_storage_budget_metadata_,
&RedactedFencedFrameConfig::shared_storage_budget_metadata_,
test_shared_storage_budget_metadata, eq_fn, eq_fn);
auto pointer_value_eq_fn =
[](const raw_ptr<const SharedStorageBudgetMetadata>& a,
const SharedStorageBudgetMetadata& b) {
return a->origin == b.origin &&
a->budget_to_charge == b.budget_to_charge &&
a->top_navigated == b.top_navigated;
};
TestProperty(
&FencedFrameProperties::shared_storage_budget_metadata_,
&RedactedFencedFrameProperties::shared_storage_budget_metadata_,
static_cast<base::raw_ptr<const SharedStorageBudgetMetadata>>(
&test_shared_storage_budget_metadata),
pointer_value_eq_fn, eq_fn);
}
}
// Test `has_fenced_frame_reporting`, which only appears in
// FencedFrameProperties, and does not use the redacted mechanism used by other
// fields.
TEST(FencedFrameConfigMojomTraitsTest, PropertiesHasFencedFrameReportingTest) {
FencedFrameProperties properties;
RedactedFencedFrameProperties input_properties =
properties.RedactFor(FencedFrameEntity::kEmbedder);
EXPECT_FALSE(input_properties.has_fenced_frame_reporting());
RedactedFencedFrameProperties output_properties;
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameProperties>(
input_properties, output_properties);
EXPECT_FALSE(output_properties.has_fenced_frame_reporting());
// Create a reporting service with a dummy SharedURLLoaderFactory.
properties.fenced_frame_reporter_ = FencedFrameReporter::CreateForFledge(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(nullptr),
/*attribution_manager=*/nullptr,
/*direct_seller_is_seller=*/false,
/*private_aggregation_manager=*/nullptr,
/*main_frame_origin=*/url::Origin(),
/*winner_origin=*/url::Origin());
input_properties = properties.RedactFor(FencedFrameEntity::kEmbedder);
EXPECT_TRUE(input_properties.has_fenced_frame_reporting());
mojo::test::SerializeAndDeserialize<blink::mojom::FencedFrameProperties>(
input_properties, output_properties);
EXPECT_TRUE(output_properties.has_fenced_frame_reporting());
}
} // namespace content