0

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:
Sandor Major
2025-03-07 13:13:09 -08:00
committed by Chromium LUCI CQ
parent ad438f99b5
commit 5e79508c68
9 changed files with 327 additions and 52 deletions

@ -355,6 +355,8 @@ component("web_platform") {
"permissions_policy/permissions_policy_declaration.h",
"permissions_policy/permissions_policy_features.cc",
"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_internal.cc",
"permissions_policy/permissions_policy_features_internal.h",
@ -706,6 +708,7 @@ source_set("tests") {
"parsed_request_cookie_mojom_traits_unittest.cc",
"permissions_policy/origin_with_possible_wildcards_unittest.cc",
"permissions_policy/permissions_policy_declaration_unittest.cc",
"permissions_policy/permissions_policy_features_bitset_unittest.cc",
"permissions_policy/permissions_policy_unittest.cc",
"proxy_config_mojom_traits_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/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_bitset.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 "url/gurl.h"
@ -158,23 +159,24 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFromParsedPolicy(
parsed_policy_for_isolated_app,
const url::Origin& origin,
const network::PermissionsPolicyFeatureList& features) {
network::PermissionsPolicyFeatureState inherited_policies;
network::PermissionsPolicyFeaturesBitset inherited_policies;
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints =
parsed_policy_for_isolated_app
? CombinePolicies(parsed_policy_for_isolated_app.value(),
parsed_policy)
: CreateAllowlistsAndReportingEndpoints(parsed_policy);
for (const auto& feature : features) {
inherited_policies[feature.first] =
base::Contains(allow_lists_and_reporting_endpoints.allowlists_,
feature.first) &&
allow_lists_and_reporting_endpoints.allowlists_[feature.first].Contains(
origin);
for (const auto& [feature, unused] : features) {
if (base::Contains(allow_lists_and_reporting_endpoints.allowlists_,
feature) &&
allow_lists_and_reporting_endpoints.allowlists_[feature].Contains(
origin)) {
inherited_policies.Add(feature);
}
}
std::unique_ptr<PermissionsPolicy> new_policy = base::WrapUnique(
new PermissionsPolicy(origin, allow_lists_and_reporting_endpoints,
inherited_policies, features));
std::move(inherited_policies), features));
return new_policy;
}
@ -187,8 +189,7 @@ bool PermissionsPolicy::IsHeaderlessUrl(const GURL& url) {
bool PermissionsPolicy::IsFeatureEnabledByInheritedPolicy(
network::mojom::PermissionsPolicyFeature feature) const {
DCHECK(base::Contains(inherited_policies_, feature));
return inherited_policies_.at(feature);
return inherited_policies_.Contains(feature);
}
bool PermissionsPolicy::IsFeatureEnabled(
@ -482,7 +483,7 @@ const network::mojom::PermissionsPolicyFeature
PermissionsPolicy::PermissionsPolicy(
url::Origin origin,
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
network::PermissionsPolicyFeatureState inherited_policies,
network::PermissionsPolicyFeaturesBitset inherited_policies,
const network::PermissionsPolicyFeatureList& feature_list,
bool headerless)
: origin_(std::move(origin)),
@ -515,18 +516,17 @@ PermissionsPolicy::CreateFlexibleForFencedFrame(
const network::ParsedPermissionsPolicy& container_policy,
const url::Origin& subframe_origin,
const network::PermissionsPolicyFeatureList& features) {
network::PermissionsPolicyFeatureState inherited_policies;
for (const auto& feature : features) {
if (base::Contains(network::kFencedFrameAllowedFeatures, feature.first)) {
inherited_policies[feature.first] = InheritedValueForFeature(
subframe_origin, parent_policy, feature, container_policy);
} else {
inherited_policies[feature.first] = false;
network::PermissionsPolicyFeaturesBitset inherited_policies;
for (const auto& [feature, default_value] : features) {
if (base::Contains(network::kFencedFrameAllowedFeatures, feature) &&
InheritedValueForFeature(subframe_origin, parent_policy,
{feature, default_value}, container_policy)) {
inherited_policies.Add(feature);
}
}
return base::WrapUnique(new PermissionsPolicy(
subframe_origin, CreateAllowlistsAndReportingEndpoints(header_policy),
inherited_policies, features));
std::move(inherited_policies), features));
}
// static
@ -547,18 +547,15 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFixedForFencedFrame(
const network::PermissionsPolicyFeatureList& features,
base::span<const network::mojom::PermissionsPolicyFeature>
effective_enabled_permissions) {
network::PermissionsPolicyFeatureState inherited_policies;
for (const auto& feature : features) {
inherited_policies[feature.first] = false;
}
network::PermissionsPolicyFeaturesBitset inherited_policies;
for (const network::mojom::PermissionsPolicyFeature feature :
effective_enabled_permissions) {
inherited_policies[feature] = true;
inherited_policies.Add(feature);
}
return base::WrapUnique(new PermissionsPolicy(
origin, CreateAllowlistsAndReportingEndpoints(header_policy),
inherited_policies, features));
std::move(inherited_policies), features));
}
// static
@ -569,14 +566,16 @@ std::unique_ptr<PermissionsPolicy> PermissionsPolicy::CreateFromParentPolicy(
const url::Origin& origin,
const network::PermissionsPolicyFeatureList& features,
bool headerless) {
network::PermissionsPolicyFeatureState inherited_policies;
for (const auto& feature : features) {
inherited_policies[feature.first] = InheritedValueForFeature(
origin, parent_policy, feature, container_policy);
network::PermissionsPolicyFeaturesBitset inherited_policies;
for (const auto& [feature, default_value] : features) {
if (InheritedValueForFeature(origin, parent_policy,
{feature, default_value}, container_policy)) {
inherited_policies.Add(feature);
}
}
return base::WrapUnique(new PermissionsPolicy(
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?

@ -14,6 +14,7 @@
#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_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/web_sandbox_flags.mojom-shared.h"
#include "url/origin.h"
@ -321,7 +322,7 @@ class COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM) PermissionsPolicy {
PermissionsPolicy(
url::Origin origin,
AllowlistsAndReportingEndpoints allow_lists_and_reporting_endpoints,
network::PermissionsPolicyFeatureState inherited_policies,
network::PermissionsPolicyFeaturesBitset inherited_policies,
const network::PermissionsPolicyFeatureList& feature_list,
bool headerless = false);
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
// parent frame.
const network::PermissionsPolicyFeatureState inherited_policies_;
const network::PermissionsPolicyFeaturesBitset inherited_policies_;
// The map of features to their default enable state.
const raw_ref<const network::PermissionsPolicyFeatureList> feature_list_;

@ -65,11 +65,6 @@ const PermissionsPolicyFeatureList& GetPermissionsPolicyFeatureList(
COMPONENT_EXPORT(NETWORK_CPP_WEB_PLATFORM)
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
#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_

@ -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 {
const network::mojom::PermissionsPolicyFeature kDefaultOnFeature =
static_cast<network::mojom::PermissionsPolicyFeature>(
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
1);
network::mojom::PermissionsPolicyFeature::kDeferredFetchMinimal;
const network::mojom::PermissionsPolicyFeature kDefaultSelfFeature =
static_cast<network::mojom::PermissionsPolicyFeature>(
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
2);
network::mojom::PermissionsPolicyFeature::kAmbientLightSensor;
const network::mojom::PermissionsPolicyFeature kDefaultOffFeature =
static_cast<network::mojom::PermissionsPolicyFeature>(
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
3);
network::mojom::PermissionsPolicyFeature::kUnload;
// 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 =
static_cast<network::mojom::PermissionsPolicyFeature>(
static_cast<int>(network::mojom::PermissionsPolicyFeature::kMaxValue) +
4);
network::mojom::PermissionsPolicyFeature::kNotFound;
} // namespace
@ -135,7 +127,7 @@ class PermissionsPolicyTest : public testing::Test {
bool PolicyContainsInheritedValue(
const PermissionsPolicy* policy,
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/"));

@ -2149,7 +2149,6 @@ WebLocalFrameImpl* WebLocalFrameImpl::CreateProvisional(
frame_token);
network::mojom::blink::WebSandboxFlags sandbox_flags =
network::mojom::blink::WebSandboxFlags::kNone;
network::PermissionsPolicyFeatureState feature_state;
if (!previous_frame->Owner() || previous_frame->IsFencedFrameRoot()) {
// Provisional main frames need to force sandbox flags. This is necessary
// to inherit sandbox flags when a sandboxed frame does a window.open()