Skip to content

Conversation

@bban160
Copy link
Contributor

@bban160 bban160 commented Dec 26, 2025

Description + Motivation

Regarding the Permissions-Policy: fullscreen directive page:

  • The "General example" is incomplete. Cross-origin iframes must have allow="fullscreen" in addition to their origin being allowlisted in the Permissions-Policy HTTP response header.

  • The "With an <iframe> element" example is invalid. The Permissions-Policy: fullscreen=(self) HTTP response header prevents other origins from using fullscreen. The iframe allow attribute cannot grant fullscreen access because that would violate the most restrictive subset inheritance strategy.

After correcting the above, it seemed appropriate to recategorize which one is the "general example" (more widely applicable) and which is the secondary example (more niche).

Additional details

I tested both Chromium and Firefox to confirm the updated examples are correct (aside from Firefox ignoring the Permissions-Policy header due to lack of support).

@bban160 bban160 requested a review from a team as a code owner December 26, 2025 08:37
@bban160 bban160 requested review from hamishwillee and removed request for a team December 26, 2025 08:37
@github-actions github-actions bot added Content:HTTP HTTP docs size/s [PR only] 6-50 LoC changed labels Dec 26, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 29, 2025

@hamishwillee
Copy link
Collaborator

@bban160 You're right in all your main points:

  • You need to set the allowlist on a cross origin frame to allow it to use the feature, whether or not you have sent the header
  • If the header is not sent the default allowlist is *, which means that you can just set the allow list on the frame if the header is not sent.

Two points thought:

  • This document is about the header, and it grates to see the "general example" that does not show the use of the header.
  • I'm not sure that this captures both those points above.

So a suggestion.

  • Make your heading "With a Permissions-Policy header" into "Basic usage" - that explains the main use.
  • Move "general example" below the first example and rename to something like "Cross-origin access without the header" and then explain that second point above.

Does that make sense?

@bban160
Copy link
Contributor Author

bban160 commented Dec 29, 2025

If the header is not sent the default allowlist is *, which means that you can just set the allow list on the frame if the header is not sent.

This is not actually correct. I was initially confused about how Permissions Policy propagates as well, but I now believe I understand it correctly after reading numerous specs over the past week and testing in Chromium and Firefox:

  • If the default allowlist is *, cross-origin frames automatically have access to the feature (unless it is explicitly restricted by the header or allow attribute).
  • If the default allowlist is self, cross-origin frames must be explicitly opted in to use the feature by the allow attribute.
    • If a Permissions-Policy header sent with the top-level document includes the feature in question, the allowlist in that header must also permit the separate origin to use the feature.

This is very unintuitive, and I do not believe it is documented in plain English on MDN or any other site at the moment. I would like to contribute this to MDN in the future, though I do not mind if someone else gets to it first.

That tangent aside, the part that relates to this PR is that fullscreen has a default allowlist of self, not *.

This document is about the header, and it grates to see the "general example" that does not show the use of the header.

You are right. I should have looked more closely at the framing of the article.

This PR is not the proper venue for this discussion, but I believe the ideal solution would be to rewrite most of the pages concerning Permissions Policy to better reflect the actual standard. Which is to say, the HTTP response header and iframe allow attribute are both equally relevant and equally important. The pages documenting individual directives like fullscreen should discuss both ways of controlling Permissions Policy without prioritizing one over the other. The browser compatibility data should also reflect that Permissions Policy directives are supported in Firefox and Safari in the iframe allow attribute, even though the HTTP header is only supported on Chromium.


With all that in mind, would you like me to still make the changes you suggested?

My main goal with this PR is to be a (relatively) simple fix of the incorrect/misleading information. I noticed a few other pages have the exact same issue and plan to use this PR as a template to fix those, but not until receiving feedback on this first one.

The broader overhaul is something I would like to do eventually but do not have the bandwidth to take on right now. It will also take time to complete and probably deserves more discussion of its own.

@hamishwillee
Copy link
Collaborator

hamishwillee commented Dec 30, 2025

Arrggg. After reading the spec yesterday I had come to the conclusion there was a different default for if the header wasn't specified (at all) vs the usual default allowlist that would apply if the header was sent but the specific feature was not set. Reading today I have no idea how I came to that conclusion.

I think your summary matches the spec here: https://w3c.github.io/webappsec-permissions-policy/#default-allowlists - putting it down below to summarise my thoughts:

  • * allows the feature in all origins for a document and for any nested contexts.
  • self allows the feature in the document and same-origin nested contexts, but not cross origin contexts.
  • You can set the container policy using allow, but only to grant access to a feature that is already allowed by the parent node
  • What this means is that
    • if the default for a feature is * and no header is set you can always just use the feature in an iframe. You can also make the access more specific, for example setting allow = "fullscreen exmaple.com".
    • if the default is self and no header is set then for a cross-origin frame you can't add an allow to grant the feature to the frame.
    • if the header has been set, then that defines what the parent node can access. My understanding is that allow can only grant nested contexts the same features the parent has - so if the parent featre was restricted to access to example.com, then you couldn't use allow to grant the feature to a nested frame that wasn't example.com.
  • For fullscreen the default is self. That means you must have the header set to allow a cross origin iframe to be granted fullscreen.
  1. So the allow is not equally important, because it can only permit access to a feature already granted to the parent.
  2. The iframe example we have now should not work. Because the default is self, without the header you can't grant the crsoss origin frame access to the feature.
  3. So I think we should omit that, and just have the "Basic usage" example, with an explanation.

Hope that makes sense, and I haven't confused both of us again.

@bban160
Copy link
Contributor Author

bban160 commented Dec 30, 2025

if the default is self and no header is set then for a cross-origin frame you can't add an allow to grant the feature to the frame.

This is not correct. If the default is self and no header is set, then the feature can be enabled in the cross-origin frame if specified in the iframe allow attribute. In other words, the feature is already available in the top-level document, which is allowed to "delegate" access to the nested document.

For fullscreen the default is self. That means you must have the header set to allow a cross origin iframe to be granted fullscreen.

See above. If it worked this way, it would be an untenable situation in terms of website breakage. Before Permissions Policy existed, you only needed the allowfullscreen attribute on an iframe. Now, you can use allowfullscreen or allow="fullscreen *" or allow="fullscreen" (and allowfullscreen has been redefined as allow="fullscreen *" as per my other PR). No Permissions-Policy header is needed to enable this behavior. Also note that Firefox and Safari do not support the header, but they do support allow the same way as Chromium behaves when no header is sent.

Hope that makes sense, and I haven't confused both of us again.

No worries, I fully sympathize with the unintuitiveness. If it helps, you can run document.featurePolicy.allowsFeature("fullscreen") (returns a boolean) in both the top-level and iframe contexts to experiment (Firefox requires dom.security.featurePolicy.webidl.enabled to expose this interface).

@hamishwillee
Copy link
Collaborator

hamishwillee commented Jan 4, 2026

Thanks very much @bban160

I'd been misreading the spec to mean that the parent must allow the feature for the origin of the child in order for the feature to be delegated. Once I understand that the parent can delgate the feature if it has it itself, this does indeed make sense - also hanks for the test method, which I used to verify all this.

But I am still confused. Looking at the example as it is now, we have:

Permissions-Policy: fullscreen=(self "https://example.com")

From our conversation above, if sefl is specified you should not need to specify "https://example.com" to delegate the feature to a child with "https://example.com" - because the parent has the feature.
Further, if self (or * is not specified you can't delegate to https://example.com even same-origin because the parent does not have the feature.

Also in the this case you still need to specify allow="fullscreen" to delegate the feature, so it isn't some matter of allowing delegation automatically.

In other words, what's the point of specifying anything other than * or self on the parent?

@hamishwillee
Copy link
Collaborator

hamishwillee commented Jan 5, 2026

@miketaylr You might want to be involved in this discussion. The assertion here is that:

  • parent must have a feature in order to delegate it to a nested context such as an iframe.
  • When using default allow lists (i.e. no header or feature not specified).
    • If the parent has * for the feature then the feature is available in all nested contexts by default.
    • If the parent has self for a feature then it is available in same-origin contexts by default but is only allowed in cross-origin contexts if explicitly delegated (such as using allow="featurename")
  • When you explicitly specify a feature using the header, the behaviour is slightly different
    • Specifically a policy of self only allows the feature for the parent and same-origin resources. For cross-origin resources the policy must include the origin of the cross origin resources to be allowed AND the allow attribute must still be specified.
    • * behaves the same way as previously.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

I will circle back to this PR after finding a better way to illustrate the rules (probably a flowchart). Other MDN readers will benefit from this as well.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

The purpose of specifying origins in the header isn't clear to me though (other than the parent origin)- because if the parent doesn't have the feature it can't delegate. So you can't just specify other origins you want to use the feature with, because the parent won't have the feature and the child can't get it. But if the parent does have the feature via self then what's th point of listing another origin?

So, if you have a header fullscreen=(self), that prevents other origins from using fullscreen. It cannot be delegated.

If you have no header at all, fullscreen can be delegated to frames from any origin.

If you have a header fullscreen=(self "https://example.com"), fullscreen can be delegated to frames from https://example.com but not to any other cross-origin frames.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

In other words, what's the point of specifying anything other than * or self on the parent?

My usecase for this is probably quite underrepresented on MDN. I host a lot of software, mostly web apps, written by other people. It's very easy for me to control HTTP headers in my reverse proxy, but modifying the HTML itself can be a significant maintenance burden. So it is important in my usecase to implement security hardening via headers, while modifying HTML is only done out of necessity.

You can see specific examples of my methodology on my website (sorry for the shameless plug, but it really is relevant here). A notable example would be autofill, where I do not want to risk user PII being automatically delivered to a third-party frame without my explicit approval (remember: the HTML may not be written by me). I also use Content-Security-Policy to limit third-party frames, but that is beyond the scope of this discussion.

@hamishwillee
Copy link
Collaborator

@bban160 Ah, my test case on this was broken.

  1. Anyway, yes, we do need to update the main docs, and if you want to take a shot at that, it would be very welcome.

  2. For this example,

    • I still think first having heading "Basic usage" which uses your "With a Permissions-Policy header" example would be best.
    • For the second example I think we should consider calling it something like "Using default permissions", and again this can be mostly the content in the current "General example".

We do probably need a few more words of explanation in each case - i.e. in the second case, note that self is the default, and so the feature is granted automatically to same origin but needs to be explicitly delegated for cross origin frames. Also note that this differs from when explicitly setting the policy for a a feature, where you must specify the origins.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

@hamishwillee Sure, I think we are on the same page now. I will finish this PR and some other small ones first, then tackle the main docs which will be a much larger PR.

Since a few other pages are in the exact same situation as this page and need the exact same changes, should I integrate them into this PR? Or do a separate PR for each one?

@hamishwillee
Copy link
Collaborator

hamishwillee commented Jan 5, 2026

Since a few other pages are in the exact same situation as this page and need the exact same changes, should I integrate them into this PR? Or do a separate PR for each one?

After you've done the main docs you may wish to cross link here, or reduce what you say here because it is all said elsewhere. For that reason I'd be tempted to do this first, then the main docs, then another new PR which does all the rest.

That's just a suggestion though. If you want to do all of them now in one PR I won't be complaining - but it is likely you won't get this all reviewed for a bit in that case, as I'm on holiday for 2 weeks from Friday.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

Yes, the plan is to modify quite a few pages alongside the main docs update to reduce redundancy.

Since we are on the same page now with this PR, I'll go ahead and integrate the remaining pages with the same changeset (they are so identical that reviewing one means all of them have been reviewed).

@hamishwillee
Copy link
Collaborator

Since we are on the same page now with this PR, I'll go ahead and integrate the remaining pages with the same changeset (they are so identical that reviewing one means all of them have been reviewed).

Well yes, but more churn since they will all have to be reviewed twice if there is modification after the main docs are updated.

@bban160
Copy link
Contributor Author

bban160 commented Jan 5, 2026

I understand it will be more churn, but revising the main docs will take a lot longer (I have some unanswered questions I still need to research myself).

Right now I am more concerned with fixing the incorrect and doing so quickly. The larger changes will follow once I finish researching and think about the best way to structure the docs.

It's not ideal, but that's the best I can offer right now.

@bban160 bban160 force-pushed the fix-fullscreen-examples branch 3 times, most recently from fc2b9ae to 97939d4 Compare January 8, 2026 00:53
@github-actions github-actions bot added size/m [PR only] 51-500 LoC changed and removed size/s [PR only] 6-50 LoC changed labels Jan 8, 2026
@bban160 bban160 changed the title Fix incorrect examples of Permissions-Policy: fullscreen Update and clarify Permissions-Policy usage Jan 8, 2026
@bban160
Copy link
Contributor Author

bban160 commented Jan 8, 2026

@hamishwillee Ready for review.

bluetooth, fullscreen, and geolocation are updated as we discussed.

For compute-pressure, it's just a fix of the header syntax. It could have been a separate PR, but it's a tiny change and fits under this PR anyway.

@hamishwillee
Copy link
Collaborator

@chrisdavidmills I'm on holiday for two weeks. If you happen to have bandwidth for this then please do review. Otherwise I'll look when I return. @bban160 has a better understanding of this than me, though finally I do get it.

Copy link
Contributor

@chrisdavidmills chrisdavidmills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there @bban160. I've stepped in as reviewer on this one, given that @hamishwillee is on vacation for a couple of weeks (lucky man!). This is looking pretty good, but I did have a few comments, which apply to all of the three main pages you've updated, given that the example is the same in each case.

Let me know what you think.

@bban160
Copy link
Contributor Author

bban160 commented Jan 13, 2026

@chrisdavidmills Should I split out the compute-pressure change to its own PR so it does not get held up by this one? It seems like we have a few more rounds of review before the rest of this PR is finalized.

@chrisdavidmills
Copy link
Contributor

@chrisdavidmills Should I split out the compute-pressure change to its own PR so it does not get held up by this one? It seems like we have a few more rounds of review before the rest of this PR is finalized.

You can do it if you want, but I don't mind either way. I don't think it will take us more than a few days to resolve this review.

@bban160
Copy link
Contributor Author

bban160 commented Jan 16, 2026

@chrisdavidmills The compute-pressure change now lives in #42793 since I haven't had time to work on this yet. That PR however got assigned to hamishwillee, so I guess it might not be merged any sooner unless you want to take over that PR too.

@chrisdavidmills
Copy link
Contributor

@chrisdavidmills The compute-pressure change now lives in #42793 since I haven't had time to work on this yet. That PR however got assigned to hamishwillee, so I guess it might not be merged any sooner unless you want to take over that PR too.

I've taken it over ;-)

@bban160 bban160 force-pushed the fix-fullscreen-examples branch from 99cd516 to 7b36211 Compare January 26, 2026 03:27
@hamishwillee
Copy link
Collaborator

I've taken it over ;-)

Thanks very much! Looks like you two are having fun :-)

Copy link
Contributor

@chrisdavidmills chrisdavidmills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, @bban160! This is really close now; I've just got a couple of minor language suggestions for you to look at (the same two suggestions on each page).

@bban160 bban160 force-pushed the fix-fullscreen-examples branch from 7b36211 to d393519 Compare January 26, 2026 20:45
Copy link
Contributor

@chrisdavidmills chrisdavidmills left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, let's get this merged. Thanks again, @bban160, for all your work on this.

@chrisdavidmills chrisdavidmills merged commit 63c77c0 into mdn:main Jan 27, 2026
8 checks passed
@bban160 bban160 deleted the fix-fullscreen-examples branch January 27, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:HTTP HTTP docs size/m [PR only] 51-500 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants