Cross-Origin-Opener-Policy
response header (also known as COOP)
Introduction
This is a semi-formal definition of the Cross-Origin-Opener-Policy
response header, to be merged into the HTML Standard pending various refactoring efforts. (See also the open issues.)
COOP can be used to prevent a site from opening a site with COOP in an auxiliary browsing context. The site attempting to do so will hold a reference to a closed browsing context and the site using COOP will be in a fresh top-level browsing context with its own browsing context group. A site using COOP can open auxiliary browsing contexts itself, if they are same-origin and also use COOP, or if it uses the same-origin-allow-popups
COOP policy.
Goal
Integrate this into the HTML Standard.
Model
This should only work over a secure context.
To ensure robust isolation the processing model also needs to account for the Cross-Origin-Embedder-Policy
response header (also known as COEP) to some extent. This document only defines how it augments COOP.
Document's have an associated cross-origin opener-policy, which is "same-origin + COEP
", "same-origin
", "same-origin-allow-popups
", or "unsafe-none
". It's initially "unsafe-none
". (XXX: document creation needs to use "obtaining a cross-origin opener-policy".)
Note: "same-origin + COEP
" is a special value that cannot be directly set, but is the result of setting COOP and COEP for a top-level document at the same time.
about:blank documents inherit cross-origin opener-policy from their creator's top-level browsing context's active document at the time of creation, if there is a creator and if the creator's origin is same origin with the creator's top-level origin.
To obtain a cross-origin opener-policy from a response response:
- Let value be the result of getting
Cross-Origin-Opener-Policy
from response's header list. - If value is null, then return "
unsafe-none
". - Let decodedValue be value, isomorphic decoded. (XXX: we should change Fetch so we can merge this into step 1.)
- If decodedValue is not "
same-origin
" or "same-origin-allow-popups
", then return "unsafe-none
". - If decodedValue is "
same-origin
", then:- Let coep be the result of obtaining a cross-origin embedder-policy from response. (XXX: the integration probably needs some slight adjustments as even if COOP ends up being "
unsafe-none
", we'd still want to respect COEP for non-COOP purposes. If we process COEP before COOP we could pass COEP into this algorithm instead.) - If coep is "
require-corp
", then return "same-origin + COEP
".
- Let coep be the result of obtaining a cross-origin embedder-policy from response. (XXX: the integration probably needs some slight adjustments as even if COOP ends up being "
- Return decodedValue.
Note: the Cross-Origin-Opener-Policy
header part is roughly equivalent to this non-normative ABNF:
Cross-Origin-Opener-Policy = %s"same-origin" / %s"same-origin-allow-popups" / %s"unsafe-none"; case-sensitive
To match cross-origin opener-policies, given A, originA, B, and originB:
- If A is "
unsafe-none
" and B is "unsafe-none
", then return true. - If A or B is "
unsafe-none
", then return false. - If A is B and originA is same origin with originB, then return true.
- Return false.
Changes to navigation
Then, when navigating from a document doc in a top-level browsing context bc to a response response:
-
Let currentCOOP be doc's cross-origin opener-policy.
-
Let currentOrigin be doc's origin.
-
Let potentialCOOP be the result of obtaining a cross-origin opener-policy from response.
-
Let potentialOrigin be response's URL's origin. TODO: this should take CSP into account as that can make it an opaque origin (and future policies that can do similar things).
-
If bc's popup sandboxing flag set is not empty and potentialCOOP is not "
unsafe-none
", then navigate bc to a network error and abort these steps.Note: This results in a network error as one cannot simultaneously provide a clean slate to a response using cross-origin opener policy and sandbox the result of navigating to that response.
-
If the result of matching currentCOOP, currentOrigin, potentialCOOP, and potentialOrigin is false and one of the following is false
- doc is the initial about:blank document
- currentCOOP is "
same-origin-allow-popups
" - potentialCOOP is "
unsafe-none
"
then:
- Let bcg be the result of creating a new browsing context group.
- If potentialCOOP is "
same-origin + COEP
", then set bcg's cross-origin isolated to true. - Move loading of response to a new top-level browsing context newTLBC in bcg.
- If bc's popup sandboxing flag set is not empty, then:
- Assert: potentialCOOP is "
unsafe-none
". - Set newTLBC's popup sandboxing flag set to bc's popup sandboxing flag set.
- Assert: potentialCOOP is "
- Discard bc. [Note: this does not close bc's browsing context group, except if it was the sole top-level browsing context in which case it could be collected.]
-
Otherwise, [do the normal thing].
Note: the intent is that this runs for each response, including redirects. User agents are expected to optimize away the many browsing context groups that would be created through a redirect chain, by keeping track as to whether it is needed at all.
Changes to choosing a browsing context
Then, a change is needed to "If the user agent has been configured such that in this instance it will create a new browsing context", which is part of the "The rules for choosing a browsing context" algorithm. In particular before the current algorithm runs:
- If current's top-level browsing context's active document's cross-origin opener-policy is "
same-origin
" or "same-origin + COEP
", then:- Let currentDoc be current's active document.
- If currentDoc's origin is not same origin with currentDoc's top-level origin, then set noopener to true and name to "
_blank
".
Additional implications of "same-origin + COEP
"
- It impacts
postMessage()
and causeswindow.crossOriginIsolated
to return true as detailed in HTML PR #4734. - As described in that same PR and proposed in issue #5122 it also impacts keying of agent clusters in the newly created browsing context (using origin rather than site as the key) as well as no-opping the
document.domain
setter. This still needs some compatibility analysis.
Drive-by nit: "compare ..." should return some description of how the arguments relate, but the compare algorithm here just returns true or false. e.g. "Compare 3 and 4" could return "less" but not "true". Is there a more semantic description of what that operation is trying to do?