diff --git a/spec.bs b/spec.bs
index 7279c189..4d8a99a8 100644
--- a/spec.bs
+++ b/spec.bs
@@ -80,9 +80,11 @@ spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
text: historyHandling; url: navigation-hh
text: referrerPolicy; url: navigation-referrer-policy
text: attempt to populate the history entry's document; url: attempt-to-populate-the-history-entry's-document
+ text: navigation params; url: navigation-params
for: navigation params
text: response; url: navigation-params-response
text: navigable; url: navigation-params-navigable
+ text: origin; url: navigation-params-origin
for: history handling behavior
text: replace; url: hh-replace
for: document state
@@ -109,6 +111,21 @@ spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
urlPrefix: nav-history-apis.html
for: Window
text: navigable; url: window-navigable
+ urlPrefix: webappapis.html
+ for: environment
+ text: target browsing context; url: concept-environment-target-browsing-context
+ urlPrefix: document-sequences.html
+ for: browsing context
+ text: active document; url: active-document
+spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/
+ type: dfn
+ text: queue a cross-origin embedder policy CORP violation report; url: queue-a-cross-origin-embedder-policy-corp-violation-report
+ text: should request be blocked due to a bad port; url: block-bad-port
+spec: mixed-content; urlPrefix: https://w3c.github.io/webappsec-mixed-content/
+ type: dfn
+ text: should fetching request be blocked as mixed content; url: should-block-fetch
+spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/
+ type: dfn
urlPrefix: interactive-elements.html
text: accesskey attribute command; url: using-the-accesskey-attribute-to-define-a-command-on-other-elements
text: previously focused element; url: previously-focused-element
@@ -125,6 +142,16 @@ spec: RFC8941; urlPrefix: https://www.rfc-editor.org/rfc/rfc8941.html
text: structured header; url: #section-1
for: structured header
text: token; url: name-tokens
+spec: permissions-policy; urlPrefix: https://w3c.github.io/webappsec-permissions-policy
+ type: dfn
+ text: ASCII-serialized policy directive; url: serialized-policy-directive
+ text: serialized permissions policy; url: serialized-permissions-policy
+ text: supported features; url: supported-features
+ text: declared policy; url: declared-policy
+ text: the special value *; url: the-special-value
+ text: permissions policy; url: permissions-policy
+ for: permissions
+ text: matches; url: matches
spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/
type: dfn
text: directive value; url: directive-value
@@ -258,6 +285,7 @@ dl, dd {
[=Global attributes=]
[=width=]
— Horizontal dimension
[=height=]
— Vertical dimension
+ <{fencedframe/allow}>
— [=Permissions policy=] to be applied to the <{fencedframe}>'s contents
[=Accessibility considerations=]:
TODO
[=DOM interface=]:
@@ -270,6 +298,7 @@ interface HTMLFencedFrameElement : HTMLElement {
[CEReactions] attribute FencedFrameConfig? config;
[CEReactions] attribute DOMString width;
[CEReactions] attribute DOMString height;
+ [CEReactions] attribute DOMString allow;
};
@@ -344,6 +373,14 @@ The config IDL attribute getter
1. TODO
+The allow attribute, when specified, determines the
+[=container policy=] that will be used when the [=Document/permissions policy=] for a {{Document}}
+in the <{fencedframe}>'s [=fenced navigable container/fenced navigable=] is initialized. Its value
+must be a [=serialized permissions policy=]. [[!PERMISSIONS-POLICY]]
+
+The IDL attribute allow must [=reflect=] the
+respective content attribute of the same name.
+
Dimension attributes
This section details monkeypatches to [[!HTML]]'s creator<
document's referrer|referrer=], [=Document/origin=], [=creator base url=], [=Document/policy
container=], across the fenced frame boundary.
-Issue: Ensure we are doing the right thing for [=Document/permissions policy=].
-
Nested traversables
Introduction
@@ -1731,3 +1766,239 @@ specification is printed below:
/fenced-frame/cspee.https.html
/fenced-frame/embedder-csp-not-propagate.https.html
+
+Permissions Policies
+
+*This introductory sub-section is non-normative.*
+
+The [=policy-controlled features=] available to {{Document}}s inside of a <{fencedframe}> are
+determined exclusively by the {{FencedFrameConfig}} that the <{fencedframe}> navigates to.
+Specifically, the {{FencedFrameConfig}}'s [=fencedframeconfig/config=]'s [=fenced frame
+config/effective enabled permissions=] defines the exclusive list of [=policy-controlled features=]
+that will be enabled in the {{Document}} (all others will be disabled).
+
+During navigation, the {{FencedFrameConfig}}'s [=fencedframeconfig/config=] [=instantiate a
+config|instantiates=] a [=navigable/fenced frame config instance=] that is stored on the [=fenced
+navigable container/fenced navigable=]. This navigable's [=navigable/fenced frame config
+instance=]'s [=fenced frame config instance/effective enabled permissions=] is consulted [=Should
+navigation response to navigation request be blocked by Permissions Policy?|during navigation=]. A
+<{fencedframe}> navigation can only succeed if the [=Document/permissions policy=] for the
+navigation's resulting {{Document}} has an [=permissions policy/inherited policy=] such that the
+[=inherited policy for a feature|inherited policy value=] is "`Enabled`" for each feature in the
+[=fenced frame config/effective enabled permissions=]. Otherwise the environment the <{fencedframe}>
+is embedded in is deemed unsuitable for the [=fenced frame config=], and the navigation is blocked.
+
+At the same time, to make sure that a <{fencedframe}>'s embedder does not directly influence content
+in the frame based on that navigation's [=navigation params/origin=] (since the origin is derived
+from cross-site data), this specification modifies various [[PERMISSIONS-POLICY]] algorithms such
+that a <{fencedframe}> {{Document}}'s [=permissions policy/inherited policy=] is computed without
+consideration of whether its [=navigation params/origin=] is [=same origin=] with its embedder's.
+Therefore a feature can only be enabled inside of a <{fencedframe}> if its embedder *explicitly*
+delegates it via [=the special value *=] [=allowlist=].
+
+Considering all of the above, we get the following interesting implications:
+
+ * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame
+ config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of
+ [=the special value *=], and no \``Permissions-Policy`\` header is served on
+ the <{fencedframe}> embedder, and the <{fencedframe/allow}> attribute is empty, the navigation
+ inside the <{fencedframe}> will succeed, and the resulting {{Document}} will be [=allowed to
+ use=] the [=policy-controlled feature|feature=] (i.e., it will be enabled).
+
+ * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame
+ config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of
+ `'self'`, and no \``Permissions-Policy`\`
+ header is served on the <{fencedframe}> embedder, and the <{fencedframe/allow}> attribute is
+ empty, the navigation inside the <{fencedframe}> will be blocked.
+
+ Note: This is because ordinarily this [=policy-controlled feature|feature=] would only be enabled
+ if the subframe's {{Document}} was [=same origin=] with its embedder, a check this specification
+ avoids for fenced frames, since the <{fencedframe}>s {{Document}}'s [=Document/origin=] is
+ derived from cross-site data. Therefore, we simply "fail close".
+
+ * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame
+ config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of
+ `'self'`, and the <{fencedframe}>'s <{fencedframe/allow}>
+ attribute contains the [=policy-controlled feature|feature=] but no [=allowlist=], the rules
+ described in The `allow` attribute section, the default
+ [=allowlist=] for the feature will be `'src'` which is meant to represent the embedder-supplied
+ navigation [=URL=], for which there is none when navigating a <{fencedframe}>, as the navigation
+ [=URL=] is determined by the [=fenced frame config=]. The navigation will be blocked.
+
+ * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame
+ config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of
+ `'self'` but either the \``Permissions-Policy`\` header served on the <{fencedframe}>'s embedder *OR* the
+ <{fencedframe/allow}> attribute sets that [=policy-controlled feature|feature=]'s [=allowlist=]
+ to [=the special value *=], then the navigation inside the <{fencedframe}> will succeed, and the
+ resulting {{Document}} is [=allowed to use=] the [=policy-controlled feature|feature=].
+
+ * If a navigation inside a <{fencedframe}> would otherwise succeed, but the [=response=] on the
+ navigation inside the <{fencedframe}> is served with a \``Permissions-Policy`\` header that sets the [=allowlist=] to "`none`" or an
+ otherwise incompatible [=origin=] for a feature in the [=fenced frame config/effective enabled
+ permissions=], the navigation still succeeds, but the {{Document}} in the <{fencedframe}> is
+ **not** [=allowed to use=] the feature.
+
+ Note: This is OK because it is the <{fencedframe}>'s content *itself* that is making the decision
+ to disable a particular feature, not its embedder environment.
+
+The patches in the below section "fence" the appropriate [[PERMISSIONS-POLICY]] algorithms to
+achieve the outcomes described in the above explanatory content.
+
+Algorithm patches
+
+
+ Rename the
The
+ `allow` attribute of the `iframe` element section to "The `allow` attribute of the `iframe`
+ and `fencedframe` element", and rewrite the section to read:
+
+ <{iframe}> and <{fencedframe}> elements have an respective `allow` attributes (<{iframe}>:
+ <{iframe/allow}>; <{fencedframe}>: <{fencedframe/allow}>), which contain an [=ASCII-serialized
+ policy directive=].
+
+ The allowlist for the features named in the attribute may be empty; in that case, the default
+ value for the [=allowlist=] is "`src`", which represents the origin of the URL in the iframe's
+ <{iframe/src}> attribute, or the fencedframe's [=fenced frame config=].
+
+ When not empty, the <{iframe}>'s <{iframe/allow}> or <{fencedframe}>'s <{fencedframe/allow}>
+ attribute will result in adding an [=allowlist=] for each [=supported feature=] to the <{iframe}>
+ or <{fencedframe}>'s [=container policy=].
+
+
+
+ Modify the [$Create a Permissions Policy for a navigable$] algorithm:
+
+ Given null or an element (|container|), an [=origin=] (|origin|), and an optional [=boolean=]
+ |fenced| that defaults to false, this algorithm returns a new Permissions Policy.
+
+ Rewrite step 1 to read:
+
+ 1. [=Assert=]: if not null, container is either a [=navigable container=] or a
+ [=fenced navigable container=].
+
+ Rewrite step 4 to read:
+
+ 4. For each |feature| supported:
+
+ 1. Let |isInherited| be the result of running [$Define an inherited policy for feature in
+ container at origin$] on |feature|, |container|, |origin|, and |fenced|.
+
+ 2. Set inherited policy[|feature|] to |isInherited|.
+
+
+
+ Modify the [$Create a Permissions Policy for a navigable from response$] algorithm to read:
+
+ Given null, a [=navigable container=], or a [=fenced navigable container=] (|container|), an
+ [=origin=] (|origin|), and a [=response=] (response), this algorithm returns a
+ new [=Document/permissions policy=].
+
+ Add a step before step 1 that reads:
+
+ 1. Let |fenced| be true if |container| is a [=fenced navigable container=], and false otherwise.
+
+ Modify what is *now* step 2 of the algorithm to read:
+
+ 2. Let policy be the result of running [$Create a Permissions Policy
+ for a navigable$] given |container|, |origin|, and |fenced|.
+
+
+
+ Modify [[HTML]]'s [=attempt to populate the history entry's document=] algorithm. Add a step
+ before the step inside the [=queue a task|queued task=] starting with "If
+ |failure| is true, then:" that reads:
+
+ 8. Otherwise, if the result of [=Should navigation response to navigation request be blocked by
+ Permissions Policy?=] given navigationParams is "`Blocked`", then set
+ |failure| to true.
+
+ Note: If this algorithm returns "`Blocked`", the pre-existing {{Document}} in the <{fencedframe}>
+ does not stick around; an error page will be loaded.
+
+
+
+ Create a new algorithm called Should navigation response to navigation request be blocked by
+ Permissions Policy? in [[!HTML]].
+
+ Given a [=navigation params=] (|navigationParams|), this algorithm returns "`Blocked`" or
+ "`Allowed`":
+
+ 1. Let |navigable| be |navigationParams|'s [=navigation params/navigable=].
+
+ 1. If |navigable| is not a [=fenced navigable container/fenced navigable=], then return
+ "`Allowed`".
+
+ 1. Let |origin| be |navigationParams|'s [=navigation params/origin=].
+
+ 1. Let |effective permissions| be the |navigable|'s [=navigable/fenced frame config instance=]'s
+ [=fenced frame config instance/effective enabled permissions=].
+
+ 1. Let |permissions policy| be the result of [$Create a Permissions Policy for a navigable|
+ creating a permissions policy$] given |navigable|'s [=fenced navigable container=], |origin|,
+ and fenced set to true.
+
+ Note: This is identical to the [=Document/permissions policy=] that will be created when the
+ navigation constructs the ultimate {{Document}} for this pending navigation. We create it now
+ and run tests on it since this is the appropriate time to determine if a navigation will fail,
+ and then throw it away. If the navigation succeeds, it will be recreated identically and
+ unconditionally installed on the {{Document}}.
+
+ 1. Let |inherited policy| be |permissions policy|'s [=permissions policy/inherited policy=].
+
+ 1. [=list/For each=] |effective permission| of |effective permissions|:
+
+ 1. If |inherited policy|[|effective permission|] is "Disabled", return "`Blocked`".
+
+ 1. Return "`Allowed`."
+
+
+
+ Modify the [$Define an inherited policy for feature in container at origin$] algorithm to
+ read:
+
+ Given a feature (|feature|), null or a [=navigable container=] (|container|), an [=origin=] for a
+ document in that container (|origin|), and an optional [=boolean=] |fenced| that defaults to
+ false, this algorithm returns the [=permissions policy/inherited policy=] for that feature.
+
+ Rewrite step 3 to read:
+
+ 3. If the result of executing [$Is feature enabled in document for origin?$] on |feature|,
+ |container|'s [=Node/node document=], |origin|, and |fenced| is "Disabled", return
+ "Disabled".
+
+ Note: We don't have to rewrite step 2, which also delegates to the same algorithm, to pass in the
+ |fenced| [=boolean=] because step 2 has to do with checking to see if |feature| is enabled
+ |container|'s [=Node/node document=], not the {{Document}} hosted *inside* |container|.
+
+ Rewrite step 7 to read:
+
+ 7. If |fenced| is false, |feature|'s [=policy-controlled feature/default allowlist=] is
+ `'self'`, and |origin| is [=same origin=] with |container|'s [=Node/node document=]'s
+ origin, return `"Enabled"`.
+
+
+
+ Modify the [$Is feature enabled in document for origin?$] algorithm to read:
+
+ Given a feature (|feature|), a {{Document}} object (|document|), an [=url/origin=] (|origin|), and
+ an optional [=boolean=] |fenced| that defaults to false, this algorithm returns "`Disabled`" if
+ |feature| should be considered disabled, and "`Enabled`" otherwise.
+
+ Rewrite step 3 to read:
+
+ 3. If |feature| is present in |policy|'s [=declared policy=],
+
+ 1. If |fenced| is set to false, and the [=allowlist=] for |feature| in |policy|'s [=declared
+ policy=] [=permissions/matches=] |origin|, then return "`Enabled`".
+
+ 2. Otherwise, if |fenced| is set to true, and the [=allowlist=] for |feature| in |policy|'s
+ [=declared policy=] is [=the special value *=], then return "`Enabled`".
+
+ 3. Otherwise, return "`Disabled`".
+
+ Rewrite step 5 to read:
+
+ 5. If |fenced| is set to false, |feature|'s [=policy-controlled feature/default allowlist=] is
+ `'self'`, and |origin| is [=same origin=] with |document|'s origin, return "Enabled".
+