blob: 564b6d2c2e785f30793ba4bfd66de19d12d0abf1 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <string>
#include "base/ranges/algorithm.h"
#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "url/gurl.h"
#include "url/origin.h"
// Origin strings used for tests
#define ORIGIN_A "https://example.com/"
#define ORIGIN_A_SUBDOMAIN_WILDCARD "https://*.example.com/"
#define ORIGIN_A_SUBDOMAIN_ESCAPED "https://%2A.example.com/"
#define ORIGIN_B "https://example.net/"
#define ORIGIN_C "https://example.org/"
#define OPAQUE_ORIGIN ""
// identifier used for feature/permissions policy parsing test.
// when there is a testcase for one syntax but not the other.
#define NOT_APPLICABLE nullptr
class GURL;
namespace blink {
namespace {
const char* const kValidHeaderPolicies[] = {
"", // An empty policy.
" ", // An empty policy.
";;", // Empty policies.
",,", // Empty policies.
" ; ;", // Empty policies.
" , ,", // Empty policies.
",;,", // Empty policies.
"geolocation 'none'",
"geolocation 'self'",
"geolocation",
"geolocation; fullscreen; payment",
"geolocation *",
"geolocation " ORIGIN_A "",
"geolocation " ORIGIN_B "",
"geolocation " ORIGIN_A " " ORIGIN_B "",
"geolocation 'none' " ORIGIN_A " " ORIGIN_B "",
"geolocation " ORIGIN_A " 'none' " ORIGIN_B "",
"geolocation 'none' 'none' 'none'",
"geolocation " ORIGIN_A " *",
"fullscreen " ORIGIN_A "; payment 'self'",
"fullscreen " ORIGIN_A "; payment *, geolocation 'self'"};
const char* const kInvalidHeaderPolicies[] = {
"badfeaturename",
"badfeaturename 'self'",
"1.0",
"geolocation 'src'", // Only valid for iframe allow attribute.
"geolocation data:///badorigin",
"geolocation https://bad;origin",
"geolocation https:/bad,origin",
"geolocation https://example.com, https://a.com",
"geolocation *, payment data:///badorigin",
"geolocation ws://xn--fd\xbcwsw3taaaaaBaa333aBBBBBBJBBJBBBt",
};
} // namespace
class PermissionsPolicyParserTest : public ::testing::Test {
protected:
PermissionsPolicyParserTest() = default;
~PermissionsPolicyParserTest() override = default;
scoped_refptr<const SecurityOrigin> origin_a_ =
SecurityOrigin::CreateFromString(ORIGIN_A);
scoped_refptr<const SecurityOrigin> origin_b_ =
SecurityOrigin::CreateFromString(ORIGIN_B);
scoped_refptr<const SecurityOrigin> origin_c_ =
SecurityOrigin::CreateFromString(ORIGIN_C);
url::Origin expected_url_origin_a_ = url::Origin::Create(GURL(ORIGIN_A));
url::Origin expected_url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
url::Origin expected_url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
const FeatureNameMap test_feature_name_map = {
{"fullscreen",
blink::mojom::blink::PermissionsPolicyFeature::kFullscreen},
{"payment", blink::mojom::blink::PermissionsPolicyFeature::kPayment},
{"geolocation",
blink::mojom::blink::PermissionsPolicyFeature::kGeolocation}};
ParsedPermissionsPolicy ParseFeaturePolicyHeader(
const String& feature_policy_header,
scoped_refptr<const SecurityOrigin> origin,
PolicyParserMessageBuffer& logger,
ExecutionContext* context = nullptr) {
return PermissionsPolicyParser::ParseHeader(
feature_policy_header, g_empty_string, origin, logger, logger, context);
}
test::TaskEnvironment task_environment_;
};
struct OriginWithPossibleWildcardsForTest {
const char* origin;
bool has_subdomain_wildcard;
};
struct ParsedPolicyDeclarationForTest {
mojom::blink::PermissionsPolicyFeature feature;
std::optional<const char*> self_if_matches;
bool matches_all_origins;
bool matches_opaque_src;
std::vector<OriginWithPossibleWildcardsForTest> allowed_origins;
std::optional<std::string> reporting_endpoint;
};
using ParsedPolicyForTest = std::vector<ParsedPolicyDeclarationForTest>;
struct PermissionsPolicyParserTestCase {
const char* test_name;
// Test inputs.
const char* feature_policy_string;
const char* permissions_policy_string;
const char* self_origin;
const char* src_origin;
// Test expectation.
ParsedPolicyForTest expected_parse_result;
};
class PermissionsPolicyParserParsingTest
: public PermissionsPolicyParserTest,
public ::testing::WithParamInterface<PermissionsPolicyParserTestCase> {
private:
scoped_refptr<const SecurityOrigin> GetSrcOrigin(const char* origin_str) {
scoped_refptr<const SecurityOrigin> src_origin;
if (String(origin_str) == OPAQUE_ORIGIN) {
src_origin = SecurityOrigin::CreateUniqueOpaque();
} else {
src_origin =
origin_str ? SecurityOrigin::CreateFromString(origin_str) : nullptr;
}
return src_origin;
}
protected:
ParsedPermissionsPolicy ParseFeaturePolicy(
const char* policy_string,
const char* self_origin_string,
const char* src_origin_string,
PolicyParserMessageBuffer& logger,
const FeatureNameMap& feature_names,
ExecutionContext* context = nullptr) {
return PermissionsPolicyParser::ParseFeaturePolicyForTest(
policy_string, SecurityOrigin::CreateFromString(self_origin_string),
GetSrcOrigin(src_origin_string), logger, feature_names, context);
}
ParsedPermissionsPolicy ParsePermissionsPolicy(
const char* policy_string,
const char* self_origin_string,
const char* src_origin_string,
PolicyParserMessageBuffer& logger,
const FeatureNameMap& feature_names,
ExecutionContext* context = nullptr) {
return PermissionsPolicyParser::ParsePermissionsPolicyForTest(
policy_string, SecurityOrigin::CreateFromString(self_origin_string),
GetSrcOrigin(src_origin_string), logger, feature_names, context);
}
void CheckParsedPolicy(const ParsedPermissionsPolicy& actual,
const ParsedPolicyForTest& expected) {
ASSERT_EQ(actual.size(), expected.size());
for (size_t i = 0; i < actual.size(); ++i) {
const auto& actual_declaration = actual[i];
const auto& expected_declaration = expected[i];
EXPECT_EQ(actual_declaration.feature, expected_declaration.feature);
if (expected_declaration.self_if_matches) {
EXPECT_TRUE(actual_declaration.self_if_matches->IsSameOriginWith(
url::Origin::Create(GURL(*expected_declaration.self_if_matches))));
} else {
EXPECT_FALSE(actual_declaration.self_if_matches);
}
EXPECT_EQ(actual_declaration.matches_all_origins,
expected_declaration.matches_all_origins);
EXPECT_EQ(actual_declaration.matches_opaque_src,
expected_declaration.matches_opaque_src);
EXPECT_EQ(actual_declaration.reporting_endpoint,
expected_declaration.reporting_endpoint);
ASSERT_EQ(actual_declaration.allowed_origins.size(),
expected_declaration.allowed_origins.size());
for (size_t j = 0; j < actual_declaration.allowed_origins.size(); ++j) {
const url::Origin origin = url::Origin::Create(
GURL(expected_declaration.allowed_origins[j].origin));
EXPECT_EQ(
actual_declaration.allowed_origins[j].CSPSourceForTest().scheme,
origin.scheme());
EXPECT_EQ(actual_declaration.allowed_origins[j].CSPSourceForTest().host,
origin.host());
if (actual_declaration.allowed_origins[j].CSPSourceForTest().port !=
url::PORT_UNSPECIFIED) {
EXPECT_EQ(
actual_declaration.allowed_origins[j].CSPSourceForTest().port,
origin.port());
}
EXPECT_EQ(
actual_declaration.allowed_origins[j]
.CSPSourceForTest()
.is_host_wildcard,
expected_declaration.allowed_origins[j].has_subdomain_wildcard);
}
}
}
void CheckConsoleMessage(
const Vector<PolicyParserMessageBuffer::Message>& actual,
const std::vector<String> expected) {
ASSERT_EQ(actual.size(), expected.size());
for (wtf_size_t i = 0; i < actual.size(); ++i) {
EXPECT_EQ(actual[i].content, expected[i]);
}
}
public:
static const PermissionsPolicyParserTestCase kCases[];
};
const PermissionsPolicyParserTestCase
PermissionsPolicyParserParsingTest::kCases[] = {
{
/* test_name */ "EmptyPolicy",
/* feature_policy_string */ "",
/* permissions_policy_string */ "",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */ {},
},
{
/* test_name */ "SimplePolicyWithSelf",
/* feature_policy_string */ "geolocation 'self'",
/* permissions_policy_string */ "geolocation=self",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "SimplePolicyWithSelfExplicitListSyntax",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */ "geolocation=(self)",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "SimplePolicyWithStar",
/* feature_policy_string */ "geolocation *",
/* permissions_policy_string */ "geolocation=*",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
},
},
{
/* test_name */ "ComplicatedPolicy",
/* feature_policy_string */
"geolocation *; "
"fullscreen " ORIGIN_B " " ORIGIN_C "; "
"payment 'self'",
/* permissions_policy_string */
"geolocation=*, "
"fullscreen=(\"" ORIGIN_B "\" \"" ORIGIN_C "\"),"
"payment=self",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false},
{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
},
{
mojom::blink::PermissionsPolicyFeature::kPayment,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "MultiplePoliciesIncludingBadFeatureName",
/* feature_policy_string */
"geolocation * " ORIGIN_B "; "
"fullscreen " ORIGIN_B " bad_feature_name " ORIGIN_C ";"
"payment 'self' badorigin",
/* permissions_policy_string */
"geolocation=(* \"" ORIGIN_B "\"),"
"fullscreen=(\"" ORIGIN_B "\" bad_feature_name \"" ORIGIN_C "\"),"
"payment=(self \"badorigin\")",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false},
{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
},
{
mojom::blink::PermissionsPolicyFeature::kPayment,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "HeaderPoliciesWithNoOptionalOriginLists",
/* feature_policy_string */ "geolocation;fullscreen;payment",
// Note: In structured header, if no value is associated with a key
// in dictionary, default value would be boolean true, which is
// not allowed as allowlist value in permission policy syntax.
/* permissions_policy_string */
"geolocation=self,fullscreen=self,payment=self",
/* self_origin */ ORIGIN_A,
/* src_origin */ nullptr,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kPayment,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "EmptyPolicyOpaqueSrcOrigin",
/* feature_policy_string */ "",
/* permissions_policy_string */ "",
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */ {},
},
{
/* test_name */ "SimplePolicyOpaqueSrcOrigin",
/* feature_policy_string */ "geolocation",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ true,
{},
},
},
},
{
/* test_name */ "SimplePolicyWithSrcOpaqueSrcOrigin",
/* feature_policy_string */ "geolocation 'src'",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ true,
{},
},
},
},
{
/* test_name */ "SimplePolicyWithStarOpaqueSrcOrigin",
/* feature_policy_string */ "geolocation *",
/* permissions_policy_string */ "geolocation=*",
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
},
},
{
/* test_name */ "PolicyWithExplicitOriginsOpaqueSrcOrigin",
/* feature_policy_string */ "geolocation " ORIGIN_B " " ORIGIN_C,
/* permissions_policy_string */
"geolocation=(\"" ORIGIN_B "\" \"" ORIGIN_C "\")",
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false},
{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
},
},
},
{
/* test_name */ "PolicyWithMultipleOriginsIncludingSrc"
"OpaqueSrcOrigin",
/* feature_policy_string */ "geolocation " ORIGIN_B " 'src'",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ OPAQUE_ORIGIN,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ true,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
},
},
{
/* test_name */ "PolicyWithInvalidDataTypeInt",
/* feature_policy_string */ NOT_APPLICABLE,
// int value should be rejected as allowlist items.
/* permissions_policy_string */ "geolocation=9",
/* self_origin */ ORIGIN_A,
/* src_origin */ nullptr,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "PolicyWithInvalidDataTypeFloat",
/* feature_policy_string */ NOT_APPLICABLE,
// decimal value should be rejected as allowlist items.
/* permissions_policy_string */ "geolocation=1.1",
/* self_origin */ ORIGIN_A,
/* src_origin */ nullptr,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "PolicyWithInvalidDataTypeBoolean",
/* feature_policy_string */ NOT_APPLICABLE,
// boolean value should be rejected as allowlist items.
/* permissions_policy_string */ "geolocation=?0",
/* self_origin */ ORIGIN_A,
/* src_origin */ nullptr,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "PolicyWithEmptyOriginString",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */ "geolocation=\"\"",
/* self_origin */ ORIGIN_A,
/* src_origin */ nullptr,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "ProperWildcardIncludedForFeaturePolicy",
/* feature_policy_string */
"fullscreen " ORIGIN_A_SUBDOMAIN_WILDCARD,
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "ProperWildcardIncludedForPermissionsPolicy",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=(\"" ORIGIN_A_SUBDOMAIN_WILDCARD "\")",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_A,
/*has_subdomain_wildcard=*/true}},
},
},
},
{
/* test_name */ "ImproperWildcardsIncluded",
/* feature_policy_string */
"fullscreen *://example.com https://foo.*.example.com "
"https://*.*.example.com",
/* permissions_policy_string */
"fullscreen=(\"*://example.com\" \"https://foo.*.example.com\" "
"\"https://*.*.example.com\")",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
},
},
{
/* test_name */ "AttributeWithLineBreaks",
/* feature_policy_string */
"geolocation;\n"
"fullscreen",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
},
},
{
/* test_name */ "AttributeWithCRLF",
/* feature_policy_string */
"geolocation;\r\n"
"fullscreen",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
},
},
{
/* test_name */ "AlternativeWhitespceBetweenTokens",
/* feature_policy_string */
"\r\n\r\ngeolocation\t 'self'\f\f" ORIGIN_B "\t",
/* permissions_policy_string */ NOT_APPLICABLE,
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
},
},
},
{
/* test_name */ "ReportingEndpointWithStar",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=*;report-to=endpoint",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
"endpoint",
},
},
},
{
/* test_name */ "ReportingEndpointWithList",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=(\"" ORIGIN_B "\" \"" ORIGIN_C "\");report-to=endpoint",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false},
{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
"endpoint",
},
},
},
{
/* test_name */ "ReportingEndpointWithNone",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=();report-to=endpoint",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
"endpoint",
},
},
},
{
/* test_name */ "ReportingEndpointWithSelf",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=self;report-to=endpoint",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
"endpoint",
},
},
},
{
/* test_name */ "ReportingEndpointWithSingleOrigin",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=\"" ORIGIN_C "\";report-to=endpoint",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
"endpoint",
},
},
},
{
/* test_name */ "InvalidReportingEndpointInList",
/* feature_policy_string */ NOT_APPLICABLE,
// Note: The reporting endpoint parameter needs to apply to the
// entire value for the dictionary entry. In this example, it is
// placed on a single inner list item, and should therefore be
// ignored.
/* permissions_policy_string */
"fullscreen=(\"" ORIGIN_C "\";report-to=endpoint)",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
/* reporting_endpoint */ std::nullopt,
},
},
},
{
/* test_name */ "ReportingEndpointsInsideAndOutsideList",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=(\"" ORIGIN_C
"\";report-to=endpoint1);report-to=endpoint2",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
/* reporting_endpoint */ "endpoint2",
},
},
},
// DifferentReportingEndpoints
{
/* test_name */ "DifferentReportingEndpoints",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=\"" ORIGIN_B "\";report-to=endpoint1,"
"geolocation=\"" ORIGIN_C "\";report-to=endpoint2",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_B, /*has_subdomain_wildcard=*/false}},
"endpoint1",
},
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{{ORIGIN_C, /*has_subdomain_wildcard=*/false}},
"endpoint2",
},
},
},
{
/* test_name */ "InvalidReportingEndpointsBool",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=*;report-to",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
/* reporting_endpoint */ std::nullopt,
},
},
},
{
/* test_name */ "InvalidReportingEndpointsNumber",
/* feature_policy_string */ NOT_APPLICABLE,
/* permissions_policy_string */
"fullscreen=*;report-to=7",
/* self_origin */ ORIGIN_A,
/* src_origin */ ORIGIN_B,
/* expected_parse_result */
{
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
/* reporting_endpoint */ std::nullopt,
},
},
},
};
INSTANTIATE_TEST_SUITE_P(
All,
PermissionsPolicyParserParsingTest,
::testing::ValuesIn(PermissionsPolicyParserParsingTest::kCases),
[](const testing::TestParamInfo<PermissionsPolicyParserTestCase>&
param_info) { return param_info.param.test_name; });
TEST_P(PermissionsPolicyParserParsingTest, FeaturePolicyParsedCorrectly) {
PolicyParserMessageBuffer logger;
const PermissionsPolicyParserTestCase& test_case = GetParam();
if (test_case.feature_policy_string == NOT_APPLICABLE)
return;
ASSERT_NE(test_case.self_origin, nullptr);
CheckParsedPolicy(
ParseFeaturePolicy(test_case.feature_policy_string, test_case.self_origin,
test_case.src_origin, logger, test_feature_name_map),
test_case.expected_parse_result);
}
TEST_P(PermissionsPolicyParserParsingTest, PermissionsPolicyParsedCorrectly) {
PolicyParserMessageBuffer logger;
const PermissionsPolicyParserTestCase& test_case = GetParam();
if (test_case.permissions_policy_string == NOT_APPLICABLE)
return;
ASSERT_NE(test_case.self_origin, nullptr);
CheckParsedPolicy(
ParsePermissionsPolicy(test_case.permissions_policy_string,
test_case.self_origin, test_case.src_origin,
logger, test_feature_name_map),
test_case.expected_parse_result);
}
TEST_F(PermissionsPolicyParserParsingTest,
FeaturePolicyDuplicatedFeatureDeclaration) {
PolicyParserMessageBuffer logger;
// For Feature-Policy header, if there are multiple declaration for same
// feature, the allowlist value from *FIRST* declaration will be taken.
CheckParsedPolicy(
PermissionsPolicyParser::ParseHeader(
"geolocation 'none', geolocation 'self'", "", origin_a_.get(), logger,
logger, nullptr /* context */),
{
{
// allowlist value 'none' is expected.
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
EXPECT_TRUE(logger.GetMessages().empty());
}
TEST_F(PermissionsPolicyParserParsingTest,
PermissionsPolicyDuplicatedFeatureDeclaration) {
PolicyParserMessageBuffer logger;
// For Permissions-Policy header, if there are multiple declaration for same
// feature, the allowlist value from *LAST* declaration will be taken.
CheckParsedPolicy(
PermissionsPolicyParser::ParseHeader(
"", "geolocation=(), geolocation=self", origin_a_.get(), logger,
logger, nullptr /* context */),
{
{
// allowlist value 'self' is expected.
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
EXPECT_TRUE(logger.GetMessages().empty());
}
TEST_F(PermissionsPolicyParserParsingTest,
FeaturePolicyHeaderPermissionsPolicyHeaderCoExistConflictEntry) {
PolicyParserMessageBuffer logger;
// When there is conflict take the value from permission policy,
// non-conflicting entries will be merged.
CheckParsedPolicy(
PermissionsPolicyParser::ParseHeader(
"geolocation 'none', fullscreen 'self'",
"geolocation=self, payment=*", origin_a_.get(), logger, logger,
nullptr /* context */),
{
{
// With geolocation appearing in both headers,
// the value should be taken from permissions policy
// header, which is 'self' here.
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kPayment,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
}
TEST_F(PermissionsPolicyParserParsingTest,
OverlapDeclarationSingleWarningMessage) {
PolicyParserMessageBuffer feature_policy_logger("");
PolicyParserMessageBuffer permissions_policy_logger("");
CheckParsedPolicy(
PermissionsPolicyParser::ParseHeader(
"geolocation 'self', fullscreen 'self'" /* feature_policy_header */
,
"geolocation=*, fullscreen=*" /* permissions_policy_header */
,
origin_a_.get(), feature_policy_logger, permissions_policy_logger,
nullptr /* context */
),
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
{
mojom::blink::PermissionsPolicyFeature::kFullscreen,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ true,
/* matches_opaque_src */ true,
{},
},
});
CheckConsoleMessage(feature_policy_logger.GetMessages(),
{
"Some features are specified in both Feature-Policy "
"and Permissions-Policy header: geolocation, "
"fullscreen. Values defined in "
"Permissions-Policy header will be used.",
});
CheckConsoleMessage(permissions_policy_logger.GetMessages(), {});
}
TEST_F(PermissionsPolicyParserParsingTest,
FeaturePolicyHeaderPermissionsPolicyHeaderCoExistSeparateLogger) {
PolicyParserMessageBuffer feature_policy_logger("Feature Policy: ");
PolicyParserMessageBuffer permissions_policy_logger("Permissions Policy: ");
// 'geolocation' in permissions policy has a invalid allowlist item, which
// results in an empty allowlist, which is equivalent to "()" in permissions
// policy syntax.
CheckParsedPolicy(
PermissionsPolicyParser::ParseHeader(
"worse-feature 'none', geolocation 'self'" /* feature_policy_header */
,
"bad-feature=*, geolocation=\"data:///bad-origin\"" /* permissions_policy_header
*/
,
origin_a_.get(), feature_policy_logger, permissions_policy_logger,
nullptr /* context */
),
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ std::nullopt,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
CheckConsoleMessage(
feature_policy_logger.GetMessages(),
{
"Feature Policy: Unrecognized feature: 'worse-feature'.",
"Feature Policy: Some features are specified in both Feature-Policy "
"and Permissions-Policy header: geolocation. Values defined in "
"Permissions-Policy header will be used.",
});
CheckConsoleMessage(
permissions_policy_logger.GetMessages(),
{
"Permissions Policy: Unrecognized feature: 'bad-feature'.",
"Permissions Policy: Unrecognized origin: 'data:///bad-origin'.",
});
}
TEST_F(PermissionsPolicyParserParsingTest, CommaSeparatorInAttribute) {
PolicyParserMessageBuffer logger;
CheckParsedPolicy(
PermissionsPolicyParser::ParseAttribute(
"geolocation 'none', fullscreen 'self'",
/* self_origin */ origin_a_.get(),
/* src_origin */ origin_a_.get(), logger, /* context */ nullptr),
{
{
mojom::blink::PermissionsPolicyFeature::kGeolocation,
/* self_if_matches */ ORIGIN_A,
/* matches_all_origins */ false,
/* matches_opaque_src */ false,
{},
},
});
EXPECT_EQ(logger.GetMessages().size(), 2u)
<< "Parser should report parsing error.";
EXPECT_EQ(logger.GetMessages().front().content.Ascii(),
"Unrecognized origin: ''none','.")
<< "\"'none',\" should be treated as an invalid allowlist item ";
EXPECT_EQ(logger.GetMessages().back().content.Ascii(),
"Unrecognized origin: 'fullscreen'.")
<< "\"fullscreen\" should be treated as an invalid allowlist item";
}
TEST_F(PermissionsPolicyParserTest, ParseValidHeaderPolicy) {
for (const char* policy_string : kValidHeaderPolicies) {
PolicyParserMessageBuffer logger;
PermissionsPolicyParser::ParseFeaturePolicyForTest(
policy_string, origin_a_.get(), nullptr, logger, test_feature_name_map);
EXPECT_EQ(0UL, logger.GetMessages().size())
<< "Should parse " << policy_string;
}
}
TEST_F(PermissionsPolicyParserTest, ParseInvalidHeaderPolicy) {
for (const char* policy_string : kInvalidHeaderPolicies) {
PolicyParserMessageBuffer logger;
PermissionsPolicyParser::ParseFeaturePolicyForTest(
policy_string, origin_a_.get(), nullptr, logger, test_feature_name_map);
EXPECT_LT(0UL, logger.GetMessages().size())
<< "Should fail to parse " << policy_string;
}
}
TEST_F(PermissionsPolicyParserTest, ParseTooLongPolicy) {
PolicyParserMessageBuffer logger;
auto policy_string = "geolocation http://" + std::string(1 << 17, 'a');
PermissionsPolicyParser::ParseFeaturePolicyForTest(
policy_string.c_str(), origin_a_.get(), origin_b_.get(), logger,
test_feature_name_map);
EXPECT_EQ(1UL, logger.GetMessages().size())
<< "Should fail to parse feature policy string with size "
<< policy_string.size();
PermissionsPolicyParser::ParsePermissionsPolicyForTest(
policy_string.c_str(), origin_a_.get(), origin_b_.get(), logger,
test_feature_name_map);
EXPECT_EQ(2UL, logger.GetMessages().size())
<< "Should fail to parse permissions policy string with size "
<< policy_string.size();
}
// Test histogram counting the use of permissions policies in header.
TEST_F(PermissionsPolicyParserTest, HeaderHistogram) {
const char* histogram_name = "Blink.UseCounter.FeaturePolicy.Header";
base::HistogramTester tester;
PolicyParserMessageBuffer logger;
PermissionsPolicyParser::ParseFeaturePolicyForTest(
"payment; fullscreen", origin_a_.get(), nullptr, logger,
test_feature_name_map);
tester.ExpectTotalCount(histogram_name, 2);
tester.ExpectBucketCount(
histogram_name,
static_cast<int>(blink::mojom::blink::PermissionsPolicyFeature::kPayment),
1);
tester.ExpectBucketCount(
histogram_name,
static_cast<int>(
blink::mojom::blink::PermissionsPolicyFeature::kFullscreen),
1);
}
// Test counting the use of each permissions policy only once per header.
TEST_F(PermissionsPolicyParserTest, HistogramMultiple) {
const char* histogram_name = "Blink.UseCounter.FeaturePolicy.Header";
base::HistogramTester tester;
PolicyParserMessageBuffer logger;
// If the same feature is listed multiple times, it should only be counted
// once.
PermissionsPolicyParser::ParseFeaturePolicyForTest(
"geolocation 'self'; payment; geolocation *", origin_a_.get(), nullptr,
logger, test_feature_name_map);
PermissionsPolicyParser::ParseFeaturePolicyForTest(
"fullscreen 'self', fullscreen *", origin_a_.get(), nullptr, logger,
test_feature_name_map);
tester.ExpectTotalCount(histogram_name, 3);
tester.ExpectBucketCount(
histogram_name,
static_cast<int>(
blink::mojom::blink::PermissionsPolicyFeature::kGeolocation),
1);
tester.ExpectBucketCount(
histogram_name,
static_cast<int>(
blink::mojom::blink::PermissionsPolicyFeature::kFullscreen),
1);
}
// Tests the use counter for comma separator in declarations.
TEST_F(PermissionsPolicyParserTest, CommaSeparatedUseCounter) {
PolicyParserMessageBuffer logger;
// Declarations without a semicolon should not trigger the use counter.
{
auto dummy = std::make_unique<DummyPageHolder>();
ParseFeaturePolicyHeader("payment", origin_a_.get(), logger,
dummy->GetFrame().DomWindow());
EXPECT_FALSE(dummy->GetDocument().IsUseCounted(
WebFeature::kFeaturePolicyCommaSeparatedDeclarations));
}
// Validate that declarations which should trigger the use counter do.
{
auto dummy = std::make_unique<DummyPageHolder>();
ParseFeaturePolicyHeader("payment, fullscreen", origin_a_.get(), logger,
dummy->GetFrame().DomWindow());
EXPECT_TRUE(dummy->GetDocument().IsUseCounted(
WebFeature::kFeaturePolicyCommaSeparatedDeclarations))
<< "'payment, fullscreen' should trigger the comma separated use "
"counter.";
}
}
// Tests the use counter for semicolon separator in declarations.
TEST_F(PermissionsPolicyParserTest, SemicolonSeparatedUseCounter) {
PolicyParserMessageBuffer logger;
// Declarations without a semicolon should not trigger the use counter.
{
auto dummy = std::make_unique<DummyPageHolder>();
ParseFeaturePolicyHeader("payment", origin_a_.get(), logger,
dummy->GetFrame().DomWindow());
EXPECT_FALSE(dummy->GetDocument().IsUseCounted(
WebFeature::kFeaturePolicySemicolonSeparatedDeclarations));
}
// Validate that declarations which should trigger the use counter do.
{
auto dummy = std::make_unique<DummyPageHolder>();
ParseFeaturePolicyHeader("payment; fullscreen", origin_a_.get(), logger,
dummy->GetFrame().DomWindow());
EXPECT_TRUE(dummy->GetDocument().IsUseCounted(
WebFeature::kFeaturePolicySemicolonSeparatedDeclarations))
<< "'payment; fullscreen' should trigger the semicolon separated use "
"counter.";
}
}
// Test policy mutation methods
class FeaturePolicyMutationTest : public testing::Test {
protected:
FeaturePolicyMutationTest() = default;
~FeaturePolicyMutationTest() override = default;
url::Origin url_origin_a_ = url::Origin::Create(GURL(ORIGIN_A));
url::Origin url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
url::Origin url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
// Returns true if the policy contains a declaration for the feature which
// allows it in all origins.
bool IsFeatureAllowedEverywhere(
mojom::blink::PermissionsPolicyFeature feature,
const ParsedPermissionsPolicy& policy) {
const auto& result = base::ranges::find(
policy, feature, &ParsedPermissionsPolicyDeclaration::feature);
if (result == policy.end())
return false;
return result->feature == feature && result->matches_all_origins &&
result->matches_opaque_src && result->allowed_origins.empty();
}
// Returns true if the policy contains a declaration for the feature which
// disallows it in all origins.
bool IsFeatureDisallowedEverywhere(
mojom::blink::PermissionsPolicyFeature feature,
const ParsedPermissionsPolicy& policy) {
const auto& result = base::ranges::find(
policy, feature, &ParsedPermissionsPolicyDeclaration::feature);
if (result == policy.end())
return false;
return result->feature == feature && !result->matches_all_origins &&
!result->matches_opaque_src && result->allowed_origins.empty();
}
ParsedPermissionsPolicy test_policy = {
{mojom::blink::PermissionsPolicyFeature::kFullscreen,
/*allowed_origins=*/
{*blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_),
*blink::OriginWithPossibleWildcards::FromOrigin(url_origin_b_)},
/*self_if_matches=*/std::nullopt,
/*matches_all_origins=*/false,
/*matches_opaque_src=*/false},
{mojom::blink::PermissionsPolicyFeature::kGeolocation,
/*=allowed_origins*/
{*blink::OriginWithPossibleWildcards::FromOrigin(url_origin_a_)},
/*self_if_matches=*/std::nullopt,
/*matches_all_origins=*/false,
/*matches_opaque_src=*/false}};
ParsedPermissionsPolicy empty_policy = {};
test::TaskEnvironment task_environment_;
};
TEST_F(FeaturePolicyMutationTest, TestIsFeatureDeclared) {
EXPECT_TRUE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_TRUE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
EXPECT_FALSE(IsFeatureDeclared(mojom::blink::PermissionsPolicyFeature::kUsb,
test_policy));
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kNotFound, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestIsFeatureDeclaredWithEmptyPolicy) {
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, empty_policy));
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kNotFound, empty_policy));
}
TEST_F(FeaturePolicyMutationTest, TestRemoveAbsentFeature) {
ASSERT_EQ(2UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
EXPECT_FALSE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
ASSERT_EQ(2UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestRemoveFromEmptyPolicy) {
ASSERT_EQ(0UL, empty_policy.size());
EXPECT_FALSE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
ASSERT_EQ(0UL, empty_policy.size());
}
TEST_F(FeaturePolicyMutationTest, TestRemoveFeatureIfPresent) {
ASSERT_EQ(2UL, test_policy.size());
EXPECT_TRUE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_TRUE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_EQ(1UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
// Attempt to remove the feature again
EXPECT_FALSE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_EQ(1UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestRemoveFeatureIfPresentOnSecondFeature) {
ASSERT_EQ(2UL, test_policy.size());
EXPECT_TRUE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
EXPECT_TRUE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
ASSERT_EQ(1UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
// Attempt to remove the feature again
EXPECT_FALSE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
EXPECT_EQ(1UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestRemoveAllFeatures) {
ASSERT_EQ(2UL, test_policy.size());
EXPECT_TRUE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_TRUE(RemoveFeatureIfPresent(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
EXPECT_EQ(0UL, test_policy.size());
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
EXPECT_FALSE(IsFeatureDeclared(
mojom::blink::PermissionsPolicyFeature::kGeolocation, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestDisallowIfNotPresent) {
ParsedPermissionsPolicy copy = test_policy;
// Try to disallow a feature which already exists
EXPECT_FALSE(DisallowFeatureIfNotPresent(
mojom::blink::PermissionsPolicyFeature::kFullscreen, copy));
ASSERT_EQ(copy, test_policy);
// Disallow a new feature
EXPECT_TRUE(DisallowFeatureIfNotPresent(
mojom::blink::PermissionsPolicyFeature::kPayment, copy));
EXPECT_EQ(3UL, copy.size());
// Verify that the feature is, in fact, now disallowed everywhere
EXPECT_TRUE(IsFeatureDisallowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kPayment, copy));
}
TEST_F(FeaturePolicyMutationTest, TestAllowEverywhereIfNotPresent) {
ParsedPermissionsPolicy copy = test_policy;
// Try to allow a feature which already exists
EXPECT_FALSE(AllowFeatureEverywhereIfNotPresent(
mojom::blink::PermissionsPolicyFeature::kFullscreen, copy));
ASSERT_EQ(copy, test_policy);
// Allow a new feature
EXPECT_TRUE(AllowFeatureEverywhereIfNotPresent(
mojom::blink::PermissionsPolicyFeature::kPayment, copy));
EXPECT_EQ(3UL, copy.size());
// Verify that the feature is, in fact, allowed everywhere
EXPECT_TRUE(IsFeatureAllowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kPayment, copy));
}
TEST_F(FeaturePolicyMutationTest, TestDisallowUnconditionally) {
// Try to disallow a feature which already exists
DisallowFeature(mojom::blink::PermissionsPolicyFeature::kFullscreen,
test_policy);
// Should not have changed the number of declarations
EXPECT_EQ(2UL, test_policy.size());
// Verify that the feature is, in fact, now disallowed everywhere
EXPECT_TRUE(IsFeatureDisallowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestDisallowNewFeatureUnconditionally) {
// Try to disallow a feature which does not yet exist
DisallowFeature(mojom::blink::PermissionsPolicyFeature::kPayment,
test_policy);
// Should have added a new declaration
EXPECT_EQ(3UL, test_policy.size());
// Verify that the feature is, in fact, now disallowed everywhere
EXPECT_TRUE(IsFeatureDisallowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestAllowUnconditionally) {
// Try to allow a feature which already exists
AllowFeatureEverywhere(mojom::blink::PermissionsPolicyFeature::kFullscreen,
test_policy);
// Should not have changed the number of declarations
EXPECT_EQ(2UL, test_policy.size());
// Verify that the feature is, in fact, now allowed everywhere
EXPECT_TRUE(IsFeatureAllowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kFullscreen, test_policy));
}
TEST_F(FeaturePolicyMutationTest, TestAllowNewFeatureUnconditionally) {
// Try to allow a feature which does not yet exist
AllowFeatureEverywhere(mojom::blink::PermissionsPolicyFeature::kPayment,
test_policy);
// Should have added a new declaration
EXPECT_EQ(3UL, test_policy.size());
// Verify that the feature is, in fact, now allowed everywhere
EXPECT_TRUE(IsFeatureAllowedEverywhere(
mojom::blink::PermissionsPolicyFeature::kPayment, test_policy));
}
class PermissionsPolicyViolationHistogramTest : public testing::Test {
public:
PermissionsPolicyViolationHistogramTest(
const PermissionsPolicyViolationHistogramTest&) = delete;
PermissionsPolicyViolationHistogramTest& operator=(
const PermissionsPolicyViolationHistogramTest&) = delete;
protected:
PermissionsPolicyViolationHistogramTest() = default;
~PermissionsPolicyViolationHistogramTest() override = default;
test::TaskEnvironment task_environment_;
};
} // namespace blink