v151 · Permissions Policy · Security

Production Recipe

Three complete app flows — chat embed, media player, and widget host — showing where to allow focus-without-user-activation in iframes, where to deny it, and why. Each recipe includes HTTP headers, a meta-equivalent, the iframe allow attribute, live detection code, and a try-it sandbox.

Scenario — Chat embed in a host page
Goal: A third-party chat widget embedded in an iframe should NOT be able to steal focus from the host page. The chat can receive focus when the user clicks it, but must not call focus() autonomously.
/* HTTP header — on the HOST page's response */ Permissions-Policy: focus-without-user-activation=() // Empty list = deny all, including same-origin frames /* Or, allow same-origin but deny the chat iframe */ Permissions-Policy: focus-without-user-activation=(self) /* HTML — the chat iframe gets no focus-without-user-activation */ <iframe src="https://chat.example.com/widget" allow="microphone; camera" /* note: no focus-without-user-activation in allow list */ ></iframe>
Live detection — host page context
Implementation steps
  1. Add Permissions-Policy: focus-without-user-activation=(self) to the host page's HTTP response headers.
  2. Do NOT include focus-without-user-activation in the chat iframe's allow attribute.
  3. In your chat widget code, only call element.focus() inside a direct user-event handler (click, keydown).
  4. Test by calling document.activeElement.focus() from a setTimeout — it should be silently ignored in the iframe.
Scenario — Media player embedding a progress widget
Goal: A video player host allows an embedded chapter-navigation iframe to programmatically move focus to the next chapter button when the current chapter ends — this is a legitimate UX need driven by media events, not user input.
/* HTTP header — host page */ Permissions-Policy: focus-without-user-activation=(self "https://chapters.example.com") /* HTML */ <iframe src="https://chapters.example.com/nav" allow="focus-without-user-activation" ></iframe> /* Inside chapters iframe — now allowed to call focus() from media events */ video.addEventListener('ended', () => { nextChapterButton.focus(); // permitted by policy });
Live detection — this frame
Implementation steps
  1. List the chapter iframe's origin explicitly in the policy: focus-without-user-activation=(self "https://chapters.example.com").
  2. In the chapter iframe's HTML, add allow="focus-without-user-activation".
  3. Both conditions must be met: header grants AND iframe attribute grants.
  4. Log when focus() is called from non-user-event paths so you can audit intent over time.
Scenario — Widget marketplace (multiple third-party iframes)
Goal: A dashboard allows users to add third-party widgets. Only first-party dashboard widgets get focus control; all third-party widgets are denied, preventing any widget from disrupting the user's active input.
/* HTTP header — dashboard host */ Permissions-Policy: focus-without-user-activation=(self) /* First-party widget — same origin, implicitly allowed by (self) */ <iframe src="/widgets/calendar"></iframe> /* Third-party widget — cross-origin, not in allowlist */ <iframe src="https://third-party.example/widget" /* focus-without-user-activation intentionally absent from allow */ ></iframe> /* CSP can complement this — sandbox blocks even more */ <iframe src="https://untrusted.example/widget" sandbox="allow-scripts allow-same-origin" ></iframe>
Policy probes

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗