Fenced frame: introduce `allow` attribute
This CL introduces a new `allow` attribute for fenced frames. This is
based off of the attribute of the same name for iframes, and has the
same syntax for building up the permissions policies.
The attribute is sent through a new fenced frame mojo call,
`DidChangeFramePolicy`, where it is read into the fenced frame's frame
tree node to be processed during navigation.
`NavigationRequest::CheckPermissionsPoliciesForFencedFrames` now
includes checks for the fenced frame's frame policy on top of the checks
currently in place for its parent's policies.
When the fenced frame configuration code is in place, the k-anonymous
permissions needed for the fenced frame to load will be checked against
the allowed permissions which includes the "allow" attribute. Until
then, this uses the same restrictions that are currently in place for
the parent's permissions policies of a fenced frame. That is, if a
permissions policy is explicitly specified with the `allow` attribute,
attribution reporting and shared storage must be enabled for the frame's
origin.
See the "Permission policy for API backed fenced frames" explainer:
https://github.com/WICG/fenced-frame/blob/master/explainer/permissions_policy_for_API_backed_fenced_frames.md
Change-Id: I9b0e35a41edaa0dab5f7e573c0a5c4ff2c1afa61
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3999825
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Commit-Queue: Liam Brady <lbrady@google.com>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Shivani Sharma <shivanisha@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1078585}
diff --git a/content/browser/fenced_frame/fenced_frame.cc b/content/browser/fenced_frame/fenced_frame.cc
index cbb10ae..0441b5fb 100644
--- a/content/browser/fenced_frame/fenced_frame.cc
+++ b/content/browser/fenced_frame/fenced_frame.cc
@@ -277,4 +277,13 @@
void FencedFrame::UpdateOverridingUserAgent() {}
+void FencedFrame::DidChangeFramePolicy(const blink::FramePolicy& frame_policy) {
+ FrameTreeNode* inner_root = frame_tree_->root();
+ const blink::FramePolicy& current_frame_policy =
+ inner_root->pending_frame_policy();
+ inner_root->SetPendingFramePolicy(blink::FramePolicy(
+ current_frame_policy.sandbox_flags, frame_policy.container_policy,
+ current_frame_policy.required_document_policy));
+}
+
} // namespace content
diff --git a/content/browser/fenced_frame/fenced_frame.h b/content/browser/fenced_frame/fenced_frame.h
index 69a08d7..6c24d58 100644
--- a/content/browser/fenced_frame/fenced_frame.h
+++ b/content/browser/fenced_frame/fenced_frame.h
@@ -57,6 +57,7 @@
// blink::mojom::FencedFrameOwnerHost implementation.
void Navigate(const GURL& url,
base::TimeTicks navigation_start_time) override;
+ void DidChangeFramePolicy(const blink::FramePolicy& frame_policy) override;
// FrameTree::Delegate.
void DidStartLoading(FrameTreeNode* frame_tree_node,
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index ba69ce62..d20610e 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -379,6 +379,10 @@
fenced_frame_->Navigate(url, navigation_start_time);
}
+ void DidChangeFramePolicy(const blink::FramePolicy& frame_policy) override {
+ fenced_frame_->DidChangeFramePolicy(frame_policy);
+ }
+
private:
mojo::AssociatedRemote<blink::mojom::FencedFrameOwnerHost> original_remote_;
mojo::AssociatedReceiver<blink::mojom::FencedFrameOwnerHost> receiver_{
diff --git a/content/browser/renderer_host/frame_tree_node.cc b/content/browser/renderer_host/frame_tree_node.cc
index c6bafa8..24ceaf2 100644
--- a/content/browser/renderer_host/frame_tree_node.cc
+++ b/content/browser/renderer_host/frame_tree_node.cc
@@ -480,6 +480,15 @@
pending_frame_policy_.required_document_policy =
frame_policy.required_document_policy;
}
+
+ // Fenced frame roots do not have a parent, so add an extra check here to
+ // still allow a fenced frame to properly set its container policy. The
+ // required document policy and sandbox flags should stay unmodified.
+ if (IsFencedFrameRoot()) {
+ DCHECK(pending_frame_policy_.required_document_policy.empty());
+ DCHECK_EQ(pending_frame_policy_.sandbox_flags, frame_policy.sandbox_flags);
+ pending_frame_policy_.container_policy = frame_policy.container_policy;
+ }
}
void FrameTreeNode::SetAttributes(
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index cc6507f..e678bffa 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -228,8 +228,10 @@
// this frame. This includes flags inherited from parent frames and the latest
// flags from the <iframe> element hosting this frame. The returned policies
// may not yet have taken effect, since "sandbox" and "allow" attribute
- // updates in an <iframe> element take effect on next navigation. To retrieve
- // the currently active policy for this frame, use effective_frame_policy().
+ // updates in an <iframe> element take effect on next navigation. For
+ // <fencedframe> elements, not everything in the frame policy might actually
+ // take effect after the navigation. To retrieve the currently active policy
+ // for this frame, use effective_frame_policy().
const blink::FramePolicy& pending_frame_policy() const {
return pending_frame_policy_;
}
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 20e4532a..6865af82 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -154,6 +154,7 @@
#include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
#include "third_party/blink/public/common/navigation/navigation_policy.h"
#include "third_party/blink/public/common/permissions_policy/document_policy.h"
+#include "third_party/blink/public/common/permissions_policy/permissions_policy_features.h"
#include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/common/security/address_space_feature.h"
@@ -7653,27 +7654,62 @@
return true;
}
+bool NavigationRequest::IsFencedFrameRequiredPolicyFeatureAllowed(
+ const url::Origin& origin,
+ const blink::mojom::PermissionsPolicyFeature feature) {
+ const blink::PermissionsPolicyFeatureList& feature_list =
+ blink::GetPermissionsPolicyFeatureList();
+
+ // Check if the outer document's permissions policies allow all of the
+ // required policies for `origin`.
+ if (GetParentFrameOrOuterDocument()
+ ->permissions_policy()
+ ->GetAllowlistForFeatureIfExists(feature) &&
+ !GetParentFrameOrOuterDocument()
+ ->permissions_policy()
+ ->IsFeatureEnabledForOrigin(feature, origin)) {
+ return false;
+ }
+
+ // Check if the container policies to be committed allow all of the required
+ // policies for `origin`. This means that the policy must be either
+ // explicitly enabled for `origin`, or the policy must by default
+ // be enabled for all origins. Note: because the policies have not been
+ // read into a RenderFrameHost's permissions_policy_ yet, we need to check
+ // the ParsedPermissionsPolicyDeclaration object directly.
+ auto policy_iter = std::find_if(
+ commit_params_->frame_policy.container_policy.begin(),
+ commit_params_->frame_policy.container_policy.end(),
+ [feature](const blink::ParsedPermissionsPolicyDeclaration& d) {
+ return d.feature == feature;
+ });
+ if (policy_iter == commit_params_->frame_policy.container_policy.end()) {
+ return feature_list.at(feature) ==
+ blink::PermissionsPolicyFeatureDefault::EnableForAll;
+ }
+
+ return policy_iter->Contains(origin);
+}
+
bool NavigationRequest::CheckPermissionsPoliciesForFencedFrames(
const url::Origin& origin) {
// These checks only apply to fenced frames.
if (!frame_tree_node_->IsFencedFrameRoot())
return true;
+ // Check that all of the required policies for a new document with origin
+ // `origin` in the fenced frame are allowed. This looks at the outer
+ // document's policies and the "allow" attribute. Note that the document will
+ // eventually only use the required policies without policy inheritance, so
+ // extra policies defined in the outer document/"allow" attribute won't have
+ // any effect.
for (const blink::mojom::PermissionsPolicyFeature feature :
blink::kFencedFrameOpaqueAdsDefaultAllowedFeatures) {
- // Only check if the feature is enabled for this origin if
- // a policy was explicitly specified.
- if (GetParentFrameOrOuterDocument()
- ->permissions_policy()
- ->GetAllowlistForFeatureIfExists(feature) &&
- !GetParentFrameOrOuterDocument()
- ->permissions_policy()
- ->IsFeatureEnabledForOrigin(feature, origin)) {
+ if (!IsFencedFrameRequiredPolicyFeatureAllowed(origin, feature)) {
const blink::PermissionsPolicyFeatureToNameMap& feature_to_name_map =
blink::GetPermissionsPolicyFeatureToNameMap();
- const std::string feature_string(
- (feature_to_name_map.find(feature))->second);
- AddDeferredConsoleMessage(
+ const std::string feature_string(feature_to_name_map.at(feature));
+ frame_tree_node_->current_frame_host()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kError,
base::StringPrintf(
"Refused to frame '%s' as a fenced frame because permissions "
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index d04636e..cdc2aeac 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -1500,11 +1500,17 @@
// If they aren't, this returns false and emits a crash report.
bool CoopCoepSanityCheck();
- // Checks if all of the permissions policies that a fenced frame requires to
- // be enabled for its origin are enabled. If not, it logs a console message
- // and returns false.
+ // Checks that, given an origin to be committed, all of the permissions
+ // policies that a fenced frame requires to be enabled are enabled. If not, it
+ // logs a console message and returns false.
bool CheckPermissionsPoliciesForFencedFrames(const url::Origin&);
+ // Helper function that determines if a given required permissions policy
+ // feature is properly enabled for a given origin to be committed.
+ bool IsFencedFrameRequiredPolicyFeatureAllowed(
+ const url::Origin&,
+ const blink::mojom::PermissionsPolicyFeature feature);
+
// Returns the user-agent override, or an empty string if one isn't set.
std::string GetUserAgentOverride();
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 9576c68..72b9361 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -636,8 +636,11 @@
const url::Origin& subframe_origin) {
std::unique_ptr<blink::PermissionsPolicy> subframe_policy;
if (frame->IsNestedWithinFencedFrame()) {
- // In Fenced Frames, all permission policy gated features must be disabled
- // for privacy reasons.
+ // Fenced frames have a list of required permission policies to load and
+ // can't be granted extra policies, so use the required policies instead of
+ // inheriting from its parent. Note that the parent policies must allow the
+ // required policies, which is checked separately in
+ // NavigationRequest::CheckPermissionsPoliciesForFencedFrames.
subframe_policy = blink::PermissionsPolicy::CreateForFencedFrame(
subframe_origin,
frame->frame_tree_node()->GetFencedFrameMode().value());
@@ -10195,8 +10198,11 @@
void RenderFrameHostImpl::ResetPermissionsPolicy() {
if (IsNestedWithinFencedFrame()) {
- // In Fenced Frames, all permission policy gated features must be disabled
- // for privacy reasons.
+ // Fenced frames have a list of required permission policies to load and
+ // can't be granted extra policies, so use the required policies instead of
+ // inheriting from its parent. Note that the parent policies must allow the
+ // required policies, which is checked separately in
+ // NavigationRequest::CheckPermissionsPoliciesForFencedFrames.
permissions_policy_ = blink::PermissionsPolicy::CreateForFencedFrame(
last_committed_origin_,
frame_tree_node()->GetFencedFrameMode().value());
diff --git a/third_party/blink/common/permissions_policy/permissions_policy.cc b/third_party/blink/common/permissions_policy/permissions_policy.cc
index ab61eb7f..7fb5e82 100644
--- a/third_party/blink/common/permissions_policy/permissions_policy.cc
+++ b/third_party/blink/common/permissions_policy/permissions_policy.cc
@@ -360,10 +360,6 @@
const ParsedPermissionsPolicy& container_policy,
const url::Origin& origin,
const PermissionsPolicyFeatureList& features) {
- // If there is a non-empty container policy, then there must also be a parent
- // policy.
- DCHECK(parent_policy || container_policy.empty());
-
std::unique_ptr<PermissionsPolicy> new_policy =
base::WrapUnique(new PermissionsPolicy(origin, features));
for (const auto& feature : features) {
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 3f45a3e..040b248 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -72,7 +72,6 @@
"drag/drag.mojom",
"favicon/favicon_url.mojom",
"feature_observer/feature_observer.mojom",
- "fenced_frame/fenced_frame.mojom",
"fetch/fetch_api_request.mojom",
"fetch/fetch_api_response.mojom",
"file/file_utilities.mojom",
@@ -1110,6 +1109,7 @@
"messaging/user_activation_snapshot.mojom",
# Depends on frame_policy.mojom.
+ "fenced_frame/fenced_frame.mojom",
"navigation/navigation_params.mojom",
# Depends on transferable_message.mojom.
diff --git a/third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom b/third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom
index c6be033..65e22c40 100644
--- a/third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom
+++ b/third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom
@@ -5,16 +5,9 @@
module blink.mojom;
import "mojo/public/mojom/base/time.mojom";
+import "third_party/blink/public/mojom/frame/frame_policy.mojom";
import "url/mojom/url.mojom";
-// These values represent the `mode` attribute of the fenced frame element. Keep
-// this values in sync with the conditions in `GetModeAttributeValue()` in
-// `html_fenced_frame_element.cc`.
-enum FencedFrameMode {
- kDefault = 0,
- kOpaqueAds = 1,
-};
-
// The `FencedFrameOwnerHost` interface is used by the HTMLFencedFrameElement in
// the renderer process that hosts the "outer" frame tree. It is used to
// interact with the backing `content::FencedFrame` in the browser process which
@@ -24,6 +17,11 @@
// TODO(crbug.com/1243568): Document the restrictions on the types of URLs
// that `url` can represent, once these restrictions are made.
Navigate(url.mojom.Url url, mojo_base.mojom.TimeTicks navigation_start_time);
+ // Updates the container policy on the browser side. Note: right now, this
+ // does not change the sandbox flags or the required document policy. We use
+ // this instead of RenderFrameHostImpl::DidChangeFramePolicy to avoid doing
+ // iframe-only checks that would break for fenced frames.
+ DidChangeFramePolicy(blink.mojom.FramePolicy frame_policy);
};
enum ReportingDestination {
diff --git a/third_party/blink/public/mojom/frame/frame_policy.mojom b/third_party/blink/public/mojom/frame/frame_policy.mojom
index 152d7dd..365f00c4 100644
--- a/third_party/blink/public/mojom/frame/frame_policy.mojom
+++ b/third_party/blink/public/mojom/frame/frame_policy.mojom
@@ -5,12 +5,19 @@
module blink.mojom;
import "services/network/public/mojom/web_sandbox_flags.mojom";
-import "third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom";
import "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom";
import "third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom";
import "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom";
import "third_party/blink/public/mojom/permissions_policy/policy_value.mojom";
+// These values represent the `mode` attribute of the fenced frame element. Keep
+// this values in sync with the conditions in `GetModeAttributeValue()` in
+// `html_fenced_frame_element.cc`.
+enum FencedFrameMode {
+ kDefault = 0,
+ kOpaqueAds = 1,
+};
+
// This struct holds frame policy value that needs to be passed from browser
// process to renderer process during navigation. It is used as a data
// member of CommitNavigationParams. For details, please refer to
diff --git a/third_party/blink/renderer/core/execution_context/security_context_init.cc b/third_party/blink/renderer/core/execution_context/security_context_init.cc
index 469a874..1aa18e91 100644
--- a/third_party/blink/renderer/core/execution_context/security_context_init.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context_init.cc
@@ -201,8 +201,11 @@
} else {
std::unique_ptr<PermissionsPolicy> permissions_policy;
if (frame.IsInFencedFrameTree()) {
- // In Fenced Frames, all permission policy gated features must be disabled
- // for privacy reasons.
+ // Fenced frames have a list of required permission policies to load and
+ // can't be granted extra policies, so use the required policies instead
+ // of inheriting from its parent. Note that the parent policies must allow
+ // the required policies, which is checked separately in
+ // NavigationRequest::CheckPermissionsPoliciesForFencedFrames.
permissions_policy = PermissionsPolicy::CreateForFencedFrame(
origin, frame.GetFencedFrameMode().value());
} else {
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 0194d31c..f9a7720 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -42,8 +42,8 @@
#include "third_party/blink/public/common/permissions_policy/document_policy_features.h"
#include "third_party/blink/public/common/permissions_policy/permissions_policy_features.h"
#include "third_party/blink/public/common/tokens/tokens.h"
-#include "third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/frame/frame_policy.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/frame/remote_frame.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/input/scroll_direction.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.cc b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.cc
index c6478ad..fe1bf3d 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.cc
@@ -72,4 +72,10 @@
GetElement().StopResizeObserver();
}
+void FencedFrameMPArchDelegate::DidChangeFramePolicy(
+ const FramePolicy& frame_policy) {
+ DCHECK(remote_);
+ remote_->DidChangeFramePolicy(frame_policy);
+}
+
} // namespace blink
diff --git a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.h b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.h
index 78cdf78..a535de37 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/fenced_frame_mparch_delegate.h
@@ -28,6 +28,7 @@
void AttachLayoutTree() override;
bool SupportsFocus() override;
void FreezeFrameSize() override;
+ void DidChangeFramePolicy(const FramePolicy&) override;
private:
mojo::AssociatedRemote<mojom::blink::FencedFrameOwnerHost> remote_;
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
index 359fbfd9..f587a1b 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
@@ -155,6 +155,33 @@
HTMLFrameOwnerElement::DisconnectContentFrame();
}
+ParsedPermissionsPolicy HTMLFencedFrameElement::ConstructContainerPolicy()
+ const {
+ if (!GetExecutionContext())
+ return ParsedPermissionsPolicy();
+
+ scoped_refptr<const SecurityOrigin> src_origin =
+ GetOriginForPermissionsPolicy();
+ scoped_refptr<const SecurityOrigin> self_origin =
+ GetExecutionContext()->GetSecurityOrigin();
+
+ PolicyParserMessageBuffer logger;
+
+ ParsedPermissionsPolicy container_policy =
+ PermissionsPolicyParser::ParseAttribute(allow_, self_origin, src_origin,
+ logger, GetExecutionContext());
+
+ for (const auto& message : logger.GetMessages()) {
+ GetDocument().AddConsoleMessage(
+ MakeGarbageCollected<ConsoleMessage>(
+ mojom::blink::ConsoleMessageSource::kOther, message.level,
+ message.content),
+ /* discard_duplicates */ true);
+ }
+
+ return container_policy;
+}
+
void HTMLFencedFrameElement::SetCollapsed(bool collapse) {
if (collapsed_by_client_ == collapse)
return;
@@ -170,6 +197,13 @@
style_change_reason::kFrame));
}
+void HTMLFencedFrameElement::DidChangeContainerPolicy() {
+ // Don't notify about updates if frame_delegate_ is null, for example when
+ // the delegate hasn't been created yet.
+ if (frame_delegate_)
+ frame_delegate_->DidChangeFramePolicy(GetFramePolicy());
+}
+
// START HTMLFencedFrameElement::FencedFrameDelegate.
HTMLFencedFrameElement::FencedFrameDelegate*
@@ -418,6 +452,14 @@
KURL url = GetNonEmptyURLAttribute(html_names::kSrcAttr);
Navigate(url);
+ } else if (params.name == html_names::kAllowAttr) {
+ if (allow_ != params.new_value) {
+ allow_ = params.new_value;
+ if (!params.new_value.empty()) {
+ UseCounter::Count(GetDocument(),
+ WebFeature::kFeaturePolicyAllowAttribute);
+ }
+ }
} else {
HTMLFrameOwnerElement::ParseAttribute(params);
}
@@ -503,6 +545,8 @@
return;
}
+ UpdateContainerPolicy();
+
frame_delegate_->Navigate(url);
if (!frozen_frame_size_) {
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
index 323a2e2..8182cf8 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
@@ -58,6 +58,7 @@
virtual void AttachLayoutTree() {}
virtual bool SupportsFocus() { return false; }
virtual void FreezeFrameSize() {}
+ virtual void DidChangeFramePolicy(const FramePolicy& frame_policy) {}
protected:
HTMLFencedFrameElement& GetElement() const { return *outer_element_; }
@@ -75,11 +76,9 @@
FrameOwnerElementType OwnerType() const override {
return FrameOwnerElementType::kFencedframe;
}
- ParsedPermissionsPolicy ConstructContainerPolicy() const override {
- NOTREACHED();
- return ParsedPermissionsPolicy();
- }
+ ParsedPermissionsPolicy ConstructContainerPolicy() const override;
void SetCollapsed(bool) override;
+ void DidChangeContainerPolicy() override;
// HTMLElement overrides.
bool IsHTMLFencedFrameElement() const final { return true; }
@@ -188,6 +187,8 @@
// set after being frozen. This ensures that multiple logs don't happen
// for one fenced frame if it's constantly being resized.
bool size_set_after_freeze_ = false;
+ // Attributes that are modeled off of their iframe equivalents
+ AtomicString allow_;
friend class FencedFrameMPArchDelegate;
friend class FencedFrameShadowDOMDelegate;
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.idl b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.idl
index 4b709f5..cd8a2f5 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.idl
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.idl
@@ -12,4 +12,6 @@
[CEReactions, Reflect] attribute DOMString height;
[CEReactions, Reflect, ReflectOnly=("default", "opaque-ads"), ReflectMissing="default", ReflectInvalid="default"] attribute DOMString mode;
[CallWith=ScriptState] static boolean canLoadOpaqueURL();
+ // Feature Policy
+ [CEReactions, Reflect, RuntimeEnabled=FencedFramesAPIChanges] attribute DOMString allow;
};
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 67ec75a1..51ef0db 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -403,6 +403,10 @@
void HTMLFrameOwnerElement::UpdateContainerPolicy() {
frame_policy_.container_policy = ConstructContainerPolicy();
+ DidChangeContainerPolicy();
+}
+
+void HTMLFrameOwnerElement::DidChangeContainerPolicy() {
// Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet.
if (ContentFrame()) {
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index 0dde0fd..553e842 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -168,6 +168,10 @@
// changes.
void UpdateContainerPolicy();
+ // Called when the container policy changes. Typically used to sync a
+ // container policy update to the browser process.
+ virtual void DidChangeContainerPolicy();
+
// Return a document policy required policy for this frame, based on the
// frame attributes.
virtual DocumentPolicyFeatureState ConstructRequiredPolicy() const {
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-allow.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-allow.https.html
new file mode 100644
index 0000000..c5b38b7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-allow.https.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>Test default permission policy features with allow="" attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/default-enabled-features-helper.js"></script>
+
+<body>
+<script>
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/true,
+ get_host_info().ORIGIN,
+ allow="shared-storage *; attribution-reporting *");
+}, 'Same-origin fenced frame with allow attribute enabling required features');
+
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/true,
+ get_host_info().REMOTE_ORIGIN,
+ allow="shared-storage *; attribution-reporting *");
+}, 'Cross-origin fenced frame with allow attribute enabling required features');
+
+promise_test(async(t) => {
+ const fencedframe = attachFencedFrameContext({
+ attributes: [["mode", "opaque-ads"]],
+ headers: [["Permissions-Policy", "attribution-reporting=()"]],
+ origin: get_host_info().ORIGIN});
+
+ await fencedframe.execute(async () => {
+ assert_false(document.featurePolicy.allowsFeature('attribution-reporting'),
+ "Attribution reporting should NOT be allowed in the fenced frame.");
+ }, []);
+}, 'Delivered policies can further restrict permissions of a fenced frame');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-change.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-change.https.html
new file mode 100644
index 0000000..c5beea6
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-change.https.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>Test changing the allow="" attribute after a navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/default-enabled-features-helper.js"></script>
+
+<body>
+<script>
+promise_test(async(t) => {
+ const fencedframe = attachFencedFrameContext({
+ attributes: [["mode", "opaque-ads"],
+ ["allow", "shared-storage *; attribution-reporting *"]],
+ origin: get_host_info().ORIGIN});
+
+ fencedframe.element.allow = "attribution-reporting 'none'";
+
+ await fencedframe.execute(async () => {
+ assert_true(document.featurePolicy.allowsFeature('attribution-reporting'),
+ "Changing the allow attribute should do nothing for this navigation.");
+ }, []);
+}, 'Changing the allow attribute is a no-op for the current navigation');
+
+promise_test(async(t) => {
+ const fencedframe = attachFencedFrameContext({
+ attributes: [["mode", "opaque-ads"],
+ ["allow", "shared-storage *; attribution-reporting *"]],
+ origin: get_host_info().ORIGIN});
+
+ fencedframe.element.allow = "attribution-reporting 'none'";
+
+ await fencedframe.execute(async () => {
+ location.reload();
+ }, []);
+
+ await fencedframe.execute(async () => {
+ assert_true(document.featurePolicy.allowsFeature('attribution-reporting'),
+ "Changing the allow attribute should do nothing on frame refresh.");
+ }, []);
+
+}, 'Changing the allow attribute is a no-op for frame-initiated navigations');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-disallow.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-disallow.https.html
new file mode 100644
index 0000000..29fdc78
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribute-disallow.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Test default permission policy features with allow="" attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/default-enabled-features-helper.js"></script>
+
+<body>
+<script>
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/false,
+ get_host_info().ORIGIN, allow="shared-storage 'none'");
+}, 'Same-origin fenced frame with allow attribute disabling required feature');
+
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/false,
+ get_host_info().REMOTE_ORIGIN, allow="shared-storage 'none'");
+}, 'Cross-origin fenced frame with allow attribute disabling required feature');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html
new file mode 100644
index 0000000..7683b35
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Test where attribution-reporting is disabled in the top-level page</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/default-enabled-features-helper.js"></script>
+
+<body>
+<script>
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/false,
+ get_host_info().ORIGIN,
+ allow="attribution-reporting *; shared-storage *;");
+}, 'Same-origin fenced frame with allow attribute enabling required feature ' +
+ 'but page disabling feature.');
+
+promise_test(async(t) => {
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/false,
+ get_host_info().REMOTE_ORIGIN,
+ allow="attribution-reporting *; shared-storage *;");
+}, 'Cross-origin fenced frame with allow attribute enabling required feature ' +
+ 'but page disabling feature.');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html.headers b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html.headers
new file mode 100644
index 0000000..af6d6ec
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-attribution-disabled.https.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: attribution-reporting=()
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-unset.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-unset.https.html
index e9c0d9e5..f50cf2ab 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-unset.https.html
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/default-enabled-features-unset.https.html
@@ -11,11 +11,13 @@
<body>
<script>
promise_test(async(t) => {
- await runDefaultEnabledFeaturesTest(t, true, get_host_info().ORIGIN);
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/true,
+ get_host_info().ORIGIN);
}, 'Same-origin fenced frame loads when feature policies are unset');
promise_test(async(t) => {
- await runDefaultEnabledFeaturesTest(t, true, get_host_info().REMOTE_ORIGIN);
+ await runDefaultEnabledFeaturesTest(t, /*should_load=*/true,
+ get_host_info().REMOTE_ORIGIN);
}, 'Cross-origin fenced frame loads when feature policies are unset');
</script>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/default-enabled-features-helper.js b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/default-enabled-features-helper.js
index 9d98fcd..a56a367 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/default-enabled-features-helper.js
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/default-enabled-features-helper.js
@@ -7,18 +7,18 @@
// <script src="resources/utils.js"></script>
// <script src="/common/get-host-info.sub.js"></script>
-async function runDefaultEnabledFeaturesTest(t, should_load, fenced_origin) {
+async function runDefaultEnabledFeaturesTest(t, should_load, fenced_origin, allow="") {
const fencedframe = attachFencedFrameContext({
- attributes: [["mode", "opaque-ads"]],
+ attributes: [["mode", "opaque-ads"], ["allow", allow]],
origin: fenced_origin});
if (!should_load) {
const fencedframe_blocked = new Promise(r => t.step_timeout(r, 1000));
const fencedframe_loaded = fencedframe.execute(() => {});
- assert_equals("blocked", await Promise.any([
+ assert_equals(await Promise.any([
fencedframe_blocked.then(() => "blocked"),
fencedframe_loaded.then(() => "loaded"),
- ]), "The fenced frame should not be loaded.");
+ ]), "blocked", "The fenced frame should not be loaded.");
return;
}