This is a semi-formal definition of the
Cross-Origin-Opener-Policy header, to be merged into the HTML Standard pending various refactoring efforts. (See also the discussion in whatwg/html issue #3740.)
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 header to some extent. That header is mostly defined in https://mikewest.github.io/corpp/, but this document defines its effect on the
Cross-Origin-Opener-Policy header processing model.
Document's have an associated cross-origin opener-policy (a cross-origin opener policy). A cross-origin opener policy is null (
Cross-Origin-Opener-Policy header isn't set) or a struct consisting of a sameness ("same-origin" or "same-site"), an unsafe-allow-outgoing (a boolean), and a cross-origin embedder-policy (a boolean). (XXX: document creation needs to use "obtaining a cross-origin opener-policy".)
about:blank documents inherit cross-origin opener-policy from their creator's top-level browsing context's active document at the time of creation.
Note: When A embeds B and B creates a popup, this means the popup's initial about:blank document has origin B and A's cross-origin opener-policy.
To obtain a cross-origin opener-policy from a response response and a cross-origin opener policy initiator:
- Let value be the result of getting
Cross-Origin-Opener-Policyfrom response's header list.
- If value is null, then return null.
- Let decodedValue be value, isomorphic decoded. (XXX: we should change Fetch so we can merge this into step 1.)
- Let sameness be "same-origin".
- Let unsafeAllowOutgoing be false.
- If value is "
- If initiator is null, then return null.
- Set sameness to initiator's sameness and unsafeAllowOutgoing to initiator's unsafe-allow-outgoing.
- Let values be the result of strictly splitting decodedValue on HTTP tab or space. (XXX: strictly splitting requires a single code point at the moment.)
- If values is not "
same-origin" or "
same-site", then return null.
- If values's size is greater than 2, then return null.
- If values's size is 2 and values is not "
unsafe-allow-outgoing", then return null.
- Set sameness to values.
- If values's size is 2, then set unsafeAllowOutgoing to true.
- Let coop be a cross-origin opener policy whose sameness is sameness, unsafe-allow-outgoing is unsafeAllowOutgoing, and cross-origin embedder-policy is false.
- If coop's sameness is "same-origin" and unsafe-allow-outgoing is false, then set coop's cross-origin embedder-policy to 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 null, 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.)
- Return coop.
Cross-Origin-Opener-Policy header part is roughly equivalent to this non-normative ABNF:
Cross-Origin-Opener-Policy = sameness [ RWS outgoing ] / inherit sameness = %s"same-origin" / %s"same-site" ; case-sensitive outgoing = %s"unsafe-allow-outgoing" ; case-sensitive inherit = %s"unsafe-inherit" ; case-sensitive
To match cross-origin opener-policies, given A, originA, B, and originB:
- If A is null and B is null, then return true.
- If A or B is null, then return false.
- If A's sameness is B's sameness, A's unsafe-allow-outgoing is B's unsafe-allow-outgoing, and A's cross-origin embedder-policy is B's cross-origin embedder-policy then:
- If A's sameness is "same-origin" and originA is same origin with originB, then return true.
- If A's sameness is "same-site", originA's scheme is originB's scheme, and originA's host is same site with originB's host, then return true.
- Return false.
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 and currentCOOP.
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 non-null, then navigate bc to a network error and abort these steps.
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's unsafe-allow-outgoing is true
- potentialCOOP is null
- Create a new browsing context group, move loading of response to a new top-level browsing context newTLBC in that browsing context group.
- If bc's popup sandboxing flag set is not empty, then:
- Assert: potentialCOOP is null.
- Set newTLBC's popup sandboxing flag set to bc's popup sandboxing flag set.
- 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.