Optimize inherited permissions policy storage
`PermissionsPolicyFeatureState` used to store a map of _each_ enum value of `network::mojom::PermissionsPolicyFeature` to a boolean, telling whether the feature is enabled or not. There are more than a hundred enum values which makes this object large, expensive to copy, and expensive to mojo-serialze/deserialize. The new approach introduced in this CL stores this state on a bitset. Bug: 382291442 Change-Id: I798c58a04ece4b1c95c0940f4b92726ff0eeeddb Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6333964 Reviewed-by: Maks Orlovich <morlovich@chromium.org> Commit-Queue: Sandor «Alex» Major <sandormajor@chromium.org> Reviewed-by: David Bokan <bokan@chromium.org> Cr-Commit-Position: refs/heads/main@{#1429700}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
ad438f99b5
commit
5e79508c68
@ -355,6 +355,8 @@ component("web_platform") {
|
|||||||
"permissions_policy/permissions_policy_declaration.h",
|
"permissions_policy/permissions_policy_declaration.h",
|
||||||
"permissions_policy/permissions_policy_features.cc",
|
"permissions_policy/permissions_policy_features.cc",
|
||||||
"permissions_policy/permissions_policy_features.h",
|
"permissions_policy/permissions_policy_features.h",
|
||||||
|
"permissions_policy/permissions_policy_features_bitset.cc",
|
||||||
|
"permissions_policy/permissions_policy_features_bitset.h",
|
||||||
"permissions_policy/permissions_policy_features_generated.h",
|
"permissions_policy/permissions_policy_features_generated.h",
|
||||||
"permissions_policy/permissions_policy_features_internal.cc",
|
"permissions_policy/permissions_policy_features_internal.cc",
|
||||||
"permissions_policy/permissions_policy_features_internal.h",
|
"permissions_policy/permissions_policy_features_internal.h",
|
||||||
@ -706,6 +708,7 @@ source_set("tests") {
|
|||||||
"parsed_request_cookie_mojom_traits_unittest.cc",
|
"parsed_request_cookie_mojom_traits_unittest.cc",
|
||||||
"permissions_policy/origin_with_possible_wildcards_unittest.cc",
|
"permissions_policy/origin_with_possible_wildcards_unittest.cc",
|
||||||
"permissions_policy/permissions_policy_declaration_unittest.cc",
|
"permissions_policy/permissions_policy_declaration_unittest.cc",
|
||||||
|
"permissions_policy/permissions_policy_features_bitset_unittest.cc",
|
||||||
"permissions_policy/permissions_policy_unittest.cc",
|
"permissions_policy/permissions_policy_unittest.cc",
|
||||||
"proxy_config_mojom_traits_unittest.cc",
|
"proxy_config_mojom_traits_unittest.cc",
|
||||||
"request_destination_unittest.cc",
|
"request_destination_unittest.cc",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h"
|
#include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h"
|
||||||
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
|
||||||
#include "services/network/public/cpp/permissions_policy/permissions_policy_features.h"
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features.h"
|
||||||
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features_bitset.h"
|
||||||
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
||||||
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
|
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
@ -158,23 +159,24 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFromParsedPolicy(
|
|||||||
parsed_policy_for_isolated_app,
|
parsed_policy_for_isolated_app,
|
||||||
const url::Origin& origin,
|
const url::Origin& origin,
|
||||||
const network::PermissionsPolicyFeatureList& features) {
|
const network::PermissionsPolicyFeatureList& features) {
|
||||||
network::PermissionsPolicyFeatureState inherited_policies;
|
network::PermissionsPolicyFeaturesBitset inherited_policies;
|
||||||
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints =
|
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints =
|
||||||
parsed_policy_for_isolated_app
|
parsed_policy_for_isolated_app
|
||||||
? CombinePolicies(parsed_policy_for_isolated_app.value(),
|
? CombinePolicies(parsed_policy_for_isolated_app.value(),
|
||||||
parsed_policy)
|
parsed_policy)
|
||||||
: CreateAllowlistsAndReportingEndpoints(parsed_policy);
|
: CreateAllowlistsAndReportingEndpoints(parsed_policy);
|
||||||
for (const auto& feature : features) {
|
for (const auto& [feature, unused] : features) {
|
||||||
inherited_policies[feature.first] =
|
if (base::Contains(allow_lists_and_reporting_endpoints.allowlists_,
|
||||||
base::Contains(allow_lists_and_reporting_endpoints.allowlists_,
|
feature) &&
|
||||||
feature.first) &&
|
allow_lists_and_reporting_endpoints.allowlists_[feature].Contains(
|
||||||
allow_lists_and_reporting_endpoints.allowlists_[feature.first].Contains(
|
origin)) {
|
||||||
origin);
|
inherited_policies.Add(feature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PermissionsPolicy> new_policy = base::WrapUnique(
|
std::unique_ptr<PermissionsPolicy> new_policy = base::WrapUnique(
|
||||||
new PermissionsPolicy(origin, allow_lists_and_reporting_endpoints,
|
new PermissionsPolicy(origin, allow_lists_and_reporting_endpoints,
|
||||||
inherited_policies, features));
|
std::move(inherited_policies), features));
|
||||||
|
|
||||||
return new_policy;
|
return new_policy;
|
||||||
}
|
}
|
||||||
@ -187,8 +189,7 @@ bool PermissionsPolicy::IsHeaderlessUrl(const GURL& url) {
|
|||||||
|
|
||||||
bool PermissionsPolicy::IsFeatureEnabledByInheritedPolicy(
|
bool PermissionsPolicy::IsFeatureEnabledByInheritedPolicy(
|
||||||
network::mojom::PermissionsPolicyFeature feature) const {
|
network::mojom::PermissionsPolicyFeature feature) const {
|
||||||
DCHECK(base::Contains(inherited_policies_, feature));
|
return inherited_policies_.Contains(feature);
|
||||||
return inherited_policies_.at(feature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PermissionsPolicy::IsFeatureEnabled(
|
bool PermissionsPolicy::IsFeatureEnabled(
|
||||||
@ -482,7 +483,7 @@ const network::mojom::PermissionsPolicyFeature
|
|||||||
PermissionsPolicy::PermissionsPolicy(
|
PermissionsPolicy::PermissionsPolicy(
|
||||||
url::Origin origin,
|
url::Origin origin,
|
||||||
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
|
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
|
||||||
network::PermissionsPolicyFeatureState inherited_policies,
|
network::PermissionsPolicyFeaturesBitset inherited_policies,
|
||||||
const network::PermissionsPolicyFeatureList& feature_list,
|
const network::PermissionsPolicyFeatureList& feature_list,
|
||||||
bool headerless)
|
bool headerless)
|
||||||
: origin_(std::move(origin)),
|
: origin_(std::move(origin)),
|
||||||
@ -515,18 +516,17 @@ PermissionsPolicy::CreateFlexibleForFencedFrame(
|
|||||||
const network::ParsedPermissionsPolicy& container_policy,
|
const network::ParsedPermissionsPolicy& container_policy,
|
||||||
const url::Origin& subframe_origin,
|
const url::Origin& subframe_origin,
|
||||||
const network::PermissionsPolicyFeatureList& features) {
|
const network::PermissionsPolicyFeatureList& features) {
|
||||||
network::PermissionsPolicyFeatureState inherited_policies;
|
network::PermissionsPolicyFeaturesBitset inherited_policies;
|
||||||
for (const auto& feature : features) {
|
for (const auto& [feature, default_value] : features) {
|
||||||
if (base::Contains(network::kFencedFrameAllowedFeatures, feature.first)) {
|
if (base::Contains(network::kFencedFrameAllowedFeatures, feature) &&
|
||||||
inherited_policies[feature.first] = InheritedValueForFeature(
|
InheritedValueForFeature(subframe_origin, parent_policy,
|
||||||
subframe_origin, parent_policy, feature, container_policy);
|
{feature, default_value}, container_policy)) {
|
||||||
} else {
|
inherited_policies.Add(feature);
|
||||||
inherited_policies[feature.first] = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return base::WrapUnique(new PermissionsPolicy(
|
return base::WrapUnique(new PermissionsPolicy(
|
||||||
subframe_origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
subframe_origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
||||||
inherited_policies, features));
|
std::move(inherited_policies), features));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -547,18 +547,15 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFixedForFencedFrame(
|
|||||||
const network::PermissionsPolicyFeatureList& features,
|
const network::PermissionsPolicyFeatureList& features,
|
||||||
base::span<const network::mojom::PermissionsPolicyFeature>
|
base::span<const network::mojom::PermissionsPolicyFeature>
|
||||||
effective_enabled_permissions) {
|
effective_enabled_permissions) {
|
||||||
network::PermissionsPolicyFeatureState inherited_policies;
|
network::PermissionsPolicyFeaturesBitset inherited_policies;
|
||||||
for (const auto& feature : features) {
|
|
||||||
inherited_policies[feature.first] = false;
|
|
||||||
}
|
|
||||||
for (const network::mojom::PermissionsPolicyFeature feature :
|
for (const network::mojom::PermissionsPolicyFeature feature :
|
||||||
effective_enabled_permissions) {
|
effective_enabled_permissions) {
|
||||||
inherited_policies[feature] = true;
|
inherited_policies.Add(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base::WrapUnique(new PermissionsPolicy(
|
return base::WrapUnique(new PermissionsPolicy(
|
||||||
origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
||||||
inherited_policies, features));
|
std::move(inherited_policies), features));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -569,14 +566,16 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFromParentPolicy(
|
|||||||
const url::Origin& origin,
|
const url::Origin& origin,
|
||||||
const network::PermissionsPolicyFeatureList& features,
|
const network::PermissionsPolicyFeatureList& features,
|
||||||
bool headerless) {
|
bool headerless) {
|
||||||
network::PermissionsPolicyFeatureState inherited_policies;
|
network::PermissionsPolicyFeaturesBitset inherited_policies;
|
||||||
for (const auto& feature : features) {
|
for (const auto& [feature, default_value] : features) {
|
||||||
inherited_policies[feature.first] = InheritedValueForFeature(
|
if (InheritedValueForFeature(origin, parent_policy,
|
||||||
origin, parent_policy, feature, container_policy);
|
{feature, default_value}, container_policy)) {
|
||||||
|
inherited_policies.Add(feature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return base::WrapUnique(new PermissionsPolicy(
|
return base::WrapUnique(new PermissionsPolicy(
|
||||||
origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
origin, CreateAllowlistsAndReportingEndpoints(header_policy),
|
||||||
inherited_policies, features, headerless));
|
std::move(inherited_policies), features, headerless));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Permissions Policy 9.9: Is feature enabled in document for origin?
|
// Implements Permissions Policy 9.9: Is feature enabled in document for origin?
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h"
|
#include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h"
|
||||||
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
|
||||||
#include "services/network/public/cpp/permissions_policy/permissions_policy_features.h"
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features.h"
|
||||||
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features_bitset.h"
|
||||||
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
||||||
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
|
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
|
||||||
#include "url/origin.h"
|
#include "url/origin.h"
|
||||||
@ -321,7 +322,7 @@ class COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM) PermissionsPolicy {
|
|||||||
PermissionsPolicy(
|
PermissionsPolicy(
|
||||||
url::Origin origin,
|
url::Origin origin,
|
||||||
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
|
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
|
||||||
network::PermissionsPolicyFeatureState inherited_policies,
|
network::PermissionsPolicyFeaturesBitset inherited_policies,
|
||||||
const network::PermissionsPolicyFeatureList& feature_list,
|
const network::PermissionsPolicyFeatureList& feature_list,
|
||||||
bool headerless = false);
|
bool headerless = false);
|
||||||
static std::unique_ptr<PermissionsPolicy> CreateFromParentPolicy(
|
static std::unique_ptr<PermissionsPolicy> CreateFromParentPolicy(
|
||||||
@ -396,7 +397,7 @@ class COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM) PermissionsPolicy {
|
|||||||
|
|
||||||
// Records whether or not each feature was enabled for this frame by its
|
// Records whether or not each feature was enabled for this frame by its
|
||||||
// parent frame.
|
// parent frame.
|
||||||
const network::PermissionsPolicyFeatureState inherited_policies_;
|
const network::PermissionsPolicyFeaturesBitset inherited_policies_;
|
||||||
|
|
||||||
// The map of features to their default enable state.
|
// The map of features to their default enable state.
|
||||||
const raw_ref<const network::PermissionsPolicyFeatureList> feature_list_;
|
const raw_ref<const network::PermissionsPolicyFeatureList> feature_list_;
|
||||||
|
@ -65,11 +65,6 @@ const PermissionsPolicyFeatureList& GetPermissionsPolicyFeatureList(
|
|||||||
COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM)
|
COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM)
|
||||||
void UpdatePermissionsPolicyFeatureListForTesting();
|
void UpdatePermissionsPolicyFeatureListForTesting();
|
||||||
|
|
||||||
// TODO(iclelland): Generate, instead of this map, a set of bool flags, one
|
|
||||||
// for each feature, as all features are supposed to be represented here.
|
|
||||||
using PermissionsPolicyFeatureState =
|
|
||||||
std::map<network::mojom::PermissionsPolicyFeature, bool>;
|
|
||||||
|
|
||||||
} // namespace network
|
} // namespace network
|
||||||
|
|
||||||
#endif // SERVICES_NETWORK_PUBLIC_CPP_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FEATURES_H_
|
#endif // SERVICES_NETWORK_PUBLIC_CPP_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FEATURES_H_
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2025 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features_bitset.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "base/check_op.h"
|
||||||
|
#include "base/containers/span.h"
|
||||||
|
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset::PermissionsPolicyFeaturesBitset(
|
||||||
|
const PermissionsPolicyFeaturesBitset&) = default;
|
||||||
|
PermissionsPolicyFeaturesBitset& PermissionsPolicyFeaturesBitset::operator=(
|
||||||
|
const PermissionsPolicyFeaturesBitset&) = default;
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset::PermissionsPolicyFeaturesBitset(
|
||||||
|
PermissionsPolicyFeaturesBitset&&) = default;
|
||||||
|
PermissionsPolicyFeaturesBitset& PermissionsPolicyFeaturesBitset::operator=(
|
||||||
|
PermissionsPolicyFeaturesBitset&&) = default;
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset::PermissionsPolicyFeaturesBitset() = default;
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset::~PermissionsPolicyFeaturesBitset() = default;
|
||||||
|
|
||||||
|
void PermissionsPolicyFeaturesBitset::Add(
|
||||||
|
network::mojom::PermissionsPolicyFeature feature) {
|
||||||
|
size_t index = static_cast<size_t>(feature);
|
||||||
|
CHECK_LT(index, kPermissionsPolicyFeaturesBitsetSize);
|
||||||
|
size_t internal_index = ToInternalIndex(index);
|
||||||
|
uint8_t bitmask = ToBitmask(index);
|
||||||
|
bitset_[internal_index] |= bitmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PermissionsPolicyFeaturesBitset::Contains(
|
||||||
|
network::mojom::PermissionsPolicyFeature feature) const {
|
||||||
|
return Contains(static_cast<size_t>(feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PermissionsPolicyFeaturesBitset::Contains(size_t index) const {
|
||||||
|
CHECK_LT(index, kPermissionsPolicyFeaturesBitsetSize);
|
||||||
|
size_t internal_index = ToInternalIndex(index);
|
||||||
|
uint8_t bitmask = ToBitmask(index);
|
||||||
|
return (bitset_[internal_index] & bitmask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PermissionsPolicyFeaturesBitset::Serialize() const {
|
||||||
|
// Since the bitset is stored from right to left, as an optimization, omit all
|
||||||
|
// the leftmost 0's.
|
||||||
|
size_t offset;
|
||||||
|
for (offset = 0; offset < bitset_.size(); ++offset) {
|
||||||
|
if (bitset_[offset] != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base::span<const char> s =
|
||||||
|
base::as_chars(base::span(bitset_).subspan(offset));
|
||||||
|
return std::string(s.begin(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PermissionsPolicyFeaturesBitset::Deserialize(std::string_view data) {
|
||||||
|
if (data.size() > bitset_.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the passed `data` to the end of the internal `bitset_`. For example,
|
||||||
|
// if `data` is {0xAA, 0xBB}, and set size is 32 (so `bitset_` is a vector of
|
||||||
|
// 4 uint8_t's), then the final `bitset_` should be {0x00, 0x00, 0xAA, 0xBB}.
|
||||||
|
base::span(bitset_).last(data.size()).copy_from(base::as_byte_span((data)));
|
||||||
|
|
||||||
|
// Zero out the rest of `bitset_` to clear any residual data.
|
||||||
|
size_t zero_count = kPermissionsPolicyFeaturesBitsetArraySize - data.size();
|
||||||
|
if (zero_count > 0) {
|
||||||
|
std::fill(bitset_.begin(), bitset_.begin() + zero_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PermissionsPolicyFeaturesBitset::bitset_size() const {
|
||||||
|
return kPermissionsPolicyFeaturesBitsetSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PermissionsPolicyFeaturesBitset::ToInternalIndex(size_t index) const {
|
||||||
|
// Note: internally, the bitset is stored from right to left. For example,
|
||||||
|
// index 0 maps to the least significant bit of the last element of `bitset_`.
|
||||||
|
return bitset_.size() - 1 - index / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t PermissionsPolicyFeaturesBitset::ToBitmask(size_t index) const {
|
||||||
|
return 1 << (index % 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace network
|
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2025 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef SERVICES_NETWORK_PUBLIC_CPP_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FEATURES_BITSET_H_
|
||||||
|
#define SERVICES_NETWORK_PUBLIC_CPP_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FEATURES_BITSET_H_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/component_export.h"
|
||||||
|
#include "base/gtest_prod_util.h"
|
||||||
|
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
inline constexpr size_t kPermissionsPolicyFeaturesBitsetSize =
|
||||||
|
static_cast<size_t>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
|
||||||
|
1;
|
||||||
|
inline constexpr size_t kPermissionsPolicyFeaturesBitsetArraySize =
|
||||||
|
1 + (kPermissionsPolicyFeaturesBitsetSize - 1) / 8;
|
||||||
|
|
||||||
|
// A custom bitset class used to keep track of permissions policy feature state
|
||||||
|
// across processes. `std::bitset<>` and `base::EnumSet<>` are intentionally not
|
||||||
|
// used because they do not provide the flexibility needed to efficiently
|
||||||
|
// serialize/deserialize the set.
|
||||||
|
class COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM)
|
||||||
|
PermissionsPolicyFeaturesBitset {
|
||||||
|
public:
|
||||||
|
// Creates an empty PermissionsPolicyFeaturesBitset with the size of
|
||||||
|
// `network::mojom::PermissionsPolicyFeature`.
|
||||||
|
explicit PermissionsPolicyFeaturesBitset();
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset(const PermissionsPolicyFeaturesBitset&);
|
||||||
|
PermissionsPolicyFeaturesBitset& operator=(
|
||||||
|
const PermissionsPolicyFeaturesBitset&);
|
||||||
|
|
||||||
|
PermissionsPolicyFeaturesBitset(PermissionsPolicyFeaturesBitset&&);
|
||||||
|
PermissionsPolicyFeaturesBitset& operator=(PermissionsPolicyFeaturesBitset&&);
|
||||||
|
|
||||||
|
friend bool operator==(const PermissionsPolicyFeaturesBitset&,
|
||||||
|
const PermissionsPolicyFeaturesBitset&) = default;
|
||||||
|
|
||||||
|
~PermissionsPolicyFeaturesBitset();
|
||||||
|
|
||||||
|
// Adds the given `feature` to the bitset.
|
||||||
|
void Add(network::mojom::PermissionsPolicyFeature feature);
|
||||||
|
// Returns whether the given `feature` is present in the bitset.
|
||||||
|
bool Contains(network::mojom::PermissionsPolicyFeature feature) const;
|
||||||
|
|
||||||
|
// Serializes `this` and returns the compressed value. The bitset can be
|
||||||
|
// deserialized with `Deserialize()`.
|
||||||
|
std::string Serialize() const;
|
||||||
|
|
||||||
|
// Deserializes the bitset from `data`. If `data` is longer than the bitset,
|
||||||
|
// returns false.
|
||||||
|
bool Deserialize(std::string_view data);
|
||||||
|
|
||||||
|
// Size of the bitset as number of bits.
|
||||||
|
size_t bitset_size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(PermissionsPolicyFeaturesBitsetTest,
|
||||||
|
PermissionsPolicyFeaturesBitset);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(PermissionsPolicyFeaturesBitsetTest,
|
||||||
|
DeserializePartialData);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(PermissionsPolicyFeaturesBitsetTest,
|
||||||
|
DeserializeRepeatedly);
|
||||||
|
|
||||||
|
// Returns whether the given `index` is present in the bitset. Carved out of
|
||||||
|
// the public `Contains()` method to make it available for tests because the
|
||||||
|
// enums are not sequential.
|
||||||
|
bool Contains(size_t index) const;
|
||||||
|
// Returns which element `index` maps to in `bitset_`.
|
||||||
|
size_t ToInternalIndex(size_t index) const;
|
||||||
|
// Returns which bit `index` maps to given a certain `bitset_` element.
|
||||||
|
uint8_t ToBitmask(size_t index) const;
|
||||||
|
|
||||||
|
std::array<uint8_t, kPermissionsPolicyFeaturesBitsetArraySize> bitset_ = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
|
||||||
|
#endif // SERVICES_NETWORK_PUBLIC_CPP_PERMISSIONS_POLICY_PERMISSIONS_POLICY_FEATURES_BITSET_H_
|
103
services/network/public/cpp/permissions_policy/permissions_policy_features_bitset_unittest.cc
Normal file
103
services/network/public/cpp/permissions_policy/permissions_policy_features_bitset_unittest.cc
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2025 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "services/network/public/cpp/permissions_policy/permissions_policy_features_bitset.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
TEST(PermissionsPolicyFeaturesBitsetTest, PermissionsPolicyFeaturesBitset) {
|
||||||
|
// Create a bitset and verify that it is initially empty.
|
||||||
|
PermissionsPolicyFeaturesBitset bitset1;
|
||||||
|
EXPECT_EQ(kPermissionsPolicyFeaturesBitsetSize, bitset1.bitset_size());
|
||||||
|
for (size_t i = 0; i < bitset1.bitset_size(); ++i) {
|
||||||
|
EXPECT_FALSE(bitset1.Contains(i));
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(bitset1.Serialize().empty());
|
||||||
|
|
||||||
|
// Add some elements to the bitset, and verify they can be looked up.
|
||||||
|
bitset1.Add(network::mojom::PermissionsPolicyFeature::kNotFound);
|
||||||
|
bitset1.Add(network::mojom::PermissionsPolicyFeature::kUsb);
|
||||||
|
for (size_t i = 0; i < bitset1.bitset_size(); ++i) {
|
||||||
|
EXPECT_EQ(bitset1.Contains(i), i == 0 || i == 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the bitset. The trailing zeros should be optimized away, and
|
||||||
|
// the resulting output should fit in 2 bytes (0b01000000 and 0b00000001).
|
||||||
|
std::string serialized = bitset1.Serialize();
|
||||||
|
EXPECT_EQ(serialized.size(), 2U);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[0]), 0b01000000);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[1]), 0b00000001);
|
||||||
|
|
||||||
|
// Create a new bitset using the serialized data and verify that it contains
|
||||||
|
// the same data.
|
||||||
|
PermissionsPolicyFeaturesBitset bitset2;
|
||||||
|
EXPECT_TRUE(bitset2.Deserialize(serialized));
|
||||||
|
EXPECT_EQ(kPermissionsPolicyFeaturesBitsetSize, bitset2.bitset_size());
|
||||||
|
for (size_t i = 0; i < bitset2.bitset_size(); ++i) {
|
||||||
|
EXPECT_EQ(bitset1.Contains(i), bitset2.Contains(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some last few checks for good measure.
|
||||||
|
bitset2.Add(network::mojom::PermissionsPolicyFeature::kScreenWakeLock);
|
||||||
|
// Adding the same element a second time should have no impact.
|
||||||
|
bitset2.Add(network::mojom::PermissionsPolicyFeature::kScreenWakeLock);
|
||||||
|
for (size_t i = 0; i < bitset2.bitset_size(); ++i) {
|
||||||
|
EXPECT_EQ(bitset2.Contains(i), i == 0 || i == 14 || i == 31);
|
||||||
|
}
|
||||||
|
serialized = bitset2.Serialize();
|
||||||
|
EXPECT_EQ(serialized.size(), 4U);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[0]), 0b10000000);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[1]), 0b00000000);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[2]), 0b01000000);
|
||||||
|
EXPECT_EQ(static_cast<uint8_t>(serialized[3]), 0b00000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PermissionsPolicyFeaturesBitsetTest, DeserializeTooLargeData) {
|
||||||
|
std::string data(kPermissionsPolicyFeaturesBitsetArraySize + 1, 0xFF);
|
||||||
|
PermissionsPolicyFeaturesBitset bitset;
|
||||||
|
EXPECT_FALSE(bitset.Deserialize(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PermissionsPolicyFeaturesBitsetTest, DeserializePartialData) {
|
||||||
|
PermissionsPolicyFeaturesBitset bitset;
|
||||||
|
std::string data = "\x01\x02\x03";
|
||||||
|
ASSERT_TRUE(bitset.Deserialize(data));
|
||||||
|
|
||||||
|
std::array<uint8_t, kPermissionsPolicyFeaturesBitsetArraySize>
|
||||||
|
expected_bitset = {};
|
||||||
|
expected_bitset[kPermissionsPolicyFeaturesBitsetArraySize - 3] = 0x01;
|
||||||
|
expected_bitset[kPermissionsPolicyFeaturesBitsetArraySize - 2] = 0x02;
|
||||||
|
expected_bitset[kPermissionsPolicyFeaturesBitsetArraySize - 1] = 0x03;
|
||||||
|
|
||||||
|
EXPECT_EQ(bitset.bitset_, expected_bitset);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PermissionsPolicyFeaturesBitsetTest, DeserializeRepeatedly) {
|
||||||
|
PermissionsPolicyFeaturesBitset bitset;
|
||||||
|
std::string data1 = "\xAA\xBB";
|
||||||
|
ASSERT_TRUE(bitset.Deserialize(data1));
|
||||||
|
|
||||||
|
std::array<uint8_t, kPermissionsPolicyFeaturesBitsetArraySize>
|
||||||
|
expected_bitset1 = {};
|
||||||
|
expected_bitset1[kPermissionsPolicyFeaturesBitsetArraySize - 2] = 0xAA;
|
||||||
|
expected_bitset1[kPermissionsPolicyFeaturesBitsetArraySize - 1] = 0xBB;
|
||||||
|
|
||||||
|
ASSERT_EQ(bitset.bitset_, expected_bitset1);
|
||||||
|
|
||||||
|
std::string data2 = "\xCC";
|
||||||
|
ASSERT_TRUE(bitset.Deserialize(data2));
|
||||||
|
|
||||||
|
std::array<uint8_t, kPermissionsPolicyFeaturesBitsetArraySize>
|
||||||
|
expected_bitset2 = {};
|
||||||
|
expected_bitset2[kPermissionsPolicyFeaturesBitsetArraySize - 1] = 0xCC;
|
||||||
|
|
||||||
|
ASSERT_EQ(bitset.bitset_, expected_bitset2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace network
|
@ -29,25 +29,17 @@ namespace network {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const network::mojom::PermissionsPolicyFeature kDefaultOnFeature =
|
const network::mojom::PermissionsPolicyFeature kDefaultOnFeature =
|
||||||
static_cast<network::mojom::PermissionsPolicyFeature>(
|
network::mojom::PermissionsPolicyFeature::kDeferredFetchMinimal;
|
||||||
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
|
|
||||||
1);
|
|
||||||
|
|
||||||
const network::mojom::PermissionsPolicyFeature kDefaultSelfFeature =
|
const network::mojom::PermissionsPolicyFeature kDefaultSelfFeature =
|
||||||
static_cast<network::mojom::PermissionsPolicyFeature>(
|
network::mojom::PermissionsPolicyFeature::kAmbientLightSensor;
|
||||||
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
|
|
||||||
2);
|
|
||||||
|
|
||||||
const network::mojom::PermissionsPolicyFeature kDefaultOffFeature =
|
const network::mojom::PermissionsPolicyFeature kDefaultOffFeature =
|
||||||
static_cast<network::mojom::PermissionsPolicyFeature>(
|
network::mojom::PermissionsPolicyFeature::kUnload;
|
||||||
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
|
|
||||||
3);
|
|
||||||
|
|
||||||
// This feature is defined in code, but not present in the feature list.
|
// This feature is defined in mojo, but not present in the feature list.
|
||||||
const network::mojom::PermissionsPolicyFeature kUnavailableFeature =
|
const network::mojom::PermissionsPolicyFeature kUnavailableFeature =
|
||||||
static_cast<network::mojom::PermissionsPolicyFeature>(
|
network::mojom::PermissionsPolicyFeature::kNotFound;
|
||||||
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
|
|
||||||
4);
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -135,7 +127,7 @@ class PermissionsPolicyTest : public testing::Test {
|
|||||||
bool PolicyContainsInheritedValue(
|
bool PolicyContainsInheritedValue(
|
||||||
const PermissionsPolicy* policy,
|
const PermissionsPolicy* policy,
|
||||||
network::mojom::PermissionsPolicyFeature feature) {
|
network::mojom::PermissionsPolicyFeature feature) {
|
||||||
return base::Contains(policy->inherited_policies_, feature);
|
return policy->inherited_policies_.Contains(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
url::Origin origin_a_ = url::Origin::Create(GURL("https://example.com/"));
|
url::Origin origin_a_ = url::Origin::Create(GURL("https://example.com/"));
|
||||||
|
@ -2149,7 +2149,6 @@ WebLocalFrameImpl* WebLocalFrameImpl::CreateProvisional(
|
|||||||
frame_token);
|
frame_token);
|
||||||
network::mojom::blink::WebSandboxFlags sandbox_flags =
|
network::mojom::blink::WebSandboxFlags sandbox_flags =
|
||||||
network::mojom::blink::WebSandboxFlags::kNone;
|
network::mojom::blink::WebSandboxFlags::kNone;
|
||||||
network::PermissionsPolicyFeatureState feature_state;
|
|
||||||
if (!previous_frame->Owner() || previous_frame->IsFencedFrameRoot()) {
|
if (!previous_frame->Owner() || previous_frame->IsFencedFrameRoot()) {
|
||||||
// Provisional main frames need to force sandbox flags. This is necessary
|
// Provisional main frames need to force sandbox flags. This is necessary
|
||||||
// to inherit sandbox flags when a sandboxed frame does a window.open()
|
// to inherit sandbox flags when a sandboxed frame does a window.open()
|
||||||
|
Reference in New Issue
Block a user