Fenced Frame

Draft Community Group Report,

This version:
https://wicg.github.io/fenced-frame/
Editor:
Dominic Farolino (Google)
Participate:
GitHub WICG/fenced-frame (new issue, open issues)
Commits:
GitHub spec.bs commits
Test Suite:
https://wpt.fyi/results/fenced-frame/

Abstract

The fenced frame enforces a boundary between the embedding page and the cross-site embedded document such that user data visible to the two sites is not able to be joined together.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

In a web that has its cookies and storage partitioned by top-frame site, there are occasions — such as interest group based advertising as provided by the [Protected-Audience] API, or Conversion Lift Measurements) — when it would be useful to display content from different partitions in the same page. This can only be done in a privacy-preserving way if the Documents that contain data from different partitions are isolated from each other, unable to communicate despite being re visually composed on the same page. iframe elements are not suitable for this, since they offer many intentional communication channels with their embedder. This specification introduces the fencedframe element, a new element to embed Documents on a page that explicitly prevents communication between the Document and its embedder.

This specification defines the new element, its integration with the rest of the web platform, including § 3 HTML Integration and § 4 Interactions with other specifications, and its supporting primitives like FencedFrameConfig, which is the major input to the fencedframe in place of normal URLs and "src" attributes. Given that this specification defines a new element and its integration with the rest of the platform, it should be read as largely a monkeypatch to the [HTML], with its end goal to be merged into that standard, provided there is adequate cross-browser support.

2. The fencedframe element

Categories:
Flow content.
Phrasing content.
Embedded content.
Interactive content.
Palpable content.
Contexts in which this element can be used:
Where embedded content is expected.
Content model:
Nothing.
Content attributes:
Global attributes
width — Horizontal dimension
height — Vertical dimension
allowPermissions policy to be applied to the fencedframe's contents
Accessibility considerations:

TODO

DOM interface:
[Exposed=Window]
interface HTMLFencedFrameElement : HTMLElement {
  [HTMLConstructor] constructor();

  [CEReactions] attribute FencedFrameConfig? config;
  [CEReactions] attribute DOMString width;
  [CEReactions] attribute DOMString height;
  [CEReactions] attribute DOMString allow;
};

The fencedframe element represents its fenced navigable.

Descendants of fencedframe elements represent nothing.

Each fencedframe has a config, which is either a FencedFrameConfig or null. It is initially null.

When a fencedframe element element is inserted into a document whose browsing context is non-null, run these steps:
  1. Let nested traversable be the result of creating a new nested traversable for element.

  2. Set nested traversable’s loading mode to "fencedframe".

  3. Parse the sandbox attributes, once it exists

It’s not necessary to call the URL and history update steps as we do during usual child navigable creation or top-level traversable creation, but we still need a mechanism to initialize History.length in the new navigable. This is an existing issue in the HTML Standard: https://github.com/whatwg/html/issues/9030.

When a fencedframe element is removed from a document, the user agent

TODO: destroy the nested traversable

.

The config IDL attribute getter steps are to return this's config.

The config IDL attribute setter steps are:
  1. If this is not connected:

    1. Assert: this's fenced navigable is null.

      Note: This holds because when the element has been removed from the DOM, its removal steps immediately destroy the fenced navigable.

  2. Let urn uuid be the given FencedFrameConfig's urn.

  3. Let shared storage context be the given FencedFrameConfig's sharedStorageContext.

  4. Navigate element’s fenced navigable to urn uuid using element’s node document, with historyHandling set to "replace", referrerPolicy set to "no-referrer", and shared storage context.

    Note: See § 3.8.3 Actual navigation changes for the fencedframe-specific changes to the ordinary navigation flow.

Tests

The allow attribute, when specified, determines the container policy that will be used when the permissions policy for a Document in the fencedframe's 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.

2.1. Dimension attributes

This section details monkeypatches to [HTML]'s Dimension attributes section. That section will be updated to include fencedframe in the list of elements whose own width and height dimension attributes have the same author requirements that apply to the general width and height dimension attributes defined in [HTML].

Furthermore, the IDL attributes width and height must reflect the respective content attributes of the same name.

2.2. Fenced frame config mapping

Each traversable navigable has a fenced frame config mapping, which is a fenced frame config mapping.

Note: This mapping is consulted during navigation, and written to by what we colloquially refer to as URN-generating APIs or config-generating APIs, that generate both urn uuids and fenced frame configs for use in navigating fencedframe and iframe elements. See for example, the [Protected-Audience] API and [Shared-Storage] specifications.

A fenced frame config mapping has three submappings:

pending config mapping

a map whose keys are urn uuids and whose values are fenced frame configs

finalized config mapping

a map whose keys are urn uuids and whose values are fenced frame configs

nested config mapping

a map whose keys are urn uuids and whose values are fenced frame configs

Note: The purpose of pending configs is to enable config-generating APIs to resolve configs asynchronously in a way that doesn’t create timing side channels, i.e., the pending config is returned to the web platform in a constant amount of time, before any computation whose duration depends on cross-site data. Because the privacy of this depends on the web platform not being able to discern when a pending config is finalized, it is important that all visibilities and values of transparent fields do not change from the pending config to the finalized config, given that they can be inspected through FencedFrameConfig's getters. Therefore, a FencedFrameConfig that is created and exposed to the web platform is effectively immutable even if the fenced frame config represented by the config's urn is technically "pending", and will finish resolving completely later.

Each fenced frame config mapping has a maximum number of configs, which is implementation-defined. The maximum number of configs may be a non-negative number or infinity.

Note: It is important to specify the behavior of maximum number of configs because its semantics can interact with config-generating APIs in a privacy sensitive way.

At a high level, in order to store a fenced frame config in the fenced frame config mapping, the creator of the config must first store a pending config, and then turn the pending config into a finalized config. Those procedures are as follows:

To store a pending config in a fenced frame config mapping mapping given a fenced frame config config, run these steps:
  1. Let pendingMapping be mapping’s pending config mapping.

  2. If the size of pendingMapping + the size of mapping’s finalized config mappingmapping’s maximum number of configs, return failure.

  3. Let urn be a randomly generated urn uuid.

  4. Assert: urn does not exist in pendingMapping.

  5. Set pendingMapping[urn] to config.

  6. Return urn.

To finalize a pending config in a fenced frame config mapping mapping given a urn uuid urn and fenced frame config config, run these steps:
  1. Let pendingMapping be mapping’s pending config mapping.

  2. Let finalizedMapping be mapping’s finalized config mapping.

  3. If pendingMapping[urn] does not exist, return failure.

  4. Remove pendingMapping[urn].

  5. Set finalizedMapping[urn] to config.

To store nested configs in a fenced frame config mapping mapping given a nested configs nestedConfigs, run these steps:
  1. Let nestedMapping be mapping’s nested config mapping.

  2. If nestedConfigs is null, return.

  3. For each urnconfig of nestedConfigs:

    1. Set nestedMapping[urn] to config.

    2. Set nestedMapping[urn]'s is ad component to true.

To find a config in a fenced frame config mapping mapping given a urn uuid urn, run these steps:
  1. Let nestedMapping be mapping’s nested config mapping.

  2. Let pendingMapping be mapping’s pending config mapping.

  3. Let finalizedMapping be mapping’s finalized config mapping.

  4. If nestedMapping[urn] exists, return its value.

  5. If pendingMapping[urn] exists, wait until it does not exist.

  6. If finalizedMapping[urn] exists, return its value.

  7. Return failure.

2.3. Fenced frame configs

2.3.1. Introduction

This section is non-normative.

A key feature of the fencedframe element is that web platform APIs can configure the behavior of the frame in a way that limits the ability of other execution contexts to modify or inspect this configuration, for security and privacy reasons. For example, the [Protected-Audience] API performs on-device ad auctions over cross-site data, and it is important that the ad that wins the auction can be loaded into a frame, without the API caller knowing which ad won the auction or being able to manipulate the environment in which the ad loads.

We achieve this using the concept of a "fenced frame config". A fenced frame config is a collection of fields that can be loaded into fencedframe elements and that specifies the resulting environments. Fenced frame configs can only be created by specific web platform APIs, and not constructed or modified by script. Their fields also contain "visibilities", which dictate whether the field should be "redacted" when inspected through the FencedFrameConfig interface. Config-generating APIs like the [Protected-Audience] and [Shared-Storage] APIs must specify values for all fields of their fenced frame configs in order to ensure that they have considered the privacy implications of each field, though they may choose to set the values to null.

Each time a fencedframe navigates to a fenced frame config, it is instantiated as a new fenced frame config instance, which governs the particular browsing context group inside the fenced navigable.

2.3.2. The fenced frame config struct

We now establish some preliminary types:

A visibility is either "opaque" or "transparent".

A size is a struct with the following items:

width

a non-negative integer

height

a non-negative integer

TODO: Consider different numeric types for these members.

An interest group descriptor is a struct with the following items:

owner

an origin

name

a string

An exhaustive set of sandbox flags is a sandboxing flag set.

A pending event is a struct with the following items:

destination

a FenceReportingDestination

event

a destination event

A reporting destination info is a struct with the following items:

reporting url map

a map whose keys are strings and whose values are URLs

reporting macro map

null, or a map whose keys are strings and whose values are strings

A fenced frame reporting map is a map whose keys are FenceReportingDestinations and whose values are either:

Note: This representation is meant to allow config-generating APIs to reduce latency by resolving the values of reporting destinations asynchronously, after they’ve already constructed and returned the fenced frame config (and even after the config has been loaded, and event reports have been generated inside the fenced frame). When the config-generating API declares the fenced frame reporting map, they can mark certain destinations as pending using an empty list, and then maintain a reference to the map for later. If the fenced frame attempts to report an event to a destination while it is still pending, it stores the event in this list for later handling. When the config-generating API or its callback eventually finalizes a reporting destination through the reference it kept, it will handle all of the pending events stored in the list. If the destination is never finalized, then the pending events will never be sent.

In order to finalize a reporting destination, given a fenced frame reporting map reporting map, a FenceReportingDestination destination, a map destination map whose keys are strings and whose values are urls, and macro map, which is either null or a map whose keys are strings and whose values are strings, run these steps:
  1. Assert that reporting map[destination] is a list (i.e., that destination’s metadata has not yet been finalized).

  2. Let pending event list be reporting map[destination].

  3. Set reporting map[destination] to a struct with the following items:

    reporting url map

    destination map

    reporting macro map

    macro map

  4. For each pending event of pending event list:

    1. Send a beacon with destination map and pending event’s event.

A fenced frame reporting metadata is a struct with the following items:

fenced frame reporting map

a fenced frame reporting map

direct seller is seller

a boolean, initially true

allowed reporting origins

null or a list of origins. An origin must be present in this list to be the destination of a destination URL event report.

attempted custom url report to disallowed origin

a boolean, initially false

An automatic beacon event type is either "reserved.top_navigation_start", "reserved.top_navigation_commit", or "reserved.top_navigation".

reserved.top_navigation is an earlier naming of reserved.top_navigation_commit. While they both do the same thing, reserved.top_navigation will be removed in the future and should not be used for new code.

A fenced frame reporter is a struct with the following items:

fenced frame reporting metadata reference

a mutable reference to a fenced frame reporting metadata TODO: Handle pointers/references in a more spec-y way

A destination enum event is a struct with the following items:

type

a string

data

a string

attributionReportingEnabled

a boolean

attributionReportingContextOrigin

an origin

A destination URL event is a URL.

An automatic beacon event is a struct with the following items:

type

an automatic beacon event type

data

a string

attributionReportingEnabled

a boolean

attributionReportingContextOrigin

an origin

A destination event is either a destination enum event, a destination URL event, or a automatic beacon event.

In order to send a beacon with a reporting destination info destination info and a destination event event, run these steps:
  1. Let destination url be an empty string.

  2. Let attributionReportingEligibility be "unset".

  3. Let processResponse be null.

  4. Let useParallelQueue be false.

  5. If event is either a destination enum event or an automatic beacon event, then:

    1. Let destination map be destination info’s reporting url map.

    2. Let eventType be either event’s destination type, or automatic type, depending on which variant event is.

    3. If destination map[eventType] does not exist, return.

    4. Set destination url to destination map[eventType].

    5. If event’s attributionReportingEnabled is true and the result of getting supported registrars is not empty and event’s attributionReportingContextOrigin is suitable:

      1. If event’s eventType matches one of the automatic beacon event type values, set attributionReportingEligibility to "navigation-source".

      2. Otherwise, set attributionReportingEligibility to "event-source".

      3. Set processResponse to these steps given a response response:

        1. Run process an attribution eligible response with event’s attributionReportingContextOrigin, attributionReportingEligibility, and response.

      4. Set useParallelQueue to true.

  6. Otherwise:

    1. Assert: event is a destination URL event.

    2. Let macro map be destination info’s reporting macro map.

    3. If macro map is null, return.

    4. Set destination url to event.

    5. Let destination url be the result of substituting macros with macro map into destination url.

  7. Optionally, return.

    Note: This implementation-defined condition is intended to allow user agents to drop the beacon for a number of reasons, for example user opt-out or destination url’s site not being enrolled.

  8. Let request be a new request with the following properties:

    method

    POST if event is a destination enum event, otherwise GET.

    URL

    destination url

    header list

    A new header list containing a header whose name is "Content-Type" and value is "text/plain".

    body

    If event is a destination enum event, a body whose source is event’s data, otherwise null.

    client

    null

    service-workers mode

    "all"

    The default is "all", so we technically don’t have to set anything here. We do in order to remind ourselves that it might be more appropriate to skip service workers here, like some other beacons.

    origin

    Get the origin from somewhere, like the implementation does.

    referrer

    "no-referrer"

    mode

    "cors"

    credentials mode

    "omit"

    Attribution Reporting eligibility

    attributionReportingEligibility

  9. Fetch request with processResponse being processResponse if it is not null and useParallelQueue being useParallelQueue.

    Note: This algorithm can be invoked from while in parallel or while on a Document's main thread. To handle the in parallel invocation correctly, we invoke fetch with useParallelQueue set to true when the optional response-handling algorithm is used, otherwise there is no need to do this even when this algorithm is running in parallel in other instances.

In order to report an event using a fenced frame reporter reporter with a FenceReportingDestination destination, and a destination event event, run these steps:
  1. Let metadata be reporter’s fenced frame reporting metadata reference.

  2. If destination is "direct-seller":

    1. If metadata’s direct seller is seller is true, set destination to "seller".

    2. Otherwise, set destination to "component-seller".

  3. If event is a destination URL event:

    1. If event’s origin is not same origin with any of the entries in metadata’s allowed reporting origins:

      1. Set metadata’s attempted custom url report to disallowed origin to true.

    2. If metadata’s attempted custom url report to disallowed origin is true, return.

  4. Let reporting map be a reference to metadata’s fenced frame reporting map.

  5. If reporting map[destination] does not exist, return.

  6. If reporting map[destination] is a list:

    1. Let newEvent be a new pending event with the following:

      destination

      destination

      event

      event

    2. Append newEvent to reporting map[destination].

    3. Return.

      Note: The pending event will be sent asynchronously.

  7. Assert that reporting map[destination] is a map (i.e. that destination’s metadata has been finalized).

  8. Send a beacon with reporting map[destination] and event.

In order to report a private aggregation event using a fenced frame reporter reporter with a string event, run these steps:
  1. If event’s eventType starts with "reserved.", then return.

  2. reporter event TODO: Fill this in

An exfiltration budget metadata is a struct with the following items:

origin

an origin

amount to debit

a non-negative valid floating point number

An exfiltration budget metadata reference is a struct with the following items:

origin

an origin

amount to debit reference

a mutable reference to a non-negative valid floating point number TODO: Handle pointers/references in a more specy-y way

A partition nonce is an implementation-defined value.

Note: This is similar to the network partition key used by Fetch.

A fenced frame config is a struct with the following items:

mapped url

a struct with the following items:

value

a URL

visibility

a visibility

container size

null, or a size

content size

null, or a struct with the following items:

value

a size

visibility

a visibility

interest group descriptor

null, or a struct with the following items:

value

an interest group descriptor

visibility

a visibility

on navigate callback

null, or a series of steps

effective sandbox flags

null, or a struct with the following items:

value

an exhaustive set of sandbox flags

visibility

a visibility

effective enabled permissions

null, or a struct with the following items:

value

a list of policy-controlled features

visibility

a visibility

Note: When non-null, this is a list of policy-controlled features that the generator of this config relies on exclusively being enabled inside the fencedframe that navigates to this config. Specifically, each feature in this list must be enabled by the fencedframe's fenced navigable's permissions policy's inherited policy when navigating to this config for the navigation to succeed. The features in this list are not force-enabled, but rather are used to check that the embedder environment that influences the aforementioned inherited policy is relaxed enough to support these essential features. If the inherited policy value for any of these features is "Disabled", the navigation to this config will fail. Any policy-controlled feature not in this list will not be "Disabled" in the fencedframe that navigates to this config.

fenced frame reporting metadata

null, or a struct with the following items:

value

a fenced frame reporting metadata

visibility

a visibility

exfiltration budget metadata

null, or a struct with the following items:

value

an exfiltration budget metadata

visibility

a visibility

nested configs

null, or a struct with the following items:

value

a list of fenced frame configs

visibility

a visibility

embedder shared storage context

null, or a string

is ad component

A boolean. Defaulting to false.

Note: When true, this fenced frame config reprsents an ad component. An ad component can be used to construct ads composed of multiple pieces. See the Protected Audience explainer. For an ad component, event reporting is handled differently. See the Fenced Frame Ads Reporting explainer that describes this.

2.3.3. The fenced frame config instance struct

A fenced frame config instance is a struct with the following items:

mapped url

a URL

container size

null, or a size

content size

null, or a size

interest group descriptor

null, or an interest group descriptor

on navigate callback

null, or a series of steps

effective sandbox flags

null, or an exhaustive set of sandbox flags

effective enabled permissions

null, or a list of policy-controlled features

fenced frame reporter

null, or a fenced frame reporter

exfiltration budget metadata reference

null, or an exfiltration budget metadata reference

nested configs

null, or an map whose keys are urn uuids and whose values are fenced frame configs

partition nonce

a partition nonce

embedder shared storage context

null, or a string

is ad component

A boolean, initially false.

To instantiate a config given a fenced frame config config, return a fenced frame config instance with the following members:
mapped url

config’s mapped url's value

container size

config’s container size

content size

config’s content size if null, otherwise config’s content size's value

interest group descriptor

config’s interest group descriptor if null, otherwise config’s interest group descriptor's value

on navigate callback

config’s on navigate callback

effective sandbox flags

config’s effective sandbox flags if null, otherwise config’s effective sandbox flags's value

effective enabled permissions

config’s effective enabled permissions if null, otherwise config’s effective enabled permissions's value

fenced frame reporter
  1. If config’s fenced frame reporting metadata's value is null, set to null.

  2. Otherwise, set to a fenced frame reporter with the following members:

    fenced frame reporting metadata reference

    a reference to config’s fenced frame reporting metadata's value

exfiltration budget metadata reference
  1. If config’s exfiltration budget metadata is null, set to null.

  2. Otherwise, set to a exfiltration budget metadata reference:

    origin

    config’s exfiltration budget metadata's value's origin

    amount to debit reference

    a reference to config’s exfiltration budget metadata's value's amount to debit

nested configs
  1. If config’s nested configs is null, set to null.

  2. Otherwise:

    1. Let results be a new map.

    2. For each nested config of config’s nested configs's value:

      1. Let urn be a randomly generated urn uuid.

      2. Set results[urn] to nested config.

    3. Set nested configs to results.

partition nonce

a random, unique partition nonce

embedder shared storage context

config’s embedder shared storage context

is ad component

config’s is ad component

Each browsing context has a fenced frame config instance, which is a fenced frame config instance or null, initially null.

This fenced frame config instance should really exist on browsing context group, however until third-party cookies are deprecated, this specification supports many of the fencedframe concepts on the iframe element. This requires that for the short term, a normal content navigable be able to load a fenced frame config, and therefore have access to the navigation’s corresponding fenced frame config instance.

2.3.4. The FencedFrameConfig interface

One major input to the fencedframe element is the FencedFrameConfig interface, which maps to an internal fenced frame config struct.

enum OpaqueProperty {"opaque"};

typedef (unsigned long or OpaqueProperty) FencedFrameConfigSize;
typedef USVString FencedFrameConfigURL;

[Exposed=Window, Serializable]
interface FencedFrameConfig {
  readonly attribute FencedFrameConfigSize? containerWidth;
  readonly attribute FencedFrameConfigSize? containerHeight;
  readonly attribute FencedFrameConfigSize? contentWidth;
  readonly attribute FencedFrameConfigSize? contentHeight;

  undefined setSharedStorageContext(DOMString contextString);
};

Note: Note that FencedFrameConfigs cannot be constructed manually from JavaScript. They can only be created by config-generating APIs. However, some browsers support a developer-only flag which help developers test the fencedframe element with arbitrary URLs not produced by config-generating APIs for development.

Each FencedFrameConfig has:

The containerWidth IDL attribute getter steps are to return this's containerWidth.
The containerHeight IDL attribute getter steps are to return this's containerHeight.
The contentWidth IDL attribute getter steps are to return this's contentWidth.
The contentHeight IDL attribute getter steps are to return this's contentHeight.
The setSharedStorageContext(contextString) method steps are to set this's sharedStorageContext to contextString.
FencedFrameConfig objects are serializable objects. Their serialization steps, given value, serialized, and forStorage are:
  1. If forStorage is true, then throw a DataCloneError DOMException.

  2. Set serialized.[[Urn]] to value’s urn.

  3. Set serialized.[[SharedStorageContext]] to value’s sharedStorageContext.

  4. Set serialized.[[ContainerWidth]] to value’s containerWidth.

  5. Set serialized.[[ContainerHeight]] to value’s containerHeight.

  6. Set serialized.[[ContentWidth]] to value’s contentWidth.

  7. Set serialized.[[ContentHeight]] to value’s contentHeight.

Their deserialization steps, given serialized, value, and targetRealm are:
  1. Initialize value’s urn to serialized.[[Urn]].

  2. Initialize value’s sharedStorageContext to serialized.[[SharedStorageContext]].

  3. Initialize value’s containerWidth to serialized.[[ContainerWidth]].

  4. Initialize value’s containerHeight to serialized.[[ContainerHeight]].

  5. Initialize value’s contentWidth to serialized.[[ContentWidth]].

  6. Initialize value’s contentHeight to serialized.[[ContentHeight]].

Note: To help with ease of adoption, until 2026 we will support the API navigator.deprecatedReplaceInURN(), which allows you to substitute macros into the mapped url corresponding to a given urn uuid or FencedFrameConfig.

typedef (USVString or FencedFrameConfig) UrnOrConfig;

partial interface Navigator {
  Promise<undefined> deprecatedReplaceInURN(
    UrnOrConfig urnOrConfig, record<USVString, USVString> replacements);
};
To substitute macros with an ordered map with string keys and string values macros into a string string, run these steps:
  1. TODO: Spec this. Substitute the keys from macros with the corresponding values into string, and return the new string. There is no recursive substitution.

The deprecatedReplaceInURN(urnOrConfig, replacements) method steps are:
  1. Let urn be null.

  2. If urnOrConfig is a USVString, set urn to urnOrConfig.

  3. Otherwise, set urn to urnOrConfig’s urn.

  4. If urn is TODO invalid, throw a TypeError.

  5. For each key → _ of replacements:

    1. If key does not start with ${ or %%, throw a TypeError.

    2. If key does not end with } or %%, throw a TypeError.

  6. Let p be a new promise.

  7. Let global be this's relevant global object.

  8. Run the following steps in parallel:

    1. Let mapping be global’s associated Document's node navigable's traversable navigable's fenced frame config mapping.

    2. Let config be the result of finding a config in mapping with urn.

    3. If config is failure, queue a global task on the DOM manipulation task source given global, to resolve p with undefined, and abort these steps.

    4. Let substitutedUrl be the result of substituting macros with replacements into config’s mapped url's value.

    5. Set config’s mapped url's value to substitutedUrl.

    6. Queue a global task on the DOM manipulation task source given global, to resolve p with undefined.

  9. Return p.

Tests

2.4. The Fence interface

Several APIs specific to fenced frames are defined on the Fence interface.

enum FenceReportingDestination {
  "buyer",
  "seller",
  "component-seller",
  "direct-seller",
  "shared-storage-select-url",
};

dictionary FenceEvent {
  // This dictionary has two mutually exclusive modes that aren’t represented as
  // distinct IDL types due to distinguishability issues:
  //
  // When reporting to a preregistered destination (specified by enum), the following
  // properties are used:
  DOMString eventType;
  DOMString eventData;
  sequence<FenceReportingDestination> destination;
    
  // When setting event data to be used later in an automatic beacon, the
  // following properties are used:
  boolean once = false;
  boolean crossOriginExposed = false;

  // When reporting to a custom destination URL (with substitution of macros defined by
  // the Protected Audience buyer), the following property is used:
  USVString destinationURL;
};

typedef (FenceEvent or DOMString) ReportEventType;

[Exposed=Window]
interface Fence {
    undefined reportEvent(optional ReportEventType event = {});
    undefined setReportEventDataForAutomaticBeacons(optional FenceEvent event = {});
    sequence<FencedFrameConfig> getNestedConfigs();
};
The reportEvent(event) method steps are:
  1. Let instance be this's relevant global object's browsing context's fenced frame config instance.

  2. If instance is null, then return.

  3. If instance’s is ad component is true, then return.

  4. If the relevant settings object's origin and instance’s mapped url's origin are not same origin, then return.

  5. If instance’s fenced frame reporter is null, then return.

  6. If event is a DOMString, run report a private aggregation event using instance’s fenced frame reporter with event.

  7. If event is a FenceEvent:

    1. If event’s eventType starts with "reserved.", then return.

    2. If event has a destinationURL:

      1. If event has a destination or a eventType or a eventData:

        1. Throw a TypeError.

      2. Let destinationURL be the result of running the URL parser on destinationURL.

      3. Throw a TypeError if any of the following conditions hold:

        • destinationURL is failure;

        • destinationURL scheme is not "https";

      4. Run report an event using instance’s fenced frame reporter with buyer and a destination URL event that is event’s destinationURL.

    3. Otherwise:

      1. If event does not have a destination or event does not have a eventType:

        1. Throw a TypeError.

      2. Let document be this's relevant global object's associated Document.

      3. Let attributionReportingEnabled be the result of determining whether document is allowed to use the "attribution-reporting" feature.

      4. Let attributionReportingContextOrigin be document’s context origin.

      5. For each destination of event’s destination:

        1. Run report an event using instance’s fenced frame reporter with destination and a destination enum event with the following items:

          type

          event’s eventType

          data

          event’s eventData (or the empty string if it is not defined).

        attributionReportingEnabled

        attributionReportingEnabled

        attributionReportingContextOrigin

        attributionReportingContextOrigin

Tests
The setReportEventDataForAutomaticBeacons(event) method steps are:
  1. If event does not have a destination or event does not have a eventType:

    1. Throw a TypeError.

  2. If event’s eventType does not match one of the automatic beacon event type values, return.

  3. Let instance be this's relevant global object's browsing context's fenced frame config instance.

  4. If instance is null, then return.

  5. If the relevant settings object's origin and instance’s mapped url's origin are not same origin, then return.

  6. If instance’s fenced frame reporter is null, then return.

  7. Set this's relevant global object's associated Document's automatic beacon data map[event’s eventType] to an automatic beacon data with the following items:

    eventData

    event’s eventData if defined and instance’s is ad component is false, otherwise empty string.

    destination

    event’s destination

    once

    event’s once

    crossOriginExposed

    event’s crossOriginExposed

Tests
The getNestedConfigs() method steps are:
  1. Let instance be this's relevant global object's browsing context's fenced frame config instance.

  2. If instance is null, then return.

  3. If the relevant settings object's origin and instance’s mapped url's origin are not same origin, then return.

  4. If instance’s nested configs is null, then return.

  5. Let results be an empty list of FencedFrameConfigs.

  6. For each urnconfig of instance’s nested configs:

    1. Let newConfig be a new FencedFrameConfig object created in this's relevant realm, with the following:

      urn

      urn

      sharedStorageContext

      config’s embedder shared storage context

      containerWidth

      null if config’s container size is null, otherwise config’s container size's width

      containerHeight

      null if config’s container size is null, otherwise config’s container size's height

      contentWidth

      null if config’s content size is null, the "opaque" OpaqueProperty if config’s content size's visibility is opaque, otherwise config’s content size's width

      contentHeight

      null if config’s content size is null, the "opaque" OpaqueProperty if config’s content size's visibility is opaque, otherwise config’s content size's height

    2. Append newConfig to results.

  7. Return results.

Tests

2.5. New request destination

The processing model of a fencedframe's navigation request deviates from that of the normal navigation request in enough ways to justify a new request destination value. This specification updates the request destination enumeration to include a new entry, "fencedframe". Perform the following monkeypatches to the [FETCH] Standard.

Add "fencedframe" to the non-subresource request list and to the navigation request list.

Add "fencedframe" to the RequestDestination enum.

In the fetch algorithm, step 13.2, where it says:

A user agent should set value to the first matching statement, if any, switching on request’s destination:

Add "fencedframe" to the switch cases alongside "document", "frame", and "iframe".

Tests

Non-normatively, update the DOM intro destination table to illustrate that fencedframe navigation requests have the following properties:

2.6. Automatic Reporting

This first introductory paragraph is non-normative.

A side effect of the fenced boundary model is that ads will lose the ability to know if a click resulted in a successful navigation. This is because the page loaded from a top-level navigation originating from a fenced frame will not be allowed to report to the fenced frame that it loaded (through something like window.opener). Instead, we introduce special event-level reporting types, reserved.top_navigation_start and reserved.top_navigation_commit, which automatically sends an event-level beacon when a fenced frame initiates a successful navigation to a top-level traversable.

To attempt to send an automatic beacon given a source snapshot params sourceSnapshotParams, an origin sourceOrigin, a Document targetDocument, and an automatic beacon event type eventType, run these steps:
  1. If targetDocument’s node navigable's traversable navigable is not a top-level traversable, abort these steps.

  2. If sourceSnapshotParams’s has transient activation is set to false, abort these steps.

  3. Let config be sourceSnapshotParams’s initiator fenced frame config instance.

  4. If config is null, abort these steps.

    Note: Since this algorithm is called unconditionally for all navigations, this is used to catch cases where a navigation to a top-level traversable does not originate from a fencedframe.

  5. Let beacon data be sourceSnapshotParams’s automatic beacon data map[eventType].

  6. Let has header opt in be sourceSnapshotParams’s automatic beacons allowed.

  7. If beacon data is null and has header opt in is false, abort these steps.

  8. Let is cross origin be true if sourceOrigin is not same origin with config’s mapped url's origin, false otherwise.

  9. If is cross origin is true and has header opt in is false, abort these steps.

  10. Let should send beacon with data be true if beacon data is not null and either is cross origin is false or beacon data’s crossOriginExposed is true, false otherwise.

  11. For each destination of config’s fenced frame reporter's fenced frame reporting metadata reference's fenced frame reporting map's keys:

    1. Run report an event using config’s fenced frame reporter with destination and an automatic beacon event with the following items:

      type

      eventType

      data

      beacon data’s eventData if should send beacon with data is true, beacon data’s destinations contains destination, and config’s is ad component is false, the empty string otherwise.

      attributionReportingEnabled

      sourceSnapshotParams’s attribution reporting enabled

      attributionReportingContextOrigin

      sourceSnapshotParams’s attribution reporting context origin

  12. If beacon data’s once is true, set sourceSnapshotParams’s automatic beacon data map[eventType] to null.

Tests
Modify [HTML]'s navigate algorithm. Add a new step after step 4 that reads:
  1. Attempt to send an automatic beacon given sourceSnapshotParams, initiatorOriginSnapshot, navigable’s associated Document, and reserved.top_navigation_start.

Modify [HTML]'s attempt to populate the history entry’s document algorithm. In step 6, substep 11, add a new step after step 5 that reads:
  1. If failure is false, then:

    1. Attempt to send an automatic beacon given sourceSnapshotParams, entry’s document state's initiator origin, document, and reserved.top_navigation_commit.

    2. Attempt to send an automatic beacon given sourceSnapshotParams, entry’s document state's initiator origin, document, and reserved.top_navigation.

3. HTML Integration

3.1. Extensions to the Window interface

partial interface Window {
  // Collection of fenced frame APIs
  readonly attribute Fence? fence;
};

Each Window object has an associated fence, which is a Fence instance created alongside the Window.

The fence getter steps are:
  1. If this's browsing context's fenced frame config instance is not null, then return this's fence.

  2. Return null.

Tests

3.2. Document supporting concepts

We first establish some preliminary types:

An automatic beacon data is a struct with the following items:

eventData

a string

destination

a list of FenceReportingDestinations

once

a boolean

crossOriginExposed

a boolean

Each Document object has an associated automatic beacon data map, which is a map whose keys are automatic beacon event types and whose values are null or an automatic beacon data

Each Document object has an associated automatic beacons allowed, which is a boolean, initially false.

3.3. Modifications to creating browsing contexts

In [HTML]'s creating a new browsing context and document algorithm, rewrite the two steps (currently steps 4 & 15) starting with;

to instead use the tighter condition:

Note: This is because we need to ensure that we do not leak creator’s referrer, origin, creator base url, policy container, across the fenced frame boundary.

Add a substep to step 4 of this algorithm (that was modified above) that reads:

  1. Set browsingContext’s fenced frame config instance to creator’s browsing context's fenced frame config instance.

3.4. Policy container inheritance

When making a navigation request to a local URL, iframes clone their policy container from the navigation request's initiator Document. If fencedframes were to do the same thing, that would allow information about the initiator’s policy container to leak across a fenced frame boundary. This section patches policy container inheritance to close that leak.

Modify the determine navigation params policy container algorithm to have a new optional boolean parameter fenced that defaults to false.

Rewrite step 3 to read:

  1. If responseURL is local, initiatorPolicyContainer is not null, and fenced false, then return a clone of initiatorPolicyContainer.

Note: We do not need to modify the case where responseURL is about:srcdoc, because navigations to about:srcdoc are not supported in fenced frames.

Add a step before step 23 of create navigation params by fetching that says:
  1. Let fenced be true if navigable is a fenced navigable, false otherwise.

    Note: This ensures fenced is true regardless of whether the initiator Document is navigable’s active document or its unfenced parent.

Rewrite step 23 (now step 24) to read:

  1. Let resultPolicyContainer be the result of determining navigation params policy container given response’s URL, entry’s document state's history policy container, sourceSnapshotParams’s source policy container, null, responsePolicyContainer, and fenced.

Note: fencedframe policy container inheritance upon initial Document creation is handled in the § 3.3 Modifications to creating browsing contexts section.

3.5. Nested traversables

3.5.1. Introduction

This section is non-normative.

The [HTML] Standard organizes navigables into two categories: child navigables and traversable navigables (also known as top-level traversables). The introduction of features like fenced frames, and to a lesser extent portals, complicates this model by adding a new type of traversable navigable that is sometimes like a child navigable. Because these new frame types are housed in a separate browsing context group from their embedder, some concrete level of isolation is expected and required; on the other hand since they are composed visually inside of other browsing context groups, sometimes they need to behave like the normal child navigables that we see in e.g., iframes.

The complexity here is in deciding when terms like navigable container, navigable parent, and descendant navigables need to cross the traversable navigable/browsing context group boundary, versus when doing so would be unsafe or incorrect. The examples below illustrate this point.

When a user activates content inside of a Document, ordinarily the activation notification steps give user activation to all ancestor navigables and all same origin descendant navigables. But because a fencedframe can host sensitive content that needs to be isolated from its embedder, and because user activation and consumption offer a communication vector between these two parties, for the purpose of user activation a fencedframe's fenced navigable cannot not be considered a descendant of its embedder, nor can its embedder be considered an ancestor of the fencedframe's fenced navigable, in the way that the user activation algorithms currently use those terms. In other words, we consider user activation to be fenced, to denote that it never crosses the fenced navigable boundary; if it were unfenced, it would behave as it would with iframes, allowing user activation to flow freely across the frame boundary.

Tests

Unlike user activation, when a fencedframe's fenced navigable gets created or navigated, it must inherit its embedder Document's active sandboxing flag set as is standard for Documents in normal child navigables. If we did not do this, then the fencedframe element would be a trivial sandbox bypass. Because fencedframe sandbox flag inheritance behaves similarly to how it does in iframe elements, we consider sandbox inheritance to be unfenced.

To provide the isolation mentioned above, and its conditional relaxation, this specification defines a new kind of parent for traversable navigables called an unfenced parent, which provides a link to its embedder that algorithms can intentionally use when they need to be unfenced, as described above.

Note: Introducing a new kind of parent (unfenced parent) is an intentional design decision. It means that by default, the fencedframe boundary is private and isolated, since by default nothing in the web platform traverses from a fencedframe's fenced navigable to its embedder. Care must be taken when modifying algorithms to make them capable of traversing across the fencedframe fenced navigable boundary, and each modification of this sort will be evaluated independently and appear in this specification.

The rest of this section provides patches to various [HTML] definitions (and their uses) that deal with collections of related navigables, with the intention of fencing and unfencing various parts of the web platform appropriately.

3.5.2. Traversable navigables

In [HTML]'s Traversable navigables section, add the following:

In addition to the properties of a navigable, a traversable navigable has:

Note: The unfenced parent link is what gives a fencedframe's fenced navigable a link to its embedder, which is used carefully for things that need to be "unfenced", like some algorithms in the focus processing model.

To get the unfenced parent of a navigable navigable:
  1. If navigable is a child navigable, return navigable’s parent.

  2. Assert: navigable is a fenced navigable.

  3. Return navigable’s unfenced parent.

Note: This algorithm is different from the traversable navigable's unfenced parent getter in that this algorithm first tries to get the navigable's normal parent if navigable is a normal child navigable.

To get the unfenced container document of a navigable navigable:
  1. Let parentNavigable be navigable’s unfenced parent.

  2. Return parentNavigable’s active document.

3.5.3. Nested traversables

In [HTML]'s Navigables section, add a new subsection titled "Nested traversables" with the following text, definitions, and algorithms.

Similar to navigable containers and their respective content navigables, other elements (so far, only the fencedframe element) present a more isolated navigable to the user. These elements are called fenced navigable containers.

A fenced navigable container has a fenced navigable, which is either a traversable navigable with a non-null unfenced parent, or null. It is initially null.

Tests
To initialize the nested traversable traversable given a document state documentState and a navigable parent:
  1. Initialize the navigable traversable given documentState.

  2. Set traversable’s unfenced parent to parent.

To create a new nested traversable given an element element:
  1. Let group be a new browsing context group.

Note: There doesn’t seem to be a reason to append group to the user agent’s browsing context group set like create a new browsing context group and document does.

  1. Let document be the second return value of creating a new browsing context and document given element node document, element, and group.

  2. Let documentState be a new document state, whose document is document.

  3. Let traversable be a new traversable navigable.

  4. Let parentNavigable be element’s node navigable.

  5. Initialize the nested traversable traversable given documentState and parentNavigable.

  6. Set element’s fenced navigable to traversable.

  7. Let initialHistoryEntry be traversable’s active session history entry.

  8. Set initialHistoryEntry’s step to 0.

  9. Append initialHistoryEntry to traversable’s session history entries.

  10. Return traversable.

Note: The create a new nested traversable algorithm creates the first kind of traversable navigable that is not a top-level traversable. This will require removing the note about nested traversables in [HTML]'s Top-level traversables section.

3.5.4. Top-level traversables

The [HTML] Standard currently defines a top-level traversable as a traversable navigable whose parent is null, however this is an insufficient definition that this specification changes. [HTML] mentions that outside of this specification, all traversable navigables are top-level traversables, but "envisions" future specifications that may want to create a kind of traversable that is nested, and achieves the nesting through a non-null parent; hence the distinction between top-level traversables and traversable navigables relies on the parent null-ness.

The fenced navigable this specification proposes is precisely what [HTML] envisioned when carving out space for the distinction between top-level traversables and traversable navigables, however this specification does not make use of the parent pointer for fenced navigables, for reasons described above (instead they use the unfenced parent pointer). That means by default, both top-level traversables and fenced navigables both have null parents, which renders the distinction meaningless.

To mend the intended distinction between top-level traversables and fenced navigables, patch the following definitions like so:

A top-level traversable is a traversable navigable whose parent and unfenced parent are both null.


To get the top-level traversable of a navigable inputNavigable:
  1. Let navigable be inputNavigable.

  2. While:

    1. If navigable’s parent and unfenced parent are both null, then break.

    2. Set navigable to navigable’s parent or unfenced parent, whichever is non-null.

      Note: Exactly one of navigable’s parent or unfenced parent will be non-null here.

  3. Return navigable.

Note: With these new definitions, a top-level traversable is essentially "unfenced" as described in the § 3.5.1 Introduction.

Modify the inclusive descendant navigables algorithm to take a new optional boolean argument unfenced that defaults to false.

Further rewrite step 2 of this algorithm to:

  1. Extend navigables with document’s descendant navigables with unfenced set to unfenced.

Modify the descendant navigables algorithm to take a new optional boolean argument unfenced that defaults to false, and rewrite the algorithm like so:
  1. Let navigables be a new list.

  2. Let navigableContainers be a list of all shadow-including descendants of document that are navigable containers (or fenced navigable containers, if unfenced is true), in shadow-including tree order.

  3. For each navigableContainer of navigableContainers:

    1. If navigableContainer’s content navigable and fenced navigable are both null, then continue.

    2. Let descendantNavigable be either navigableContainer’s content navigable or fenced navigable, whichever is non-null.

    3. Extend navigables with descendantNavigable’s active document's inclusive descendant navigables with unfenced set to unfenced.

  4. Return navigables.

3.7. Modifications to the focusing algorithms

The [HTML] standard defines how to handle focusing elements and Windows, both by user gesture and through script-initiated APIs. Since fenced frames are designed to prevent communication across a fenced frame boundary, we need to handle focusing carefully. This is because when focus crosses a fencedframe boundary, contexts on both sides of the boundary can detect that change, which can be used to open a communication channel between a fencedframe and its embedder.

We do this by not allowing the focusing steps to move script-initiated focus across a fenced frame boundary.

When a user clicks on an element like a button inside a fencedframe while another element outside of the fencedframe is focused, the focusing steps will allow the button to gain focus, because this is specifically a user-initiated action. Without allowing that, no element inside of a fencedframe would never be able gain focus.

If we were to continue allowing all elements to be focused via the focus() method as is the status quo before this specification, a fenced navigable container and its fenced navigable could use a sequence of focus() calls to send arbitrary data across the fenced frame boundary, which is a privacy leak. To avoid this, we effectively "fence" the focus() method, which sacrifices some functionality for privacy.

Modify the focusing steps to take a new optional boolean argument unfenced that defaults to false.

Add a new step after step 3 of the algorithm (that changes new focus target) that reads:

  1. If new focus target is a fenced navigable container with non-null fenced navigable, then set new focus target to the fenced navigable's active document.

Add a new step after the step that defines the new chain variable, that reads:

  1. If unfenced is false, new chain contains a Document document whose node navigable's traversable navigable is a fenced navigable, and old chain does not also contain document, then return.

    Note: This is how we bail-out early just before calling the focus update steps, in the case where focus is trying to cross the fence.

Modify the user agent sentence after the algorithm steps in focusing steps to read:

User agents must immediately run the focusing steps for a focusable area or navigable candidate with unfenced set to true whenever the user attempts to move the focus to candidate.

Modify the action of the accesskey attribute command algorithm to be:
  1. Run the focusing steps for the element with unfenced set to true.

  2. Fire a click event at the element.

Modify the behavior when a user activates a click focusable focusable area to be:

When a user activates a click focusable focusable area, the user agent must run the focusing steps on the focusable area with focus trigger set to "click" and unfenced set to true.

Modify step 10 of the hide popover algorithm to read:
  1. If previouslyFocusedElement is not null, then:

    1. Set element’s previously focused element to null.

    2. If focusPreviousElement is true, then run the focusing steps for previouslyFocusedElement with unfenced set to true; the viewport should not be scrolled by doing this step.

Note: Although dismissing a popover manually is a user-initiated gesture, the focusing steps will be called with unfenced set to false regardless of whether this was called from user gesture or via a script call.

Modify the first bullet point of step 3 of the interactively validate the constraints algorithm to read:
Add a step after step 2 of the while loop in the has focus steps algorithm that reads:
  1. If the focused area of candidate is a fenced navigable container with a non-null fenced navigable, then set candidate to the active document of that fenced navigable container's fenced navigable.

Modify step 3 of the while loop in the focus chain algorithm to read:
  1. If currentObject is a focusable area, then set currentObject to currentObject’s DOM anchor's node document.

    Otherwise, if currentObject is a Document whose node navigable's parent is non-null, then set currentObject to currentObject’s node navigable's parent.

    Otherwise, if currentObject is a Document whose node navigable is a traversable navigable whose unfenced parent is non-null, then set currentObject to currentObject’s node navigable's unfenced parent.

    Otherwise, break.

Modify the get the focusable area algorithm. Add a new case to the switch statement:
If focus target is a fenced navigable container with a non-null fenced navigable

Return the fenced navigable container's fenced navigable's active document.

Note: This algorithm can unconditionally "jump the fence" boundary because its return value always feeds into an algorithm that does carefully consider the fence boundary.

Modify step 3 of the currently focused area of a top-level traversable algorithm to read:
  1. While candidate’s focused area is either a navigable container with a non-null content navigable or a fenced navigable container with a non-null fenced navigable: set candidate to the active document of either that navigable container's content navigable or that fenced navigable container's fenced navigable, whichever is non-null.

Modify step 6 of the sequential focus navigation algorithm to read:
  1. If candidate is not null, then run the focusing steps for candidate with unfenced set to true and return.

Modify step 9 of the sequential focus navigation algorithm to read:

  1. Otherwise, starting point is a focusable area whose node document's node navigable is a child navigable or fenced navigable. Set starting point to that node navigable's unfenced parent and return to the step labeled loop.

Modify step 2 of the sequential navigation search algorithm to read:
  1. If candidate is a navigable container with a non-null content navigable, then let new candidate be the result of running the sequential navigation search algorithm with candidate’s content navigable as the first argument, direction as the second, and sequential as the third.

    If candidate is a fenced navigable container with a non-null fenced navigable, then let new candidate be the result of running the sequential navigation search algorithm with candidate’s fenced navigable as the first argument, direction as the second, and sequential as the third.

    If new candidate is null, then let starting point be candidate, and return to the top of this algorithm. Otherwise, let candidate be new candidate.

Tests

This section describes how the fencedframe element interacts with the ever-complicated process of navigation, which includes integration with various headers, isolation mechanisms, and policies.

3.8.1. The `Supports-Loading-Mode` HTTP response header

This section is intended to monkeypatch the [HTML] Standard, however since the `Supports-Loading-Mode` header has not been merged into [HTML] yet, and instead lives in the [Prerendering-Revamped] specification, this section effectively monkeypatches that monkeypatch specification.

Add the new token below to the list of valid tokens for the `Supports-Loading-Mode` response header:

The `fenced-frame` token indicates that the response can be used to create a Document inside of a fenced navigable. Without this explicit opt-in, all navigations inside of a fenced navigable will fail, as outlined in § 3.8 Navigation.

[HTML]'s attempt to populate the history entry’s document algorithm is modified such that just before the step inside the queued task starting with "If failure is true, then:", insert a new step:
  1. Otherwise, if all of the following conditions are true:

    then set failure to true.

3.8.2. COOP, COEP, and cross-origin isolation

Outside of this specification, the `Cross-Origin-Opener-Policy` header only applies to top-level traversables instead of all navigables, and this specification continues this intention insofar as this header does not have an impact on fenced navigables, nor is it inherited from its embedder. Consequently, the browsing context group hosted inside of a fenced traversable navigable will always have its cross-origin isolation mode set to "none".

Nevertheless, a fenced navigable respects its unfenced parent's embedder policy, which is accomplished below:

In the check a navigation response’s adherence to its embedder policy algorithm, rewrite all occurrences of:

with:

Note: This causes navigations inside of a fencedframe to fail if they are not served with a suitable `Cross-Origin-Embedder-Policy` header, just as iframes behave.

Determine if we need to fence or unfence the queue a cross-origin embedder policy inheritance violation algorithm, as leaving it unfenced may cause a privacy leak.

Next, we modify how the cross-origin resource policy check applies to navigation requests. Rewrite step 19, substep 13 of the create navigation params by fetching algorithm like so:
  1. If response is not a network error, navigable is a child navigable or fenced navigable, and the result of performing a cross-origin resource policy check with navigable’s unfenced container document's origin, navigable’s unfenced container document's relevant settings object, request’s destination, response, and true is blocked, then set response to a network error and break.

    Note: Here we’re running the cross-origin resource policy check against the unfenced parent navigable rather than navigable itself. This is because we care about the same-originness of the embedded content against the embedder’s context (ignoring the "fence"), not the navigation source.

Determine if we need to fence or unfence the queue a cross-origin embedder policy CORP violation report algorithm, as leaving it unfenced may cause a privacy leak.

Tests
Add the following new items to the source snapshot params struct:
initiator fenced frame config instance

a fenced frame config instance or null, initially null.

target fenced frame config

a fenced frame config or null, initially null.

attribution reporting enabled

a boolean.

attribution reporting context origin

an origin.

automatic beacons allowed

an boolean.

automatic beacon data map

a map whose keys are automatic beacon event types and whose values are null or an automatic beacon data

Note: The initiator fenced frame config instance is the fenced frame config instance that’s loaded into a navigation initiator’s browsing context, if any exists. It is used by the attempt to send an automatic beacon algorithm to compare origins and determine which FenceReportingDestinations to send beacons to, if the fencedframe-initiated navigation succeeds. The target fenced frame config on the other hand, is the non-instantiated fenced frame config that will be loaded into a fencedframe element for navigations targeting fenced frames. These fields do not interact together in any meaningful way.

To get the automatic beacon data mapping to use given a Document sourceDocument:
  1. Let automatic beacon data map be a new empty automatic beacon data map.

  2. Let current navigable be sourceDocument’s node navigable.

  3. While current navigable is not null:

    1. For each typedata of current navigable’s active document's automatic beacon data map:

      1. If automatic beacon data map[type] does not exist, set automatic beacon data map[type] to data.

      Note: This guarantees that the first ancestor that contains automatic beacon data for a specific type will be usable by the document initiating the navigation. This will also prevent an ancestor blocking a document from using data set in a higher ancestor.

    2. Set current navigable to current navigable’s parent.

  4. Return automatic beacon data map.

Modify the snapshot source snapshot params algorithm to return a source snapshot params with these additional fields:
initiator fenced frame config instance

sourceDocument’s browsing context's fenced frame config instance

attribution reporting enabled

The result of determining whether sourceDocument is allowed to use the "attribution-reporting" feature

attribution reporting context origin

sourceDocument’s context origin

automatic beacons allowed

sourceDocument’s automatic beacons allowed

automatic beacon data map

The result of running get the automatic beacon data mapping to use on sourceDocument.

Modify the definition of [HTML]'s navigate algorithm to include an extra parameter: an optional string sharedStorageContext (default null).

Modify step 7 of [HTML]'s navigate algorithm to include the following condition:

Tests

Modify step 8 of the same algorithm to include the following condition:

Tests

Insert these steps immediately after step 20, the step that goes in parallel, so that what follows are the first steps that run in parallel in the patched algorithm:

  1. If url is a urn uuid and navigable is a fenced navigable:

    1. Let config be the result of finding a config in sourceDocument’s node navigable's traversable navigable's fenced frame config mapping.

      Note: This might "wait" for an arbitrary period of time for the config associated with the urn uuid url to be "finalized" in the fenced frame config mapping. This is why this step runs in parallel. This navigation will be canceled by any subsequent embedder-initiated navigations, should they occur, by the usual mechanism that tracks the ongoing navigation.

    2. Set config’s embedder shared storage context to sharedStorageContext.

    3. Set sourceSnapshotParams’s target fenced frame config to config.

    4. Assert config’s mapped url's value is a URL whose scheme is "https".

    5. Set url to config’s mapped url's value.

Tests

Rewrite the step starting with "Let unloadPromptCanceled be the result of" to:

  1. Let unloadPromptCanceled be false if navigable is a fenced navigable, or the result of checking if unloading is user-canceled for navigable’s active document's inclusive descendant navigables otherwise.

Tests


The below patches make use of the previously-assigned target fenced frame config, instantiating it in preparation for use when the navigation is finalized.

Add a new item to the navigation params struct:
fenced frame config instance

A fenced frame config instance or null, initially null.

Note: This is only set for embedder-initiated fencedframe navigations, and if a new Document is created as a result of such a navigation, this member is transferred to the new fenced navigable's active browsing context's fenced frame config instance member.

Modify [HTML]'s create navigation params by fetching algorithm such that the last step that returns a navigation params has the following additional assignment:
fenced frame config instance

If sourceSnapshotParams’s target fenced frame config is null, then null; otherwise, the result of instantiating sourceSnapshotParams’s target fenced frame config.


Finally, the below patches make use of the navigation params's fenced frame config instance, and handle the assignment of a browsing context's fenced frame config instance that is initiated by a navigation.

Note: A browsing context's fenced frame config instance is assigned in two different ways: (1) during embedder-initiated fencedframe navigation, as described by the rest of this section, and (2) inherited during initial Document creation for Documents in child navigables whose traversable navigable is a fenced navigable, as handled in the § 3.3 Modifications to creating browsing contexts section.

For any successful navigation inside of a fencedframe, regardless of whether it was initiated by content in the fencedframe or its embedder, exactly one of two things will happen:

Modify [HTML]'s create and initialize a Document object algorithm to insert one step after step 2 that reads:
  1. If navigationParams’s fenced frame config instance is not null:

    1. Assert: browsingContext does not equal navigationParams’s navigable's active browsing context.

      Note: This is because embedder-initiated navigations (indicated by navigationParams’s fenced frame config instance being non-null) always cause a § 3.8.4 Browsing context group swap.

    2. Set browsingContext’s fenced frame config instance to navigationParams’s fenced frame config instance.

Add a new step after step 9 that reads:

  1. Let automaticBeaconsAllowed be the result of running get a structured field value on navigationParams’s response's header list given "Allow-Fenced-Frame-Automatic-Beacons" and "item".

Further rewrite step 10 (now step 12) to return a new Document with an additional parameter:

automatic beacons allowed

automaticBeaconsAllowed

TODO: Call the on navigate callback at the appropriate time.

3.8.4. Browsing context group swap

When the embedder of a fencedframe initiates navigations inside the frame, we must perform a browsing context group swap to entirely reset the context inside the frame, to ensure nothing is left over to be leaked to the next Document.

Modify [HTML]'s attempt to populate the history entry’s document algorithm. Add a step before the step that reads "6. Queue a global task on the navigation and traversal task source", that reads:
  1. If all of the following conditions are true:

    then set navigationParams’s COOP enforcement result's needs a browsing context group switch boolean to true.

    This indeed works, but we should consider using a separate mechanism to carry this out, instead of piggybacking off of the COOP mechanism which was designed without fenced frames in mind, and could evolve in ways that give this specification unwanted side-effects.

3.9. Page visibility

The Page visibility section of [HTML] is modified such that the first step of the algorithm that runs when a user-agent changes a traversable navigable's system visibility state calls the inclusive descendant navigables algorithm with unfenced set to true.

4. Interactions with other specifications

Due to the necessarily cross-cutting nature of the fencedframe element and its interactions with core concepts like navigable and browsing context group, there are a number of specifications that rely on terms whose usages must be re-evaluated in light of this specification; this section houses the various changes that we propose to other specifications.

4.1. Prerendering

The Prerendering Revamped specification defines navigable's loading mode and the values it can take on. Our specification adds another value for fenced frames:

"fencedframe"

This navigable is displaying a fencedframe's content

Specify the behavior that leads to the following:

Tests

4.2. Content Security Policy

This introductory section is non-normative.

Content Security Policy [CSP] can ordinarily be used by web content associated with a Document that hosts a navigable container to limit the source of navigations in a child navigable.

In order to prevent [CSP] from being used a communication side-channel exposing the URL of navigations inside a fencedframe to the site operating its embedder, the only source expressions that can influence a fencedframe navigation are:

See our CSP explainer that describes this.

4.2.1. Algorithms

Add a step after step 2 of the frame-src pre-request check that says:
  1. If request’s destination is "fencedframe", and this directive’s value does not contain either "https:", "https://*:*", or "*", return "Blocked".

Add a step after step 2 of the frame-src post-request check that says:
  1. If request destination is "fencedframe", and this directive’s value does not contain either "https:", "https://*:*", or "*", return "Blocked".

Next, we modify the behavior of the [CSPEE] specification. If the embedding frame specifies a required CSP, fenced frames will not load. This is done to prevent arbitrary data flow from the embedder to the fenced frame.

Add a step after step 1 in the Is response to request blocked by context’s required CSP? algorithm that reads:
  1. If context’s required csp is not null, and request destination is "fencedframe", return "Blocked".

4.2.2. New fenced-frame-src [CSP] directive

Since fencedframe is a different element than iframe, using the frame-src directive wouldn’t give web sites enough control over their CSP rules. Introduce a new [CSP] directive: fenced-frame-src. The monkey-patched specification is printed below:

The fenced-frame-src directive restricts the URLs which may be loaded into a fencedframe's fenced navigable. The syntax for the directive’s name and value is described by the following ABNF:
directive-name  = "fenced-frame-src"
directive-value = serialized-source-list
Given a page with the following Content Security Policy:
Content-Security-Policy: fenced-frame-src https://example.com/

Fetches for the following code will return a network error, as the URL provided does not match fenced-frame-src’s source list:

<fencedframe src="https://example.org/">
</fencedframe>

The Pre-request check and Post-request check will be the same as the frame-src’s check.

The default-src directive’s Example 7 and Example 8 will be amended. Where it says:
Content-Security-Policy: connect-src 'self';
                        ...
                        worker-src 'self'

It will now say:

Content-Security-Policy: connect-src 'self';
                        ...
                        fenced-frame-src 'self';
                        ...
                        worker-src 'self'
In the directive fallback list, in step 1, add a new entry to the list:
"fenced-frame-src"
  1. Return << "fenced-frame-src", "frame-src", "child-src", "default-src" >>.

Modify the switch on step 3 of the Get the effective directive for request algorithm to include the following case:
"fencedframe"
  1. Return fenced-frame-src.

Tests

4.3. Permissions Policies

This introductory sub-section is non-normative.

The policy-controlled features available to Documents inside of a fencedframe are determined exclusively by the fenced frame config that the fencedframe navigates to. Specifically, the fenced frame config's 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 fenced frame config instantiates a fenced frame config instance that is stored on the browsing context in the fenced navigable. This browsing context’s fenced frame config instance's effective enabled permissions is consulted during navigation. A fencedframe navigation can only succeed if the permissions policy for the navigation’s resulting Document has an inherited policy such that the inherited policy value is "Enabled" for each feature in the 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 origin (since the origin is derived from cross-site data), this specification modifies various [PERMISSIONS-POLICY] algorithms such that a fencedframe Document's inherited policy is computed without consideration of whether its 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:

The patches in the below section "fence" the appropriate [PERMISSIONS-POLICY] and [HTML] algorithms to achieve the outcomes described in the above explanatory content.

4.3.1. 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: allow; 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 src attribute, or the fencedframe’s fenced frame config.

When not empty, the iframe's allow or fencedframe's allow attribute will result in adding an allowlist for each supported feature to the iframe or fencedframe's container policy, when it is constructed.

Create a new algorithm, called Derive a permissions policy directly from a fenced frame config instance.

Given null or an element (container), this algorithm returns a new permissions policy.

  1. Let inherited policy be a new ordered map.

  2. Let effective permissions be an empty list.

  3. Let fenced frame config be container’s node document's active browsing context's fenced frame config instance.

  4. If fenced frame config is not null, and fenced frame config’s effective enabled permissions are not null, set effective permissions to fenced frame config’s effective enabled permissions.

  5. For each feature supported:

    1. If effective permissions contains feature, then set inherited policy[feature] to "Enabled".

      Otherwise, set inherited policy[feature] to "Disabled".

  6. Let policy be a new permissions policy, with inherited policy inherited policy and declared policy a new ordered map.

  7. Return policy.

Note: While this algorithm doesn’t take the allow attribute into consideration, it will have already been checked by the time this is called because of Should navigation response to navigation request be blocked by Permissions Policy?. Any policy specified in allow that is too restrictive would have cause the fenced frame to not load, and any policy that is more permissive than what is specified in the effective enabled permissions will be ignored.

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 3 to read:

  1. 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-fenced navigable container (container), an origin (origin), and a response (response), this algorithm returns a new permissions policy.

Rewrite step 1 to read:

  1. If container is a fenced navigable container, then let policy be the result of running derive a permissions policy directly from a fenced frame config instance given container.

    Otherwise, Let policy be the result of running Create a Permissions Policy for a navigable given container and origin.

Modify step 1 of the Process permissions policy attributes algorithm to read:
  1. If element is not an iframe element or a fencedframe element, then return an empty policy directive.

Modify [HTML]'s attempt to populate the history entry’s document algorithm. Add a step before the step inside the queued task starting with "If failure is true, then:" that reads:
  1. 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 navigable.

  2. If navigable is not a fenced navigable, then return "Allowed".

  3. Let origin be navigationParams’s origin.

  4. Let effective permissions be the navigable’s active browsing context's fenced frame config instance's effective enabled permissions.

    Per work omitted in pull request #84, the config instance has not yet been assigned to the browsing context. We should consider storing the instance inside navigationParams and reference it from here instead.

  5. Let permissions policy be the result of creating a permissions policy given navigable’s fenced navigable container, origin, and fenced set to true.

    Note: This is almost identical to the permissions policy that will be created when the navigation constructs the ultimate Document for this pending navigation. The difference is that this algorithm, just like when it is called on iframes, will include all of the permissions specified in the allow attribute, even if that permission isn’t specified in the fenced frame config's effective enabled permissions. 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 and unconditionally installed on the Document. However, the recreation will not include any additional enabled permissions that are not included in the effective enabled permissions, effectively locking the enabled permissions to only what is specified in effective enabled permissions.

  6. Let inherited policy be permissions policy’s inherited policy.

  7. For each effective permission of effective permissions:

    1. If inherited policy[effective permission] is "Disabled", return "Blocked".

  8. 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 inherited policy for that feature.

Rewrite step 3 to read:

  1. If the result of executing Is feature enabled in document for origin? on feature, container’s 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 document, not the Document hosted inside container.

Rewrite step 7 to read:

  1. If fenced is false, feature’s default allowlist is 'self', and origin is same origin with container’s 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 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:

  1. If feature is present in policy’s declared policy,

    1. If fenced is false, and the allowlist for feature in policy’s declared policy matches origin, then return "Enabled".

    2. Otherwise, if fenced is 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:

  1. If fenced is false, feature’s default allowlist is 'self', and origin is same origin with document’s origin, return "Enabled".

Tests

4.4. CSSOM View

The [CSSOM-VIEW] specification defines the scrollIntoView() method that calls the scroll a target into view algorithm. This will not only scroll the Element or viewport to make the target visible, but will also scroll ancestors if necessary to make the target visible, essentially causing the scroll to "bubble up". This means that scrollIntoView() performed in a child navigable or fenced navigable can be observed by its embedder, allowing for collusion across a fenced frame boundary. This section patches the scroll a target into view algorithm to prevent that collusion at the expense of some utility.

Modify the scroll a target into view algorithm to add a step at the end of the algorithm that reads:
  1. If scrolling box’s associated Element's associated Document's node navigable's traversable navigable is a fenced navigable, or if scrolling box’s associated viewport's associated Document's node navigable's traversable navigable is a fenced navigable, then let this be the last instance of this algorithm that stops any further recursive instances that would otherwise follow.

Note: This allows scrolling to "bubble up" to a fenced frame boundary, but not cross it.

Tests

5. Security & Privacy Considerations

This material is being upstreamed from our explainer into this specification, and in the meantime you can consult the following resources:

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ATTRIBUTION-REPORTING-API]
Attribution Reporting. Draft Community Group Report. URL: https://wicg.github.io/attribution-reporting-api/
[CSP]
Mike West; Antonio Sartori. Content Security Policy Level 3. URL: https://w3c.github.io/webappsec-csp/
[CSPEE]
Mike West. Prerendering Revamped. CG-DRAFT. URL: https://w3c.github.io/webappsec-cspee/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
[CSS22]
Bert Bos. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification. URL: https://drafts.csswg.org/css2/
[CSSOM-VIEW]
Simon Pieters. CSSOM View Module. URL: https://drafts.csswg.org/cssom-view/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[Prerendering-Revamped]
Domenic Denicola; Dominic Farolino. Prerendering Revamped. CG-DRAFT. URL: https://wicg.github.io/nav-speculation/prerendering.html
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. Referrer Policy. URL: https://w3c.github.io/webappsec-referrer-policy/
[RFC8941]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. February 2021. Proposed Standard. URL: https://httpwg.org/specs/rfc8941.html
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[Protected-Audience]
Paul Jensen. Protected Audience API. CG-DRAFT. URL: https://wicg.github.io/turtledove/
[Shared-Storage]
Cammie Barnes. Shared Storage API. CG-DRAFT. URL: https://wicg.github.io/shared-storage/

IDL Index

[Exposed=Window]
interface HTMLFencedFrameElement : HTMLElement {
  [HTMLConstructor] constructor();

  [CEReactions] attribute FencedFrameConfig? config;
  [CEReactions] attribute DOMString width;
  [CEReactions] attribute DOMString height;
  [CEReactions] attribute DOMString allow;
};

enum OpaqueProperty {"opaque"};

typedef (unsigned long or OpaqueProperty) FencedFrameConfigSize;
typedef USVString FencedFrameConfigURL;

[Exposed=Window, Serializable]
interface FencedFrameConfig {
  readonly attribute FencedFrameConfigSize? containerWidth;
  readonly attribute FencedFrameConfigSize? containerHeight;
  readonly attribute FencedFrameConfigSize? contentWidth;
  readonly attribute FencedFrameConfigSize? contentHeight;

  undefined setSharedStorageContext(DOMString contextString);
};

typedef (USVString or FencedFrameConfig) UrnOrConfig;

partial interface Navigator {
  Promise<undefined> deprecatedReplaceInURN(
    UrnOrConfig urnOrConfig, record<USVString, USVString> replacements);
};

enum FenceReportingDestination {
  "buyer",
  "seller",
  "component-seller",
  "direct-seller",
  "shared-storage-select-url",
};

dictionary FenceEvent {
  // This dictionary has two mutually exclusive modes that aren’t represented as
  // distinct IDL types due to distinguishability issues:
  //
  // When reporting to a preregistered destination (specified by enum), the following
  // properties are used:
  DOMString eventType;
  DOMString eventData;
  sequence<FenceReportingDestination> destination;
    
  // When setting event data to be used later in an automatic beacon, the
  // following properties are used:
  boolean once = false;
  boolean crossOriginExposed = false;

  // When reporting to a custom destination URL (with substitution of macros defined by
  // the Protected Audience buyer), the following property is used:
  USVString destinationURL;
};

typedef (FenceEvent or DOMString) ReportEventType;

[Exposed=Window]
interface Fence {
    undefined reportEvent(optional ReportEventType event = {});
    undefined setReportEventDataForAutomaticBeacons(optional FenceEvent event = {});
    sequence<FencedFrameConfig> getNestedConfigs();
};

partial interface Window {
  // Collection of fenced frame APIs
  readonly attribute Fence? fence;
};

Issues Index

It’s not necessary to call the URL and history update steps as we do during usual child navigable creation or top-level traversable creation, but we still need a mechanism to initialize History.length in the new navigable. This is an existing issue in the HTML Standard: https://github.com/whatwg/html/issues/9030.
The default is "all", so we technically don’t have to set anything here. We do in order to remind ourselves that it might be more appropriate to skip service workers here, like some other beacons.
Determine if we need to fence or unfence the queue a cross-origin embedder policy inheritance violation algorithm, as leaving it unfenced may cause a privacy leak.
Determine if we need to fence or unfence the queue a cross-origin embedder policy CORP violation report algorithm, as leaving it unfenced may cause a privacy leak.
This indeed works, but we should consider using a separate mechanism to carry this out, instead of piggybacking off of the COOP mechanism which was designed without fenced frames in mind, and could evolve in ways that give this specification unwanted side-effects.
Specify the behavior that leads to the following:
Per work omitted in pull request #84, the config instance has not yet been assigned to the browsing context. We should consider storing the instance inside navigationParams and reference it from here instead.