Controlled Frame API

Draft Community Group Report,

This version:
https://wicg.github.io/controlled-frame/
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google LLC)
(Google LLC)
(Google LLC)

Abstract

This document defines an API for embedding arbitrary web content only within the context of an Isolated Web Application (IWA). The embedded content is a new top-level browsing context within and controlled by the embedder.

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 specification describes a content embedding API that satisfies some critical use cases for IWAs that iframe does not support. This embedding environment should allow embedding all content without express permission from the embedded site, including content which iframe cannot embed, and provide embedding sites more control over that embedded content.

Since this is a particularly powerful API, its use and availability makes an app a target of various types of hacking. As a result, this API is limited to use in Isolated Web Applications (IWAs) which have addtional safeguards in place to protect users and developers. IWAs are not a normal web application and can exist only at a special 'isolated-app:' scheme. This means by design that this API will not be available to normal web pages.

Note: This API is not intended to be a replacement or substitute for iframe. All iframe use cases are still valid and should continue to use iframe, including IWAs where possible.

2. The Fenced Frame specification

For convenience, the Controlled Frame specification assumes that the Fenced Frame specification is in place. There are concepts introduced in the Fenced Frame specification, such as nested top-level traversables, that are broadly useful to refer to in the context of Controlled Frame.

The Fenced Frame specification achieves defining these concepts via monkey patching some specifications, such as HTML. We will also require monkey patching specifications for some parts of this Controlled Frame specification.

3. The controlledframe 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
src — Content source URL to embed
partition — Partition name to hold data related to this content
Accessibility considerations:
For authors.
For implementers.

Note: These link to the accessibility definitions of the iframe element. From an accessibility perspective, a controlledframe should behave the same as an iframe.

DOM interface:
[Exposed=Window, IsolatedContext]
interface HTMLControlledFrameElement : HTMLElement {
    [HTMLConstructor] constructor();

    [CEReactions] attribute USVString src;
    attribute DOMString partition;

    readonly attribute WindowProxy? contentWindow;
    readonly attribute ContextMenus contextMenus;
    readonly attribute WebRequest request;

    // Navigation methods.
    Promise<boolean> back();
    Promise<boolean> canGoBack();
    Promise<boolean> forward();
    Promise<boolean> canGoForward();
    Promise<boolean> go(long relativeIndex);
    undefined reload();
    undefined stop();

    // Scripting methods.
    Promise<undefined> addContentScripts(sequence<ContentScriptDetails> contentScriptList);
    Promise<any> executeScript(optional InjectDetails details = {});
    Promise<undefined> insertCSS(optional InjectDetails details = {});
    Promise<undefined> removeContentScripts(optional sequence<DOMString> scriptNameList);

    // Configuration methods.
    Promise<undefined> clearData(
      optional ClearDataOptions options = {},
      optional ClearDataTypeSet types = {});
    Promise<boolean> getAudioState();
    Promise<long> getZoom();
    Promise<DOMString> getZoomMode();
    Promise<boolean> isAudioMuted();
    undefined setAudioMuted(boolean mute);
    Promise<undefined> setZoom(long zoomFactor);
    Promise<undefined> setZoomMode(DOMString zoomMode);

    // Capture methods.
    Promise<undefined> captureVisibleRegion(optional ImageDetails options = {});
    undefined print();

    // Events.
    attribute EventHandler onconsolemessage;
    attribute EventHandler oncontentload;
    attribute EventHandler ondialog;
    attribute EventHandler onloadabort;
    attribute EventHandler onloadcommit;
    attribute EventHandler onloadstart;
    attribute EventHandler onloadstop;
    attribute EventHandler onnewwindow;
    attribute EventHandler onpermissionrequest;
    attribute EventHandler onsizechanged;
    attribute EventHandler onzoomchange;
};

The controlledframe element represents its embedded navigable.

Descendents of controlledframe elements represent nothing.

The Controlled Frame element is exposed to any Document with the "controlled-frame" policy-controlled feature whose environment settings object is an isolated context.

The IDL attributes src and partition must reflect the respective content attributes of the same name.

Each controlledframe has an associated:

The contentWindow getter steps are to return this’s contentWindow.

The request getter steps are to return this’s request.

The contextMenus getter steps are to return this’s contextMenus.

When a controlledframe element element is inserted into a document whose browsing context is non-null, run the following steps:
  1. If element’s src is not empty, then:

    1. Initialize a controlledframe given element.

When a controlledframe element element is removed from a document, run the following steps:
  1. Destroy a top-level traversable given element’s embedded navigable.

  2. Set element’s embedded navigable to null.

To initialize a controlledframe element element, run the following steps:
  1. Assert that element’s embedded navigable is null.

  2. Let group be a new browsing context group.

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

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

  5. Let traversable be a new traversable navigable.

  6. Initialize the navigable traversable given documentState.

  7. Set traversable’s embedderParent to element.

  8. Set element’s embedded navigable to traversable.

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

  10. Set initialHistoryEntry’s step to 0.

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

    These steps are needed to initialize History.length in the new navigable. This is an existing issue in the HTML Standard.

  12. Set element’s contentWindow to document’s WindowProxy.

  13. Navigate a controlledframe given element and element’s src.

To navigate a controlledframe element element given a USVString urlString, run the following steps:
  1. If urlString is not an absolute-URL string, return.

  2. Let url be the result of parsing a URL given urlString and element’s node document.

  3. Let historyHandling be "auto".

  4. If element’s embedded navigable’s active document is not completely loaded, then set historyHandling to "replace".

  5. Navigate element’s embedded navigable to url using element’s node document, with a NavigationHistoryBehavior of historyHandling.

The HTMLControlledFrameElement() constructor steps are:
  1. Set this’s request to a new WebRequest.

  2. Set this’s contextMenus to a new ContextMenus.

3.1. Common infrastructure

To resolve an embedder promise given a controlledframe controlledframe, a promise promise, and an optional value, run the following steps:
  1. Queue a global task on the DOM manipulation task source of controlledframe’s relevant global object to resolve promise with value.

    Note: The relevant global object of controlledframe is the embedder’s Window object.

To reject an embedder promise given a controlledframe controlledframe, a promise promise, and an optional value, run the following steps:
  1. Queue a global task on the DOM manipulation task source of controlledframe’s relevant global object to reject promise with value.

    Note: The relevant global object of controlledframe is the embedder’s Window object.

A URL url matches a URLPattern pattern if the following steps return true:
  1. Let result be the result of match given pattern, and url.

  2. If result is null, then return false.

  3. Return true.

3.2. Attributes

The partition attribute takes an identifier specifying where data related to the Controlled Frame’s instance should be stored. The identifier is composed of a string of alphanumeric characters. All data for the embedded navigable must be stored in a storage shelf keyed by this partition string along with the origin that created the data.

By default, all data stored must be held in an in-memory storage partition so that when the last Controlled Frame element with a given partition value is destroyed, the data is also destroyed. While the data is held in this partition, no data from the Controlled Frame’s embedded navigable shall persist.

If the partition attribute identifier contains the prefix "persist:", the user agent must use a disk-based storage environment rather than an in-memory storage partition. Embedded content should not be able to detect whether its storage is in-memory or persistent.

If multiple Controlled Frames share the same partition identifier, all of their embedded navigable instances must share the same storage partition.

Note: The [STORAGE] specification is monkey patched below to partition storage based on the value of the partition attribute.

The partition IDL attribute setter steps are:
  1. If this’s embedded navigable is not null, then:

    1. Throw a "NotSupportedError" DOMException.

  2. Set this’s partition to the given value.

The src attribute reflects the Controlled Frame’s embedded navigable’s current session history entry’s URL.

The src IDL attribute setter steps are:
  1. If this is not in a document tree, then return.

  2. If this’s embedded navigable is null, then:

    1. Initialize a controlledframe given this.

  3. Otherwise:

    1. Navigate a controlledframe given this and the given value.

3.3. Navigation methods

back()

Goes back one step in the overall session history entries list for the traversable navigable in the Controlled Frame.

Returns a promise that resolves to true if the page was successfully navigated back, or false if the navigation failed or there was no previous step.

canGoBack()

Returns a promise that resolves to true if the current session history entry is not the first one in the embedded navigable’s session history entries. This means that there is a previous session history entry for the navigable.

forward()

Goes forward one step in the overall session history entries list for the traversable navigable in the Controlled Frame.

Returns a promise that resolves to true if the page was successfully navigated forward, or false if the navigation failed or there was no next step.

canGoForward()

Returns a promise that resolves to true if the current session history entry is not the last one in the embedded navigable’s session history entries. This means that there is a next session history entry for the navigable.

go()

Reloads the current page.

go(relativeIndex)

Goes back or forward relativeIndex number of steps in the overall session history entries list for the current traversable navigable.

A zero relative index will reload the current page.

Returns a promise that resolves to true if the page was successfully navigated, or false if the navigation failed or the provided relative index was out of range.

reload()

Reloads the current page.

stop()

Cancels the document load.

To delta traverse an embedded navigable’s history, given a controlledframe controlledframe and an integer delta, run the following steps:
  1. Let resultPromise be a new promise.

  2. Return resultPromise and run the remaining steps in parallel.

  3. Let embeddedNavigable be controlledframe’s embedded navigable.

  4. If embeddedNavigable is null, then resolve an embedder promise given controlledframe, resultPromise, and false, and abort these steps.

  5. If embeddedNavigable’s active document is not fully active, then resolve an embedder promise given controlledframe, resultPromise, and false, and abort these steps.

  6. Append the following session history traversal steps to embeddedNavigable:

    1. Let allSteps be the result of getting all used history steps for embeddedNavigable.

    2. Let currentStepIndex be the index of embeddedNavigable’s current session history step within allSteps.

    3. Let targetStepIndex be currentStepIndex + delta.

    4. If allSteps[targetStepIndex] does not exist, then resolve an embedder promise given controlledframe, resultPromise, and false, and abort these steps.

    5. Let result be the result of applying the traverse history step allSteps[targetStepIndex] to embeddedNavigable given a user navigation involvement of "none".

    6. If result is not equal to "applied", then resolve an embedder promise given controlledframe, resultPromise, and false.

    7. Otherwise, resolve an embedder promise given controlledframe, resultPromise, and true.

The canGoBack() method steps are:
  1. Let result be a new promise.

  2. Let controlledframe be this.

  3. Let embeddedNavigable be controlledframe’s embedded navigable.

  4. If embeddedNavigable is null, then resolve result with false and return result.

  5. Queue a global task on the navigation and traversal task source of embeddedNavigable’s node document’s relevant global object that will run the following steps:

    1. Let canGoBack be true if embeddedNavigable’s current session history step is > 0, false otherwise.

    2. Resolve an embedder promise given controlledframe, result, and canGoBack.

  6. Return result.

The canGoForward() method steps are:
  1. Let result be a new promise.

  2. Let controlledframe be this.

  3. Let embeddedNavigable be controlledframe’s embedded navigable.

  4. If embeddedNavigable is null, then resolve result with false and return result.

  5. Queue a global task on the navigation and traversal task source of embeddedNavigable’s node document’s relevant global object that will run the following steps:

    1. Let step be embeddedNavigable’s current session history step.

    2. Let steps be the result of getting all used history steps given embeddedNavigable.

    3. Let canGoForward be true if step + 1 < the size of steps, false otherwise.

    4. Resolve an embedder promise given controlledframe, result, and canGoForward.

  6. Return result.

The back() method steps are:
  1. Return the result of delta traverse an embedded navigable’s history given this and -1.

The forward() method steps are:
  1. Return the result of delta traverse an embedded navigable’s history given this and 1.

The go(relativeIndex) method steps are:
  1. Return the result of delta traverse an embedded navigable’s history given this and relativeIndex.

The reload() steps are:
  1. Let embeddedNavigable be this’s embedded navigable.

  2. If embeddedNavigable is null, return.

  3. Queue a global task on the navigation and traversal task source of embeddedNavigable’s node document’s relevant global object that will reload embeddedNavigable given a user navigation involvement of "none".

The stop() steps are:
  1. Let embeddedNavigable be this’s embedded navigable.

  2. If embeddedNavigable is null, return.

  3. Queue a global task on the navigation and traversal task source of embeddedNavigable’s node document’s relevant global object that will stop loading embeddedNavigable.

3.4. Scripting methods

// One of |code| or |file| must be specified but not both.
dictionary InjectDetails {
  DOMString code;
  USVString file;
};

dictionary InjectionItems {
  DOMString code;
  sequence<USVString> files;
};

enum RunAt {
  "document-start",
  "document-end",
  "document-idle",
};

dictionary ContentScriptDetails {
  required DOMString name;
  InjectionItems js;
  InjectionItems css;
  required sequence<(URLPattern or URLPatternInput)> urlPatterns;
  sequence<(URLPattern or URLPatternInput)> excludeURLPatterns;
  boolean allFrames;
  boolean matchAboutBlank;
  RunAt runAt;
};

A content script config is a struct with the following items:

pendingFetchCount

a long representing the number of pending script/style fetches.

js

a list of DOMStrings containing JavaScript that will be injected into a document.

css

a list of DOMStrings containing CSS that will be injected into a document.

urlPatterns

a list of URLPatterns.

Note: Content may be injected into a document if its URL matches any of these patterns.

excludeURLPatterns

a list of URLPatterns.

Note: Content will not be injected into a document if its URL matches any of these patterns. This overrides urlPattern; if both lists have entries that a document’s URL matches, the content will not be injected into the document.

allFrames

a boolean indicating whether content should be injected into all frames in a page, or just the top-level frame.

matchAboutBlank

a boolean indicating whether content should be injected into about:blank pages.

runAt

a RunAt indicating when JavaScript content should be executed in a document’s lifecycle.

To fetch an injection item given a controlledframe controlledframe, a USVString urlString, a boolean isCss, a long index, and an algorithm completionSteps that takes a long, a boolean, and a DOMString, run the following steps:

Note: Fetch a classic script cannot be used here because the fetch needs to use controlledframe’s relevant settings object, but the classic script is executed using the embedded navigable’s active document’s relevant settings object.

  1. If urlString is not an valid URL string, then:

    1. Run completionSteps given 0, false, and "".

    2. Return.

  2. Let request be a new request with the following fields:

    URL

    The result of parsing a URL given urlString and controlledframe’s node document.

    method

    "GET"

    destination

    "style" if isCss is true, "script" otherwise

    client

    controlledframe’s relevant settings object

    mode

    "cors"

  3. Fetch request, with processResponseConsumeBody set to the following steps given a response response and a null, failure, or byte sequence contents:

    1. If response’s status is not 200, or contents is null or failure, then run completionSteps given 0, false, and ""

    2. Otherwise, run completionSteps given index, true, and contents.

To validate and resolve ContentScriptDetails given a controlledframe controlledframe and a ContentScriptDetails details, run the following steps:
  1. Let result be a new promise.

  2. If details["js"] and details["css"] are both defined, or both undefined, then reject result with a TypeError and return it.

  3. If details["urlPatterns"] is empty, then reject result with a TypeError and return it.

  4. Let isCss be a boolean equal to true if details["css"] is defined, false otherwise.

  5. If isCss is true and details["runAt"] does not equal "document-start", then reject result with a TypeError and return it.

  6. Let injectionItems be details["css"] if isCss is true, details["js"] otherwise.

  7. If injectionItems["code"] and injectionItems["files"] are both defined, or both undefined, reject result with a TypeError and return it.

  8. Return result and run the remaining steps in parallel.

  9. Let config be a new content script config with the following values:

    pendingFetchCount

    0

    urlPatterns

    «»

    excludeURLPatterns

    «»

    allFrames

    details["allFrames"] if defined, otherwise false

    matchAboutBlank

    details["matchAboutBlank"] if defined, otherwise false

    runAt

    details["runAt"] if defined, otherwise document-idle

  10. For each urlPattern in details["urlPatterns"]:

    1. If urlPattern is a URLPattern, then append urlPattern to config’s urlPatterns.

    2. Otherwise, append a new URLPattern given urlPattern to config’s urlPatterns.

  11. If details["excludeURLPatterns"] is defined, then:

    1. For each urlPattern in details["excludeURLPatterns"]:

      1. If urlPattern is a URLPattern, then append urlPattern to config’s excludeURLPatterns.

      2. Otherwise, append a new URLPattern given urlPattern to config’s excludeURLPatterns.

  12. Let completionSteps be the following algorithm, which takes a long index, a boolean success, and a DOMString source:

    1. If success is false, then reject an embedder promise given controlledframe, result, and TypeError, and abort these steps.

    2. If isCss, then:

      1. Set config’s css[index] to source.

    3. Otherwise:

      1. Set config’s js[index] to source.

    4. Decrement config’s pendingFetchCount.

    5. If config’s pendingFetchCount is greater than 0, then return.

    6. Set controlledframe’s content script map[details[name]] to config.

    7. Resolve an embedder promise given controlledframe and result.

  13. If injectionItems["code"] is defined, then:

    1. Run completionSteps given 0, true, and injectionItems ["code"].

  14. Otherwise:

    1. If injectionItems["files"] is empty, then reject an embedder promise given controlledframe, result, and TypeError, and abort these steps.

    2. For each urlString of injectionItems ["files"]:

      1. Run fetch an injection item given controlledframe, urlString, isCss, config’s pendingFetchCount, and completionSteps.

      2. Increment config’s pendingFetchCount.

To determine if a content script config applies to a document given a content script config config, a URL url, and a boolean isTopLevel, run the following steps:
  1. If isTopLevel is false, and config’s allFrames is false, then return false.

  2. If url matches about:blank and config’s matchAboutBlank is false, then return false.

  3. Let urlString be the result of serializing url.

  4. Let match be false.

  5. For each pattern of config’s urlPatterns:

    1. If urlString matches a URLPattern pattern, then set match to true.

  6. For each pattern of config’s excludeURLPatterns:

    1. If urlString matches a URLPattern pattern, then set match to false.

  7. Return match.

To inject content scripts into a document given a Document document, and a RunAt currentPhase, run the following steps:
  1. Let embeddedNavigable be document’s node navigable’s traversable navigable.

  2. If embeddedNavigable is null or its embedderParent is null, then return.

  3. Let controlledframe be embeddedNavigable’s embedderParent.

  4. Let url be document’s URL.

  5. Let isTopLevel be true if document’s node navigable’s parent is null, false otherwise.

  6. For each config of controlledframe’s content script map:

    1. If the result of determining whether a content script config applies to a document given config, url, and isTopLevel equals false, then continue.

    2. If currentPhase is equal to document-start and config’s css is not empty, then:

      1. For each styleSource of config’s css, run inject a stylesheet into a document given document and styleSource.

    3. Otherwise, if currentPhase equals config’s runAt, then:

      1. For each scriptSource of config’s js, run inject a script into a document given document, scriptSource, and an empty algorithm.

To inject a stylesheet into a document given a Document document and a DOMString styleSource, run the following steps:
  1. Let styleSheet be a new CSS style sheet object.

  2. Synchronously replace the rules of a CSSStyleSheet given styleSheet and styleSource.

  3. Queue a global task on the DOM manipulation task source of document’s global object to add a CSS style sheet given document and styleSheet.

The following algorithm executes script in a Document’s environment, but that isn’t the desired behavior. The goal, which cannot be specced with the current HTML specification infrastructure, is for this algorithm to execute script in an environment that is isolated from the Document’s environment with a different global object, but with shared access to the DOM. This execution environment is called an Isolated World in Blink, which uses it to execute content scripts in extensions. See this diagram for additional details about their execution model. Gecko uses a similar approach called Xray vision. This algorithm should eventually describe a speccable implementation of this isolation that can be implemented by all browsers.

To inject a script into a document given a Document document, a DOMString scriptSource, and an algorithm completionSteps that takes a completion record, run the following steps:

Note: document.currentScript is intentionally not set while executing scriptSource.

  1. Let script be the result of creating a classic script given scriptSource, document’s relevant settings object, document’s URL, and the default script fetch options.

  2. Queue a global task on the DOM manipulation task source of document’s global object that will run the following steps:

    1. Let completionRecord be the result of running a classic script given script.

    2. Let controlledframe be document’s node navigable’s traversable navigable’s embedderParent.

    3. Queue a global task on the DOM manipulation task source of controlledframe’s relevant global object that will run completionSteps with completionRecord.

The addContentScripts(contentScriptList) method steps are:
  1. If contentScriptList is empty, return a new promise that is rejected with a TypeError.

  2. Let promises be an empty list.

  3. For each contentScript in contentScriptList:

    1. Let promise be the result of calling validate and resolve ContentScriptDetails given contentScript.

    2. Append promise to promises.

  4. Return the result of getting a promise for waiting for all promises given promises.

The removeContentScripts(scriptNameList) method steps are:
  1. Let result be a new promise.

  2. Let controlledframe be this.

  3. Return result and run the remaining steps in parallel.

  4. If scriptNameList is undefined, then:

    1. Clear controlledframe’s content script map.

  5. Otherwise, for each name of scriptNameList:

    1. Remove controlledframe’s content script map[name].

  6. Resolve an embedder promise given controlledframe and result.

The executeScript(details) method steps are:
  1. Let result be a new promise.

  2. Let controlledframe be this.

  3. Return result and run the remaining steps in parallel.

  4. If controlledframe’s embedded navigable is null, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

  5. If details["code"] and details"[file"] are either both defined or both undefined, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

  6. Let executionSteps be the following algorithm that takes a long and a DOMString or boolean scriptString:

    1. If scriptString is not a DOMString, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

    2. Let document be controlledframe’s embedded navigable’s active document.

    3. Inject a script into a document given document, scriptString, and the following algorithm that accepts a completion record completionRecord:

      1. If completionRecord is a normal completion, then:

        1. Resolve an embedder promise given controlledframe, result, and completionRecord.[[Value]].

      2. Otherwise:

        1. Reject an embedder promise given controlledframe, result, and completionRecord.[[Value]].

  7. If details["code"] is defined, then run executionSteps given 0 and details["code"].

  8. Otherwise, fetch an injection item given controlledframe, details["file"], false, 0, and executionSteps.

The insertCSS(details) method steps are:
  1. Let result be a new promise.

  2. Let controlledframe be this.

  3. Return result and run the remaining steps in parallel.

  4. If controlledframe’s embedded navigable is null, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

  5. If details["code"] and details["file"] are either both defined or both undefined, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

  6. Let executionSteps be the following algorithm that takes a long, a boolean success, and a DOMString styleString:

    1. If success is false, then reject an embedder promise given controlledframe, result, and a TypeError, and abort these steps.

    2. Let document be controlledframe’s embedded navigable’s active document.

    3. Inject a stylesheet into a document given document and styleString.

    4. Resolve an embedder promise given controlledframe and result.

  7. If details["code"] is defined, then run executionSteps given 0, true, and details["code"].

  8. Otherwise, fetch an injection item given controlledframe, details["file"], false, 0, and executionSteps.

3.5. Configuration methods

dictionary ClearDataOptions {
  long since;
};

dictionary ClearDataTypeSet {
  boolean cache;
  boolean cookies;
  boolean fileSystems;
  boolean indexedDB;
  boolean localStorage;
  boolean persistentCookies;
  boolean sessionCookies;
};
To empty a storage bottle given a storage bucket bucket, a storage identifier identifier, and a long since, run the following steps:

Note: If possible, the user agent should only remove data that has been last used after since, which represents a timestamp in milliseconds since epoch. Not all user agents track write or access time for all browsing data. Implementations should respect since to the best of their ability, but the API does not guarantee its availability.

  1. Let bottle be bucket[identifier].

  2. Clear bottle’s map.

  3. Set bottle’s proxy map reference set to a new set.

The clearData(options, types) method steps are:
  1. Let resultPromise be a new promise.

  2. Let controlledframe be this.

  3. Return resultPromise and run the remaining steps in parallel.

  4. Let clearSince be 0.

  5. If options["since"] is defined, set clearSince to options|["since"].

  6. Let embeddingOrigin be the top-level origin of controlledframe’s relevant settings object.

  7. Let partition be controlledframe’s partition.

  8. For each storageKeyshelf of the user agent’s storage shed:

    1. If storageKey’s embedding origin is not equal to embeddingOrigin or storageKey’s partition is not equal to partition, then continue.

    2. Let bucket be shelf["default"].

    3. If types["fileSystems"] is true:

      1. Empty a storage bottle given bucket, "fileSystem", and clearSince.

    4. If types["indexedDB"] is true:

      1. Empty a storage bottle given bucket, "indexedDB", and clearSince.

    5. If types["localStorage"] is true:

      1. Empty a storage bottle given bucket, "localStorage", and clearSince.

  9. If types["cookies"], types["persistentCookies"], or types["sessionCookies"] are true, then:

    The [COOKIES] specification does not support partitioning cookies through a mechanism like storage keys. The following steps will clear all cookies, but the intention is to only delete cookies created by content within a Controlled Frame with this Controlled Frame’s current partition.

    1. For each cookie in the user agent’s cookie store:

      1. If cookie’s persistent-flag is set, then:

        1. If types["cookies"] and types["persistentCookies"] are false, then continue.

      2. Otherwise:

        1. If types["cookies"] and types["sessionCookies"] are false, then continue.

      3. If cookie’s last-access-time represented as milliseconds since the epoch is less than clearSince, then continue.

      4. Remove cookie from the user agent’s cookie store.

  10. If types["cache"] is true, then:

    1. For each storageKeycache that the user agent manages: [HTTP-CACHING]

      1. If storageKey’s embedding origin is not equal to embeddingOrigin or storageKey’s partition is not equal to partition, then continue.

      2. Clear cache.

  11. Resolve an embedder promise given controlledframe and resultPromise.

Each embedded navigable holds a muted boolean, which defaults to false. When muted is true, the user agent must mute all audio streams originating from within the embedded navigable. The muted state should not be exposed to content within the embedded navigable; when muted is true, volume state should not be changed in any script-visible way, but the underlying audio streams should not be audible to users.

The getAudioState() method steps are:
  1. Let resultPromise be a new promise.

  2. Let controlledframe be this.

  3. Return resultPromise and run the remaining steps in parallel.

  4. If controlledframe’s embedded navigable is null, then reject an embedder promise given controlledframe, resultPromise, and a TypeError, and abort these steps.

  5. Let playingAudio be true if any content, including in nested frames, within controlledframe’s embedded navigable is currently playing audio, false otherwise.

  6. Resolve an embedder promise given controlledframe, resultPromise, and playingAudio.

The isAudioMuted() method steps are:
  1. Let resultPromise be a new promise.

  2. Let controlledframe be this.

  3. Return resultPromise and run the remaining steps in parallel.

  4. If controlledframe’s embedded navigable is null, then reject an embedder promise given controlledframe, resultPromise, and a TypeError, and abort these steps.

  5. Resolve an embedder promise given controlledframe, resultPromise, and controlledframe’s embedded navigable’s muted flag.

The setAudioMuted(mute) method steps are:
  1. If this’s embedded navigable is null, then throw a TypeError.

  2. Set this’s embedded navigable’s muted flag to mute.

3.5.1. Zoom

enum ZoomMode {
  "per-origin",
  "per-view",
  "disabled"
};

The user agent holds a Controlled Frame zoom map which is a map whose keys are tuples, and values are floats.

Each HTMLControlledFrameElement holds a ZoomMode zoomMode, which is defaulted to per-origin.

Each HTMLControlledFrameElement holds a float number currentZoom, initially set to 1.0f.

How zoom level is applied to a document given float zoomLevel is specific to the implementation.

This section is non-normative.

The valid ZoomMode values behave as follows:

per-origin

Zoom changes will persist in the embedded document’s origin, i.e. all other HTMLControlledFrameElement in the same partition that are navigated to that same origin will be zoomed as well.

per-view

Zoom changes only take effect in this HTMLControlledFrameElement, and zoom changes in other HTMLControlledFrameElement will not affect the zooming of this HTMLControlledFrameElement.

disabled

Disables all zooming in the HTMLControlledFrameElement. The content will revert to the default zoom level, and all attempted zoom changes will be ignored.

To get the per-origin zoom level, given HTMLControlledFrameElement e, run the following steps:

  1. Let result be a float initially set to 1.0f.

  2. Let key be the result of obtaining a Controlled Frame storage key given the environment associated e’s embedded navigable’s active document’s current settings object.

  3. If Controlled Frame zoom map[key] exists, set result to the result of get the value of Controlled Frame zoom map given key key.

  4. Return result.

To set the per-origin zoom level, given HTMLControlledFrameElement e, and a float zoomLevel, run the following steps:

  1. Let key be the result of obtaining a Controlled Frame storage key given the environment associated e’s embedded navigable’s active document’s current settings object.

  2. Set the value of Controlled Frame zoom map given key key and value zoomLevel.

To determine whether current document has a per-origin zoom level, given HTMLControlledFrameElement e, run the following steps:

  1. Let key be the result of obtaining a Controlled Frame storage key given the environment associated e’s embedded navigable’s active document’s current settings object.

  2. If Controlled Frame zoom map[key] exist, return true.

  3. Return false.

The getZoomMode() method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the remaining steps in parallel.

  4. Resolve an embedder promise given controlledframe, p, and controlledframe’s zoomMode.

The setZoomMode(zoomMode) method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the remaining steps in parallel.

  4. Let currentZoomMode be the result of getZoomMode given controlledframe.

  5. If currentZoomMode equals zoomMode, resolve an embedder promise given controlledframe and p.

  6. Set controlledframe’s zoomMode to zoomMode.

  7. If zoomMode is per-origin:

    1. If current document has a per-origin zoom level given controlledframe is true:

      1. Let oldZoomFactor be controlledframe’s currentZoom.

      2. Set controlledframe’s currentZoom to the result of get the per-origin zoom level given controlledframe.

      3. If oldZoomFactor does not equal to controlledframe’s currentZoom:

        1. Apply zoom level to controlledframe’s embedded document given controlledframe’s currentZoom.

        2. Fire a "zoomchange" event with controlledframe, oldZoomFactor, controlledframe’s currentZoom.

  8. If zoomMode is disabled:

    1. Let oldZoomFactor be controlledframe’s currentZoom.

    2. Set controlledframe’s currentZoom to 1.0f.

    3. If oldZoomFactor does not equal to controlledframe’s currentZoom:

      1. Apply zoom level to controlledframe’s embedded document given controlledframe’s currentZoom.

      2. Fire a "zoomchange" event with controlledframe, oldZoomFactor, controlledframe’s currentZoom.

  9. Resolve an embedder promise given controlledframe and p.

The getZoom() method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the remaining steps in parallel.

  4. Let embeddedNavigable be controlledframe’s embedded navigable.

  5. If embeddedNavigable is null, then reject an embedder promise given controlledframe, p, and a TypeError, and abort these steps.

  6. Resolve an embedder promise given controlledframe, p, and controlledframe’s currentZoom.

The setZoom(zoomFactor) method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the remaining steps in parallel.

  4. Let embeddedNavigable be controlledframe’s embedded navigable.

  5. If embeddedNavigable is null, then reject an embedder promise given controlledframe, p, and a TypeError, and abort these steps.

  6. If controlledframe’s ZoomMode is disabled:

    1. Reject an embedder promise given controlledframe, p, and a TypeError, and abort these steps.

  7. If controlledframe’s ZoomMode is per-origin:

    1. Let oldZoomFactor be controlledframe’s currentZoom.

    2. Set the per-origin zoom level given controlledframe and zoomFactor.

    3. For each browsing context group group in the user agent’s browsing context group set:

      1. For each top-level browsing context browsingContext in group:

        1. Let embeddedDocument be browsingContext’s active document.

        2. Let embedder be embeddedDocument’s node navigable’s embedderParent.

        3. If embedder is null, then continue.

        4. Let match be true if all of the following conditions are true:

        5. If match is true, then:

          1. Set embedder’s currentZoom to zoomFactor.

          2. Apply zoom level to embeddedDocument given zoomFactor.

          3. Fire a "zoomchange" event with embedder, oldZoomFactor, and zoomFactor.

  8. If controlledframe’s ZoomMode is per-view:

    1. Let oldZoomFactor be controlledframe’s currentZoom.

    2. Set controlledframe’s currentZoom to zoomFactor.

    3. If oldZoomFactor does not equal to controlledframe’s currentZoom:

      1. Apply zoom level to controlledframe’s embedded document given controlledframe’s currentZoom.

      2. Fire a "zoomchange" event with controlledframe, oldZoomFactor, controlledframe’s currentZoom.

  9. Resolve an embedder promise given controlledframe and p.

3.6. Capture methods

// One of |code| or |file| must be specified but not both.
dictionary ImageDetails {
  DOMString format;
  DOMString quality;
};
The captureVisibleRegion(options) method steps are:
  1. Let resultPromise be a new promise.

  2. Let controlledframe be this.

  3. Return resultPromise and run the remaining steps in parallel.

  4. If controlledframe’s embedded navigable is null, then reject an embedder promise given controlledframe, resultPromise, and a TypeError, and abort these steps.

  5. Let optionsFormat be "JPEG" by default.

  6. Let optionsQuality be 100 by default.

  7. If options has field "format":

    1. Let optionsFormat be options["format"].

  8. If optionsFormat is an unrecognized format, then reject an embedder promise given controlledframe, resultPromise, and a TypeError, and abort these steps.

  9. If options has field "quality":

    1. Let optionsQuality be options["quality"].

  10. If optionsQuality is not an integer or is not between 0 and 100 inclusive, then reject an embedder promise given controlledframe, resultPromise, and a TypeError, and abort these steps.

  11. Let imageData be a an image showing the visible region of the embedded content encoded in optionsFormat at quality optionsQuality.

    Note: The set of supported image formats is implementation-defined, but it is recommended to support at least "JPEG" and "PNG".

  12. Resolve an embedder promise given controlledframe, resultPromise, and a data: URL that contains imageData.

The print() method steps are:
  1. If this’s embedded navigable is null, then throw a TypeError.

  2. Initiate the browser print page feature for embedded content.

3.7. Events

HTMLControlledFrameElement implements EventTarget and supports the following event handlers (and their corresponding event handler event types).

Event handlers Event handler event types
onconsolemessage consolemessage
oncontentload contentload
ondialog dialog
onloadabort loadabort
onloadcommit loadcommit
onloadstart loadstart
onloadstop loadstop
onnewwindow newwindow
onpermissionrequest permissionrequest
onsizechanged sizechanged
onzoomchange zoomchange

The interactive events:

The UI-change events:

The navigation events:

Each HTMLControlledFrameElement has a load counter which is a number that is initially zero.

Each time a onloadstart event fires, load counter increases by 1.

Each time a onloadabort event fires, load counter decreses by 1.

Each time a onloadcommit event fires, load counter decreses by 1.

When load counter changes from a non-zero number to 0, fire a "loadstop" event.

3.7.1. consolemessage

[Exposed=Window, IsolatedContext]
interface ConsoleMessage {
  readonly attribute long level;
  readonly attribute DOMString message;
};

[Exposed=Window, IsolatedContext]
interface ConsoleMessageEvent : Event {
  constructor(DOMString type, optional ConsoleMessageEventInit eventInitDict = {});
  readonly attribute ConsoleMessage consoleMessage;
};

dictionary ConsoleMessageEventInit: EventInit {
  ConsoleMessage? consoleMessage;
};

To fire a ConsoleMessageEvent e given controlledframe element target, a long logLevel and a DOMString message, run the following steps:

  1. Let consoleMessage be a new ConsoleMessage object.

  2. Set the following fields of consoleMessage:

    level

    logLevel.

    message

    message.

  3. Set e’s consoleMessage to consoleMessage.

  4. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.2. dialog

enum DialogType {
  "alert",
  "confirm",
  "prompt"
};

[Exposed=Window, IsolatedContext]
interface DialogController {
  undefined okay(optional DOMString response);
  undefined cancel();
};

[Exposed=Window, IsolatedContext]
interface DialogMessage {
  readonly attribute DialogType messageType;
  readonly attribute DOMString messageText;
  readonly attribute DialogController dialog;
};

[Exposed=Window, IsolatedContext]
interface DialogEvent : Event {
  constructor(DOMString type, optional DialogEventInit eventInitDict = {});
  readonly attribute DialogMessage dialogMessage;
};

dictionary DialogEventInit: EventInit {
  DialogMessage? dialogMessage;
};

Each DialogController has:

The okay(response) method steps are:

  1. Set accept to true.

  2. Set response to response.

The cancel() method steps are:

  1. Set accept to false.

To fire a DialogEvent e given controlledframe element target, simple-dialogs type dialogType, and message message, run the following steps:

  1. Assert that e["dialogMessage"]["dialog"]["accept"] equals false.

  2. Let dialogMessage be a new DialogMessage object.

  3. Let dialog be a new DialogController object.

  4. Set the following fields of dialogMessage:

    messageType

    dialogType.

    messageText

    message.

    dialog

    dialog.

  5. Set e’s dialogMessage to dialogMessage.

  6. Let complete be false.

  7. Queue a global task on the DOM manipulation task source of target’s relevant global object to run the following steps:

    1. Dispatch e at target.

    2. Set complete to true.

  8. Synchronously wait for complete to be true.

    Note: Blocking here is intentional as the alert, confirm, and prompt dialogs represented by "dialog" events block the main thread, and the event handlers we’re calling here can affect the return values.

  9. Return e["dialogMessage"]["dialog"]["accept"] and e["dialogMessage"]["dialog"]["response"].

3.7.3. newwindow

enum WindowOpenDisposition {
  "ignore",
  "save_to_disk",
  "current_tab",
  "new_background_tab",
  "new_foreground_tab",
  "new_window",
  "new_popup"
};

[Exposed=Window, IsolatedContext]
interface NewWindowController {
  undefined attach(HTMLControlledFrameElement newControlledFrame);
  undefined discard();
};

[Exposed=Window, IsolatedContext]
interface NewWindow {
  readonly attribute NewWindowController window;
  readonly attribute USVString targetUrl;
  readonly attribute DOMString name;
  readonly attribute WindowOpenDisposition windowOpenDisposition;
};

[Exposed=Window, IsolatedContext]
interface NewWindowEvent : Event {
  constructor(DOMString type, optional NewWindowEventInit eventInitDict = {});
  readonly attribute NewWindow newWindow;
};

dictionary NewWindowEventInit: EventInit {
  NewWindow? newWindow;
};

Each NewWindowController has reference to a target navigable, which is initially null.

The attach(newControlledFrame) method steps are:

  1. Set newControlledFrame’s embedded navigable to this’s target navigable.

The discard() method steps are:

  1. If this’s target navigable is not null, then close a top-level traversable given this’s target navigable.

To fire a NewWindowEvent e given controlledframe element controlledFrame, USVString url, DOMString target, navigable targetNavigable, and WindowOpenDisposition windowOpenDisposition, run the following steps:

  1. Let controller be a new NewWindowController object.

  2. Set controller’s target navigable to targetNavigable.

  3. Let newWindow be a new NewWindow object.

  4. Set the following fields of newWindow:

    window

    controller

    targetUrl

    url

    name

    target.

    windowOpenDisposition

    windowOpenDisposition

  5. Set e’s newWindow to newWindow.

  6. Queue a global task on the DOM manipulation task source of controlledFrame’s relevant global object to dispatch e at controlledFrame.

3.7.4. permissionrequest

enum PermissionType {
  "media",
  "geolocation",
  "pointerLock",
  "download",
  "filesystem",
  "fullscreen",
  "hid",
};

[Exposed=Window, IsolatedContext]
interface PermissionRequestControllerBase {
  undefined allow();
  undefined cancel();
};

[Exposed=Window, IsolatedContext]
interface MediaPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface GeolocationPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface PointerLockPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute boolean lastUnlockedBySelf;
  readonly attribute boolean userGesture;
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface DownloadPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute DOMString requestMethod;
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface FileSystemPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface FullscreenPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString origin;
};

[Exposed=Window, IsolatedContext]
interface HidPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface PermissionRequest {
  readonly attribute PermissionType permission;
  readonly attribute PermissionRequestControllerBase request;
};

[Exposed=Window, IsolatedContext]
interface PermissionRequestEvent : Event {
  constructor(DOMString type, optional PermissionRequestEventInit eventInitDict = {});
  readonly attribute PermissionRequest permissionRequest;
};

dictionary PermissionRequestEventInit: EventInit {
  PermissionRequest? permissionRequest;
};

Each PermissionRequestControllerBase has a boolean value allow, which is initially false.

The allow() method steps are:

  1. Set allow to true.

The cancel() method steps are:

  1. Set allow to false.

To fire a PermissionRequestEvent e given a document embeddedDocument, controlledframe element target, DOMString type, USVString url, an optional dictionary options, and an algorithm completionSteps that takes a boolean, run the following steps:

  1. Let permissionRequest be a new PermissionRequest object.

  2. If type is "media":

    1. Let requestController be a new MediaPermissionRequestController with the following attributes:

      url

      url

  3. If type is "geolocation":

    1. Let requestController be a new GeolocationPermissionRequestController with the following attributes:

      url

      url

  4. If type is "pointerLock":

    1. Let requestController be a new PointerLockPermissionRequestController with the following attributes:

      lastUnlockedBySelf

      options["lastUnlockedBySelf"]

      userGesture

      options["userGesture"]

      url

      url

  5. If type is "download":

    1. Let requestController be a new DownloadPermissionRequestController with the following attributes:

      requestMethod

      options["requestMethod"]

      url

      url

  6. If type is "filesystem":

    1. Let requestController be a new FileSystemPermissionRequestController with the following attributes:

      url

      url

  7. If type is "fullscreen":

    1. Let requestController be a new FullscreenPermissionRequestController with the following attributes:

      origin

      origin of url

  8. If type is "hid":

    1. Let requestController be a new HidPermissionRequestController with the following attributes:

      url

      url

  9. Set the following fields of permissionRequest:

    request

    requestController

    permission

    type.

  10. Set e’s permissionRequest to permissionRequest.

  11. Queue a global task on the DOM manipulation task source of target’s relevant global object to run the following steps:

    1. Dispatch e at target.

    2. Queue a global task on the DOM manipulation task source of embeddedDocument’s relevant global object that runs completionSteps with e["permissionRequest"]["permission"]["allow"].

3.7.5. sizechanged

[Exposed=Window, IsolatedContext]
interface SizeChange {
  readonly attribute unsigned long oldWidth;
  readonly attribute unsigned long oldHeight;
  readonly attribute unsigned long newWidth;
  readonly attribute unsigned long newHeight;
};

[Exposed=Window, IsolatedContext]
interface SizeChangedEvent : Event {
  constructor(DOMString type, optional SizeChangedEventInit eventInitDict = {});
  readonly attribute SizeChange sizeChange;
};

dictionary SizeChangedEventInit: EventInit {
  SizeChange? sizeChange;
};

To fire a SizeChangedEvent e given controlledframe element target, 4 non-negative numbers oldWidth, oldHeight, newWidth, newHeight:

  1. Let sizeChange be a new SizeChange object.

  2. Set the following fields of sizeChange:

    oldWidth

    oldWidth.

    oldHeight

    oldHeight.

    newWidth

    newWidth.

    newHeight

    newHeight.

  3. Set e’s sizeChange to sizeChange.

  4. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.6. zoomchange

[Exposed=Window, IsolatedContext]
interface ZoomChange {
  readonly attribute float oldZoomFactor;
  readonly attribute float newZoomFactor;
};

[Exposed=Window, IsolatedContext]
interface ZoomChangeEvent : Event {
  constructor(DOMString type, optional ZoomChangeEventInit eventInitDict = {});
  readonly attribute ZoomChange zoomChange;
};

dictionary ZoomChangeEventInit: EventInit {
  ZoomChange? zoomChange;
};

To fire a ZoomChangeEvent e given controlledframe element target, 2 float numbers oldZoomFactor, newZoomFactor:

  1. Let zoomChange be a new ZoomChange object.

  2. Set the following fields of zoomChange:

    oldZoomFactor

    oldZoomFactor.

    newZoomFactor

    newZoomFactor.

  3. Set e’s zoomChange to zoomChange.

  4. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.7. contentload

[Exposed=Window, IsolatedContext]
interface ContentLoadEvent : Event {
  constructor(DOMString type, optional EventInit eventInitDict = {});
};

To fire a ContentLoadEvent e given controlledframe element target, run the following steps:

  1. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.8. loadabort

[Exposed=Window, IsolatedContext]
interface LoadInfo {
  readonly attribute USVString url;
  readonly attribute boolean isTopLevel;
};

[Exposed=Window, IsolatedContext]
interface LoadAbortInfo : LoadInfo{
  readonly attribute long code;
  readonly attribute DOMString reason;
};

[Exposed=Window, IsolatedContext]
interface LoadRedirectInfo{
  readonly attribute USVString oldUrl;
  readonly attribute USVString newUrl;
  readonly attribute boolean isTopLevel;
};

[Exposed=Window, IsolatedContext]
interface LoadAbortEvent : Event {
  constructor(DOMString type, optional LoadAbortEventInit eventInitDict = {});
  readonly attribute LoadAbortInfo loadAbortInfo;
};

dictionary LoadAbortEventInit: EventInit {
  LoadAbortInfo? loadAbortInfo;
};

To fire a LoadAbortEvent e given controlledframe element target, a USVString url, a boolean isTopLevel, a long code, and a DOMString reason, run the following steps:

  1. Let info be a new LoadAbortInfo with the following attributes:

    url

    url

    isTopLevel

    isTopLevel

    code

    code

    reason

    reason

  2. Set e’s loadAbortInfo to info.

  3. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.9. loadcommit

[Exposed=Window, IsolatedContext]
interface LoadCommitEvent : Event {
  constructor(DOMString type, optional LoadCommitEventInit eventInitDict = {});
  readonly attribute LoadInfo loadInfo;
};

dictionary LoadCommitEventInit: EventInit {
  LoadInfo? loadInfo;
};

To fire a LoadCommitEvent e given controlledframe element target, a USVString url, and a boolean isTopLevel, run the following steps:

  1. Let info be a new LoadInfo with the following attributes:

    url

    url

    isTopLevel

    isTopLevel

  2. Set e’s loadInfo to info.

  3. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.10. loadstart

[Exposed=Window, IsolatedContext]
interface LoadStartEvent : Event {
  constructor(DOMString type, optional LoadStartEventInit eventInitDict = {});
  readonly attribute LoadInfo loadInfo;
};

dictionary LoadStartEventInit: EventInit {
  LoadInfo? loadInfo;
};

To fire a LoadStartEvent e given controlledframe element target, a USVString url, and a boolean isTopLevel, run the following steps:

  1. Let info be a new LoadInfo with the following attributes:

    url

    url

    isTopLevel

    isTopLevel

  2. Set e’s loadInfo to info.

  3. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.11. loadstop

[Exposed=Window, IsolatedContext]
interface LoadStopEvent : Event {
  constructor(DOMString type, optional LoadStopEventInit eventInitDict = {});
};

dictionary LoadStopEventInit: EventInit {
};

To fire a LoadStopEvent e given controlledframe element target, run the following steps:

  1. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.12. loadredirect

[Exposed=Window, IsolatedContext]
interface LoadRedirectEvent : Event {
  constructor(DOMString type, optional LoadRedirectEventInit eventInitDict = {});
  readonly attribute LoadRedirectInfo loadRedirectInfo;
};

dictionary LoadRedirectEventInit: EventInit {
  LoadRedirectInfo? loadRedirectInfo;
};

To fire a LoadRedirectEvent e given controlledframe element target, an URL oldUrl, an URL newUrl, and a boolean isTopLevel, run the following steps:

  1. Let info be a new LoadInfo with the following attributes:

    oldUrl

    The result of serializing an URL given oldUrl

    newUrl

    The result of serializing an URL given newUrl

    isTopLevel

    isTopLevel

  2. Set e’s loadRedirectInfo to info.

  3. Queue a global task on the DOM manipulation task source of target’s relevant global object to dispatch e at target.

3.7.13. Monkey Patches

3.7.13.1. [HTML]

For alert:

  1. Set message to the result of optionally truncating message.

  2. Let embedderParent be window’s navigable’s embedderParent. .
  3. If embedderParent is a HTMLControlledFrameElement, then fire a "dialog" event with embedderParent, "alert" and message, and return.

For confirm:

  1. Set message to the result of optionally truncating message.

  2. Let embedderParent be window’s navigable’s embedderParent. .
  3. If embedderParent is a HTMLControlledFrameElement, then return the result of firing a "dialog" event with embedderParent, "confirm", and message.

For prompt:

  1. Set default to the result of optionally truncating default.

  2. Let embedderParent be window’s navigable’s embedderParent. .
  3. If embedderParent is a HTMLControlledFrameElement, then:
    1. Let accept and response be the results of firing a "dialog" event with embedderParent, "prompt" and message.
    2. If accept equals false, return null.
    3. Return response.

For window open steps:

  1. Let windowOpenDisposition be "ignore".
  2. Let embedderParent be sourceDocument’s node navigable’s embedderParent.

...

14. If targetNavigable is null, then return null.
  1. If targetNavigable is null:
    1. If embedderParent is not null:
      1. Fire a "newwindow" event with embedderParent, url, target, targetNavigable, and windowOpenDisposition.
    2. Return null.
  2. If windowType is either "new and unrestricted" or "new with no opener", then:

    1. Set windowOpenDisposition to one of "new_background_tab", "new_foreground_tab", or "new_window".

    Note: The value depends on the behavior support of the user agent and user settings.

    1. Set targetNavigable’s active browsing context’s is popup to the result of checking if a popup window is requested, given tokenizedFeatures.

    2. If targetNavigable’s active browsing context’s is popup is true, set windowOpenDisposition to "new_popup".

    ...

  3. Otherwise:

    1. Set windowOpenDisposition to "current_tab".

    ...

Note: Step 15 and 16 also navigate targetNavigable; during navigation, windowOpenDisposition may also be updated. For example, if navigation is prevented, windowOpenDisposition may be set to "ignore". If the navigation response resulted in download, then windowOpenDisposition may be set to "save_to_disk".

  1. If embedderParent is not null:
    1. Fire a "newwindow" event with embedderParent, url, target, targetNavigable, and windowOpenDisposition.

For completely finish loading:

  1. Otherwise, if container is non-null, then queue an element task on the DOM manipulation task source given container to fire an event named load at container.

  2. Let embedderParent be document’s node navigable’s embedderParent.
  3. If embedderParent is HTMLControlledFrameElement, then Fire a "contentload" event with embedderParent.

Monkey-patch for loadstart, loadabort, and loadcommit which looks like the following:

  1. loadstart -> at the navigation entry points: normal navigation, reloads, traversal.

  2. loadcommit -> at the common navigation completion point.

  3. loadabort -> at each early exist points of the algorithm between loadstart and loadcommit.

For each of these, check whether the top-level traversable of the navigable has an embedderParent of HTMLControlledFrameElement, if so, then fire the corresponding event with input arguments.

3.7.13.2. [FETCH]

For HTTP fetch(loadredirect):

  1. If internalResponse’s status is a redirect status:

    1. If request’s window window is a environment settings object whose global object is a Window object:
      1. Let currentNavigable be window’s associated navigable.
      2. Let embedderParent be currentNavigable’s top-level traversable’s embedderParent.
      3. If embedderParent is a HTMLControlledFrameElement:

        1. Let oldUrl be request’s associated URL.

        1. Let newUrl be response’s location url.

        1. Let isTopLevel be false, if currentNavigable has a null parent set isTopLevel to true.

        1. Fire a "loadredirect" event with embedderParent, oldUrl, newUrl, and isTopLevel.

3.7.13.3. [Permissions]

Define new algorithm:

To determine if a Document document is allowed by embedder to use a permission given DOMString permission, an optional dictionary options, and an algorithm completionSteps that takes a boolean, run the following steps:

  1. Let embedderParent be document’s node navigable’s embedderParent.

  2. If embedderParent is not HTMLControlledFrameElement, return true.

  3. Fire a "permissionrequest" event with document, embedderParent, permission, document’s URL, options, and completionSteps.

For request a position (geolocation):

  1. If document is not allowed to use the "geolocation" feature:

    1. If watchId was passed, remove watchId from watchIDs.

    2. Call back with error passing errorCallback and PERMISSION_DENIED.

    3. Terminate this algorithm.

  2. Let blockedByEmbedder be false.
  3. Check if document is allowed by embedder to use the "geolocation" feature given an algorithm that assigns its boolean argument to blockedByEmbedder and continues with the remainder of these steps.
  4. If blockedByEmbedder is true, then:
    1. If watchId was passed, remove watchId from watchIDs.
    2. Call back with error passing errorCallback and PERMISSION_DENIED.
    3. Terminate this algorithm.
  5. Let embedderParent be document’s node navigable’s embedderParent.
  6. If embedderParent is HTMLControlledFrameElement:
    1. Let nodeDocument be embedderParent’s node document.

    2. If nodeDocument is null:
      1. If watchId was passed, remove watchId from watchIDs.
      2. Call back with error passing errorCallback and PERMISSION_DENIED.
      3. Terminate this algorithm.
    3. Let embedderNavigator be the associated Navigator of nodeDocument’s global object.
    4. Let embedderGeolocation be the geolocation of embedderNavigator.
    5. Return the result of request a position with embedderGeolocation, successCallback, errorCallback, options, and whatchId.

Note: Other permissions will be monkey-patched similarly to geolocation. But the details are elided in this document for brevity.

3.7.13.4. [Console]

For Logger:

  1. Otherwise, perform Printer(logLevel, Formatter(args)).

  2. Fire a "consolemessage" event with logLevel, Formatter(args).

Note: Console by default does not have association with document so we cannot track the associated HTMLControlledFrameElement.

3.8. Integration with other specifications

This specification will make some modifications to specifications to accommodate the needs of Controlled Frame.

3.8.1. Monkey Patches

3.8.1.1. [HTML]

Each navigable has:

The initialize the navigable algorithm given a navigable navigable and an optional navigable-or-null parent (default null) is monkey patched as follows:

  1. Set navigable’s parent to parent.

  2. If parent is not null (navigable is not a top-level traversable), then:
    1. Let topLevelTraversable be the top-level traversable that navigable is a descendant of.
    2. Set navigable’s frameId to topLevelTraversable’s next frameId.
    3. Increment topLevelTraversable’s next frameId.

The update the current document readiness for Document document to readinessValue algorithm is monkey patched as follows:

  1. Let runAt be the result of applying the following mapping to readinessValue:
    "loading"

    "document-start"

    "interactive"

    "document-end"

    "complete"

    "document-idle"

  2. Inject content scripts into a document given document and runAt.
  3. Fire an event named readystatechange at document.

3.8.1.2. [FETCH]

The determine the network partition key algorithm is monkey extended to require double-keying on network requests originating from a Controlled Frame’s embedded navigable.

To determine the network partition key, given an environment environment:
  1. Let topLevelSite be the result of obtaining a site, given topLevelOrigin.

  2. Let secondKey be null or an implementation-defined value .

  3. Let embedderParent be the result of getting an environment’s embedderParent given environment.
  4. If embedderParent is not null, then set secondKey to a tuple consisting of the top-level origin of embedderParent’s relevant settings object, and embedderParent’s partition.
  5. Return (topLevelSite, secondKey).

3.8.1.3. [STORAGE]

Storage keys are re-defined as follows:

A storage key is a tuple consisting of an embedding origin (an origin or null), a partition (a DOMString or null), and an origin (an origin).

Note: This definition will need to be expanded to include the embedded content’s top-level origin in addition to origin in the future once storage partitioning is fully specified.

Note: Controlled Frame data is triple keyed with the origin of the document that owns the Controlled Frame element, not the top-level document that owns it.

The obtain a storage key for non-storage purposes algorithm is extended to require double-keying on all storage belonging to a controlledframe’s embedded navigable.

To obtain a storage key for non-storage purposes, given an environment environment, run these steps:
  1. Let origin be environment’s origin if environment is an environment settings object; otherwise environment’s creation URL’s origin.

  2. Return a tuple consisting of origin.
  3. Let topLevelOrigin and partition be null.
  4. Let embedderParent be the result of getting an environment’s embedderParent given environment.
  5. If embedderParent is not null, then:
    1. Set topLevelOrigin to the top-level origin of embedderParent’s relevant settings object.
    2. Set partition to embedderParent’s partition.
  6. Return a tuple consisting of topLevelOrigin, partition, and origin.
To get an environment’s embedderParent given an environment environment, run the following steps:
  1. If environment is an environment settings object whose global object is a Window object, then:

    This algorithm doesn’t work for Shared or Service Workers because embedderParent is only defined on a navigable, and it’s not always possible to go from a non-Window environment to a navigable.

    1. Let navigable be environment’s global object’s navigable.

    2. Let top be the top-level traversable of navigable.

    3. If top’s embedderParent is not null, then return top’s embedderParent.

  2. Return null.

4. Web Request API

enum ResourceType {
  "main-frame",
  "sub-frame",
  "stylesheet",
  "script",
  "image",
  "font",
  "object",
  "xmlhttprequest",
  "ping",
  "csp-report",
  "media",
  "websocket",
  "other",
};

enum RequestedHeaders {
  "none",
  "cors",
  "all",
};

dictionary WebRequestInterceptorOptions {
  required sequence<(URLPattern or URLPatternInput)> urlPatterns;
  sequence<ResourceType> resourceTypes = [];
  boolean blocking = false;
  boolean includeRequestBody = false;
  RequestedHeaders includeHeaders = "none";
};

[Exposed=Window, IsolatedContext]
interface WebRequest {
  WebRequestInterceptor createWebRequestInterceptor(
      WebRequestInterceptorOptions options);

  Promise<undefined> interceptorBehaviorChanged();
};

[Exposed=Window, IsolatedContext]
interface WebRequestInterceptor : EventTarget {
  attribute EventHandler onauthrequired;
  attribute EventHandler onbeforeredirect;
  attribute EventHandler onbeforerequest;
  attribute EventHandler onbeforesendheaders;
  attribute EventHandler oncompleted;
  attribute EventHandler onerroroccurred;
  attribute EventHandler onheadersreceived;
  attribute EventHandler onsendheaders;
  attribute EventHandler onresponsestarted;
};

enum DocumentLifecycle {
  "prerender",
  "active",
  "cached",
  "pending-deletion",
};

enum FrameType {
  "outermost-frame",
  "fenced-frame",
  "sub-frame",
};

[Exposed=Window, IsolatedContext]
interface UploadData {
  readonly attribute ArrayBuffer? bytes;
  readonly attribute DOMString? file;
};

[Exposed=Window, IsolatedContext]
interface RequestBody {
  readonly attribute DOMString? error;
  readonly attribute any formData;
  readonly attribute FrozenArray<UploadData>? raw;
};

[Exposed=Window, IsolatedContext]
interface WebRequestRequest {
  readonly attribute DOMString method;
  readonly attribute DOMString id;
  readonly attribute ResourceType type;
  readonly attribute USVString url;
  readonly attribute USVString? initiator;
  readonly attribute Headers? headers;
  readonly attribute RequestBody? body;
};

[Exposed=Window, IsolatedContext]
interface AuthChallenger {
  readonly attribute DOMString host;
  readonly attribute long port;
};

[Exposed=Window, IsolatedContext]
interface WebRequestAuthDetails {
  readonly attribute AuthChallenger challenger;
  readonly attribute boolean isProxy;
  readonly attribute DOMString scheme;
  readonly attribute DOMString? realm;
};

[Exposed=Window, IsolatedContext]
interface WebRequestResponse {
  readonly attribute long statusCode;
  readonly attribute DOMString statusLine;
  readonly attribute boolean fromCache;
  readonly attribute Headers? headers;
  readonly attribute DOMString? ip;
  readonly attribute USVString? redirectURL;
  readonly attribute WebRequestAuthDetails? auth;
};

[Exposed=Window, IsolatedContext]
interface WebRequestEvent : Event {
  readonly attribute WebRequestRequest request;
  readonly attribute long frameId;
  readonly attribute FrameType? frameType;
  readonly attribute DOMString? documentId;
  readonly attribute DocumentLifecycle? documentLifecycle;
  readonly attribute DOMString? parentDocumentId;
  readonly attribute long? parentFrameId;
};

dictionary WebRequestAuthCredentials {
  required DOMString username;
  required DOMString password;
};

dictionary WebRequestAuthOptions {
  AbortSignal signal;
};

[Exposed=Window, IsolatedContext]
interface WebRequestAuthRequiredEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;

  undefined setCredentials(
    Promise<WebRequestAuthCredentials> credentials,
    optional WebRequestAuthOptions options = {});
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeRedirectEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeRequestEvent : WebRequestEvent {
  undefined redirect(USVString redirectURL);
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeSendHeadersEvent : WebRequestEvent {
  undefined setRequestHeaders((Headers or HeadersInit) requestHeaders);
};

[Exposed=Window, IsolatedContext]
interface WebRequestCompletedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestErrorOccurredEvent : WebRequestEvent {
  readonly attribute DOMString error;
};

[Exposed=Window, IsolatedContext]
interface WebRequestHeadersReceivedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;

  undefined redirect(USVString redirectURL);
  undefined setResponseHeaders((Headers or HeadersInit) responseHeaders);
};

[Exposed=Window, IsolatedContext]
interface WebRequestResponseStartedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestSendHeadersEvent : WebRequestEvent {};

Each WebRequest has an associated:

Each WebRequestInterceptor has an associated:

Each UploadData has an associated:

Each RequestBody has an associated:

Each WebRequestRequest has an associated:

Each AuthChallenger has an associated:

Each WebRequestAuthDetails has an associated:

Each WebRequestResponse has an associated:

Each WebRequestEvent has an associated:

Each WebRequestAuthRequiredEvent has an associated:

Each WebRequestBeforeRedirectEvent has an associated:

Each WebRequestCompletedEvent has an associated:

Each WebRequestErrorOccurredEvent has an associated:

Each WebRequestHeadersReceivedEvent has an associated:

Each WebRequestResponseStartedEvent has an associated:

The createWebRequestInterceptor(options) method steps are:
  1. Let interceptor be a new WebRequestInterceptor object.

  2. Set the following fields on interceptor:

    resourceTypes

    options["resourceTypes"]

    blocking

    options["blocking"]

    includeRequestBody

    options["includeRequestBody"]

    includeHeaders

    options["includeHeaders"]

  3. For each urlPattern in options["urlPatterns"]:

    1. If urlPattern is a URLPattern, then append urlPattern to interceptor’s urlPatterns.

    2. Otherwise, append a new URLPattern given urlPattern to interceptor’s urlPatterns.

  4. Append interceptor to this’s interceptors.

  5. Return interceptor.

The interceptorBehaviorChanged(callback) method steps are:
  1. Let controlledframe be this.

  2. In parallel, clear the HTTP cache associated with the partition used by environments embedded within the controlledframe HTMLControlledFrameElement.

    Note: The behavior of event handlers registered through the WebRequest API will be reflected in the HTTP cache, which will no longer be valid if the behavior of the event handlers changed. The purpose of this method is to invalidate any cache entries that were affected by WebRequest event handlers.

  3. Invoke callback when the HTTP cache has been cleared.

To get the applicable WebRequestInterceptors for a request request, run the following steps:
  1. Let client be request’s client.

  2. If client is null, return an empty list and false.

  3. Let window be the owning Window of an environment settings object given client.

  4. If window is null then return an empty list and false.

  5. Let navigable be window’s node navigable.

  6. If navigable’s embedderParent is null, return an empty list and false.

  7. Let controlledFrame be the HTMLControlledFrameElement corresponding to navigable’s embedderParent.

  8. Let applicableInterceptors be an empty list.

  9. Let blocking be false.

  10. For each interceptor in controlledFrame’s request’s interceptors:

    1. If request should not be intercepted by interceptor, then continue.

    2. Append interceptor to applicableInterceptors.

    3. If interceptor’s blocking is true, then set blocking to true.

  11. Return applicableInterceptors and blocking.

To determine if a request request should be intercepted by a WebRequestInterceptor, run the following steps:
  1. Let types be interceptor’s resourceTypes.

  2. If types is not empty and types does not contain the result of calling get a request’s ResourceType given request, then return false.

  3. Let urlPatterns be interceptor’s urlPatterns.

  4. For each urlPattern in urlPatterns:

    1. If request’s URL matches a URLPattern given urlPattern, then return true.

  5. Return false.

To get the owning Window of an environment settings object given an environment settings object environment, run the following steps:
  1. Let global be environment’s global object.

  2. If global is a Window object, then return global.

  3. If global is a DedicatedWorkerGlobalScope object, then:

    1. Let owner be global.

    2. While owner is a DedicatedWorkerGlobalScope object:

      1. Let owner be owner’s owner set[0].

    3. If owner is a Document object, then return owner’s relevant global object.

  4. Return null.

4.1. Events

WebRequestInterceptor fires the following events:

Event name Interface Fired when...
authrequired WebRequestAuthRequiredEvent an intercepted request receives a response with an HTTP 401 (Unauthorized) 407 (Proxy Authentication Required) status.
beforeredirect WebRequestBeforeRedirectEvent an intercepted request receives a redirect response.
beforerequest WebRequestBeforeRequestEvent an intercepted request is about to start being processed.
beforesendheaders WebRequestBeforeSendHeadersEvent the request headers of an intercepted request are about to be sent.
completed WebRequestCompletedEvent an intercepted request completed.
erroroccurred WebRequestErrorOccurredEvent an error occurred when processing an intercepted request.
headersreceived WebRequestHeadersReceivedEvent an intercepted request receives response headers.
sendheaders WebRequestSendHeadersEvent an intercepted request sends its request headers.
responsestarted WebRequestResponseStartedEvent an intercepted request has begun receiving its response.

WebRequestInterceptor supports the following event handlers (and their corresponding event handler event types) as event handler IDL attributes:

Event handlers Event handler event types
onauthrequired authrequired
onbeforeredirect beforeredirect
onbeforerequest beforerequest
onbeforesendheaders beforesendheaders
oncompleted completed
onerroroccurred erroroccurred
onheadersreceived headersreceived
onsendheaders sendheaders
onresponsestarted responsestarted

Each WebRequestAuthRequiredEvent has an associated:

WebRequestAuthRequiredEvent’s setCredentials(credentials, options) method steps are:
  1. Set this’s authCredentials to credentials.

  2. Set this’s options to options.

WebRequestAuthRequiredEvent’s preventDefault() method steps are to set this’s cancel to true.

Each WebRequestBeforeRequestEvent has an associated:

WebRequestBeforeRequestEvent’s redirect(redirectURL) method steps are to set this’s redirectURL to redirectURL.
WebRequestBeforeRequestEvent’s preventDefault() method steps are to set this’s cancel to true.

Each WebRequestBeforeSendHeadersEvent has an associated:

WebRequestBeforeSendHeadersEvent’s setRequestHeaders(requestHeaders) method steps are to set this’s requestHeaders to requestHeaders.
WebRequestBeforeSendHeadersEvent’s preventDefault() method steps are to set this’s cancel to true.

Each WebRequestHeadersReceivedEvent has an associated:

WebRequestHeadersReceivedEvent’s redirect(redirectURL) method steps are to set this’s redirectURL to redirectURL.
WebRequestHeadersReceivedEvent’s setResponseHeaders(responseHeaders) method steps are to set this’s responseHeaders to responseHeaders.
WebRequestHeadersReceivedEvent’s preventDefault() method steps are to set this’s cancel to true.

A WebRequestEvent result is a struct with the following items:

cancel

a boolean, initially false

redirectURL

a USVString, initially ""

authCredentials

a WebRequestAuthCredentials or null, initially null

requestHeaders

a header list, initially «»

responseHeaders

a header list, initially «»

To process beforeRequest events given a request request, run the following steps:
  1. Let interceptors and blocking be the result of getting the applicable WebRequestInterceptors of request.

  2. Let result be a WebRequestEvent result.

  3. Let pendingHandlerCount be interceptor’s size.

  4. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  5. For each interceptor in interceptors:

    1. Let event be a new WebRequestBeforeRequestEvent named beforerequest.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. If interceptor’s includeRequestBody is true and request’s body is not null, then:

      1. Let requestBody be a new RequestBody.

      2. Let body be request’s body.

        TODO: serialize stream if present.

      3. Switch on body’s source:

        byte sequence

        Append a new UploadData with bytes equal to the serialized body’s source to requestBody’s raw.

        Blob

        Append a new UploadData with bytes equal to the serialized body’s source to requestBody’s raw.

        FormData
        1. Let formData be «[]».

        2. For each entry in body’s source’s entry list:

          1. Switch on entry[1]:

          File

          Append a new UploadData with file equal to entry[1]'s name to requestBody’s raw.

          USVString
          1. If formData[entry[0]] does not exist, then set formData[entry[0]] equal to an empty list.

          2. Append entry[1] to formData[entry[0]].

        3. Set requestBody’s formData to formData.

      4. Set event’s request’s body to requestBody.

    4. Queue a global task on the DOM manipulation task source of embedderGlobal that will run the following steps:

      1. Dispatch event to interceptor.

      2. If event’s cancel is true, then set result’s cancel to true.

      3. If event’s redirectURL is not null and result’s redirectURL is "", then set result’s redirectURL to event’s redirectURL.

      4. Decrement pendingHandlerCount.

  6. If blocking is true, then wait in parallel for pendingHandlerCount to equal 0, then queue a global task on the networking task source of request’s client’s global object that will continue the remaining steps of this algorithm.

  7. Return result.

To process beforeSendHeaders events given a request request, run the following steps:
  1. Let interceptors and blocking be the result of getting the applicable WebRequestInterceptors of request.

  2. Let result be a WebRequestEvent result.

  3. Let pendingHandlerCount be interceptor’s size.

  4. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  5. For each interceptor in interceptors:

    1. Let event be a new WebRequestBeforeSendHeadersEvent named beforesendheaders.

    2. Populate a WebRequestEvent given event, request, and interceptor’s includeHeaders.

    3. Queue a global task on the DOM manipulation task source of embedderGlobal that will run the following steps:

      1. Dispatch event to interceptor.

      2. If event’s cancel is true, then set result’s cancel to true.

      3. If event’s requestHeaders is not null, then:

        1. If result’s requestHeaders is null, then set it to a new Headers.

        2. For each header in event’s requestHeaders:

          1. Append header to result’s requestHeaders.

      4. Decrement pendingHandlerCount.

  6. If blocking is true, then wait in parallel for pendingHandlerCount to equal 0, then queue a global task on the networking task source of request’s client’s global object that will continue the remaining steps of this algorithm.

  7. Return result.

To process sendHeaders events given a request request, run the following steps:
  1. Let interceptors be the result of getting the applicable WebRequestInterceptors of request.

  2. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  3. For each interceptor in interceptors:

    1. Let event be a new WebRequestSendHeadersEvent named sendheaders.

    2. Populate a WebRequestEvent given event, request, and interceptor’s includeHeaders.

    3. Queue a global task on the DOM manipulation task source of embedderGlobal that will dispatch event to interceptor.

To process headersReceived events given a request request and a response response, run the following steps:
  1. Let interceptors and blocking be the result of getting the applicable WebRequestInterceptors of request.

  2. Let result be a WebRequestEvent result.

  3. Let pendingHandlerCount be interceptor’s size.

  4. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  5. For each interceptor in interceptors:

    1. Let event be a new WebRequestHeadersReceivedEvent named headersreceived.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s response to the result of creating a WebRequestResponse object given request, response, and interceptor’s includeHeaders.

    4. Queue a global task on the DOM manipulation task source of embedderGlobal that will run the following steps:

      1. Dispatch event to interceptor.

      2. If event’s cancel is true, then set result’s cancel to true.

      3. If event’s redirectURL is not null and result’s redirectURL is "", then set result’s redirectURL to event’s redirectURL.

      4. If event’s responseHeaders is not null, then:

        1. If result’s responseHeaders is null, then set it to a new Headers.

        2. For each header in event’s responseHeaders:

          1. Append header to result’s responseHeaders.

      5. Decrement pendingHandlerCount.

  6. If blocking is true, then wait in parallel for pendingHandlerCount to equal 0, then queue a global task on the networking task source of request’s client’s global object that will continue the remaining steps of this algorithm.

  7. Return result.

To process authRequired events given a request request and a response response, run the following steps:
  1. Let challenger be a new AuthChallenger with host and port equal to the host and port of response’s URL.

  2. If response’s status is 401, then:

    1. Let isProxy be false.

    2. Let scheme be the scheme from response’s WWW-Authenticate header, parsed as defined by the [HTML] specification, or null if the header is missing or invalid.

    3. Let realm be the realm from response’s WWW-Authenticate header, parsed as defined by the [HTML] specification.

  3. Otherwise:

    1. Let isProxy be true.

    2. Let scheme be the scheme from response’s Proxy-Authenticate header, parsed as defined by the [HTML] specification, or null if the header is missing or invalid.

    3. Let realm be the realm from response’s Proxy-Authenticate header, parsed as defined by the [HTML] specification.

  4. If scheme is null, then return null.

  5. Let interceptors and blocking be the result of getting the applicable WebRequestInterceptors of request.

  6. Let result be a WebRequestEvent result.

  7. Let pendingHandlerCount be interceptor’s size.

  8. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  9. For each interceptor in interceptors:

    1. Let event be a new WebRequestAuthRequiredEvent named authrequired.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s response to the result of creating a WebRequestResponse object given request, response, and interceptor’s includeHeaders.

    4. Set event’s response’s auth to a new WebRequestAuthDetails whose challenger, isProxy, scheme, and realm fields have the values challenger, isProxy, scheme, and realm respectively.

    5. Queue a global task on the DOM manipulation task source of embedderGlobal that will run the following steps:

      1. Dispatch event to interceptor.

      2. Let credentials be event’s authCredentials.

      3. If event’s cancel is true, then set result’s cancel to true.

      4. Otherwise, if credentials is a Promise, then:

        1. Let signal be event’s options["signal"].

        2. Let done be false.

        3. If signal is an AbortSignal, then add the following abort algorithm to signal:

          1. Set result’s cancel to true.

          2. Set done to true.

        4. Upon fulfillment of credentials, run the following steps that take a resolvedValue:

          1. If resolvedValue is a dictionary containing two keys equal to "username" and "password", and result’s authCredentials is undefined, then set result’s authCredentials to resolvedValue.

          2. Set done to true.

        5. Upon rejection of credentials, run the following steps:

          1. Set done to true.

          Note: A rejected promise will not cancel the request, but no credentials will be provided to the server.

        6. Wait in parallel for done to equal true, then queue a global task on the DOM manipulation task source of embedderGlobal that will run continue the remaining steps of this algorithm.

      5. Decrement pendingHandlerCount.

  10. If blocking is true, then wait in parallel for pendingHandlerCount to equal 0, then queue a global task on the networking task source of request’s client’s global object that will continue the remaining steps of this algorithm.

  11. Return result.

To process beforeRedirect events given a request request and a response response, run the following steps:
  1. Let interceptors be the result of getting the applicable WebRequestInterceptors of request.

  2. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  3. For each interceptor in interceptors:

    1. Let event be a new WebRequestBeforeRedirectEvent named beforeredirect.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s response to the result of creating a WebRequestResponse object given request, response, and interceptor’s includeHeaders.

    4. Let internalResponse be response, if response is not a filtered response; otherwise response’s internal response.

    5. Set event’s response’s redirectURL to internalResponse’s location URL given request’s current URL’s fragment.

    6. Queue a global task on the DOM manipulation task source of embedderGlobal that will dispatch event to interceptor.

To process responseStarted events given a request request and a response response, run the following steps:
  1. Let interceptors be the result of getting the applicable WebRequestInterceptors of request.

  2. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  3. For each interceptor in interceptors:

    1. Let event be a new WebRequestResponseStartedEvent named responsestarted.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s response to the result of creating a WebRequestResponse object given request, response, and interceptor’s includeHeaders.

    4. Queue a global task on the DOM manipulation task source of embedderGlobal that will dispatch event to interceptor.

To process completed events given a request request and a response response, run the following steps:
  1. Let interceptors be the result of getting the applicable WebRequestInterceptors of request.

  2. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  3. For each interceptor in interceptors:

    1. Let event be a new WebRequestCompletedEvent named completed.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s response to the result of creating a WebRequestResponse object given request, response, and interceptor’s includeHeaders.

    4. Queue a global task on the DOM manipulation task source of embedderGlobal that will dispatch event to interceptor.

To process errorOccurred events given a request request and a response response, run the following steps:
  1. Let interceptors be the result of getting the applicable WebRequestInterceptors of request.

  2. Let embedderGlobal be the relevant global object of the result of getting an environment’s embedderParent given request’s client.

  3. For each interceptor in interceptors:

    1. Let event be a new WebRequestErrorOccurredEvent named erroroccurred.

    2. Populate a WebRequestEvent given event, request, and "none".

    3. Set event’s error to an implementation-defined error message describing the error in response.

    4. Queue a global task on the DOM manipulation task source of embedderGlobal that will dispatch event to interceptor.

To populate a WebRequestEvent given a WebRequestEvent event, a request request, and a RequestedHeaders requestedHeaders, run the following steps:
  1. Let environmentSettingsObject be request’s client.

  2. Set the following fields on event:

    frameId

    -1

  3. Let window be the owning Window of an environment settings object given environmentSettingsObject.

  4. If window is not null, then:

    1. Let parentNavigable be window’s navigable’s parent.

    2. Update the following fields of event:

      documentId

      environmentSettingsObject’s id.

      documentLifecycle

      The result of getting the DocumentLifecycle of a Document given window’s associated document.

      frameId

      The frameId of window’s navigable.

      frameType

      "fenced-frame" if window’s browsing context’s fenced frame config instance is non-null, "outermost-frame" if parentNavigable is null, "sub-frame" otherwise.

    3. If parentNavigable is not null, then update the following values of event:

      parentDocumentId

      The id of parentNavigable’s active document’s relevant global object.

      parentFrameId

      The frameId of parentNavigable.

  5. Set event’s request to a new WebRequestRequest with the following fields:

    url

    The last element of request’s URL list.

    method

    request’s method.

    initiator

    The serialization of an origin given request’s origin.

    Note: An opaque origin will result in initiator being set to the DOMString "null".

    type

    The result of calling get a request’s ResourceType given request.

    id

    request’s requestId.

  6. If requestedHeaders is not "none", then set event’s request’s headers to the result of calling convert a header list to a Headers object given request’s header list, requestedHeaders, and isRequest equal to true.

To get the DocumentLifecycle of a Document document, run the following steps:
  1. If document’s unload counter > 0, then return "pending-deletion".

  2. If document’s node navigable is non-null and is a prerendering navigable, then return "prerender".

  3. If document is salvageable and does not have its page showing, return "cached".

  4. Return "active".

To create a WebRequestResponse object given a request request, a response response, and a RequestedHeaders requestedHeaders, run the following steps:
  1. Let response be a new WebRequestResponse with the following fields:

    statusCode

    response’s status.

    statusLine

    response’s status message.

    fromCache

    true if response’s cache state is "local", false otherwise.

    ip

    The ip address from which response was received if the request involved a network request.

    The [FETCH] spec currently doesn’t specify storing the remote IP address used when sending request.

  2. If requestedHeaders is not "none", then set response’s headers to the result of calling convert a header list to a Headers object given response’s header list, requestedHeaders, and isRequest equal to false.

  3. Return response.

To convert a header list to a Headers object given a header list fetchHeaders, a RequestedHeaders requestedHeaders, and a boolean isRequest, run the following steps:
  1. Assert that requestedHeaders is not "none".

  2. Let headers be a new Headers object.

  3. For each fetchHeader in fetchHeaders:

    1. If all of the following conditions are met, then continue:

    2. If all of the following conditions are met, then continue:

    3. Append fetchHeader to headers.

  4. Return headers.

To get a request’s ResourceType given a request request, run the following steps:
  1. If request’s url’s scheme equals "ws" or "wss", then return "websocket".

  2. If request’s initiator equals "fetch" or "xmlhttprequest", then return "xmlhttprequest".

  3. If request’s destination equals "document", then return "main-frame".

  4. If request’s destination equals "frame" or "iframe", then return "sub-frame".

  5. If request’s destination equals "style" or "xslt", then return "stylesheet".

  6. If request’s destination equals "script", "json", "audioworklet", "paintworklet", "serviceworker", "sharedworker", or "worker", then return "script".

  7. If request’s destination equals "image", then return "image".

  8. If request’s destination equals "font", then return "font".

  9. If request’s destination equals "object" or "embed", then return "object".

  10. If request’s destination equals "audio", "track", or "video", then return "media".

  11. If request’s destination equals "report", then return "csp-report".

  12. If request’s destination equals "" and request’s keepalive is true, then return "ping".

  13. Return "other".

To create a redirect response given a DOMString redirectUrl, return a response with the following fields:
status

301

header list

« ("Location", redirectUrl) »

4.2. Monkey Patches

4.2.1. Fetch

A request has an associated requestId, which is an opaque string, randomly assigned at the request’s creation.

The main fetch algorithm is monkey patched as follows:

  1. Let request be fetchParams’s request.

  2. Let response be null.

  3. Let webRequestResult be the result of calling process beforeRequest events given request.
  4. If webRequestResult is not null, then:
    1. If webRequestResult’s cancel is true, then set response to a network error.
    2. Otherwise, if webRequestResult’s redirectURL is not an empty DOMString, then set response to the result of creating a redirect response given webRequestResult’s redirectURL.

The fetch response handover algorithm is monkey patched as follows:

  1. If response is not a network error, then call process responseStarted events given fetchParams’s request and response.
  2. Let timingInfo be fetchParams’s timing info.

  1. If fetchParams’s process response consume body is non-null, then:

  2. If response is not a network error, then call process completed events given fetchParams’s request and response.
  3. Otherwise, call process errorOccurred events given fetchParams’s request and response.

The HTTP-network-or-cache fetch algorithm is monkey patched as follows:

  1. Let the revalidatingFlag be unset.

  2. Set webRequestResult to the result of calling process beforeSendHeaders events given request.
  3. If webRequestResult is not null, then:
    1. If webRequestResult’s cancel is true, then set response to a network error.
    2. Otherwise, if webRequestResult’s requestHeaders is a non-empty list, then set request’s header list to webRequestResult’s requestHeaders’s header list.
  4. If response is not null, run these steps, but abort when fetchParams is canceled:

  5. If aborted, then return the appropriate network error for fetchParams.

  6. If response is null, then:

    1. If httpRequest’s cache mode is "only-if-cached", then return a network error.

    2. Call process sendHeaders events given request.
    3. Let forwardResponse be the result of running HTTP-network fetch given httpFetchParams, includeCredentials, and isNewConnectionFetch.

    4. Let webRequestResult be the result of calling process headersReceived events given request and forwardResponse if forwardResponse is not a network error, null otherwise.
    5. If webRequestResult is not null, then:
      1. If webRequestResult’s cancel is true, then set response to a network error.
      2. Otherwise, if webRequestResult’s redirectURL is not an empty DOMString, then set forwardResponse to the result of creating a redirect response given webRequestResult’s redirectURL.
      3. Otherwise, if webRequestResult’s responseHeaders is a non-empty list, then set forwardResponse’s header list to webRequestResult’s responseHeaders’s header list.

  1. If response’s status is 401, httpRequest’s response tainting is not "cors", includeCredentials is true, and request’s window is an environment settings object, then:

    1. If request’s use-URL-credentials flag is unset or isAuthenticationFetch is true, then:

      1. If fetchParams is canceled, then return the appropriate network error for fetchParams.

      2. Let username and password be null.

      3. Let webRequestResult be the result of calling process authRequired events given request and response.
      4. If webRequestResult is not null, then:
        1. If webRequestResult’s cancel is true, then set response to a network error.
        2. Otherwise, if webRequestResult’s authCredentials is an object, then:
          1. Set username to webRequestResult’s authCredentials["username"].
          2. Set password to webRequestResult’s authCredentials["password"].
      5. If username and password are null, then set them to the result of prompting the end user for a username and password, respectively, in request’s window.
  2. If response’s status is 407, then:

    1. Let webRequestResult be the result of calling process authRequired events given request and response.
    2. If webRequestResult is not null, then:
      1. If webRequestResult’s cancel is true, then set response to a network error.
      2. Otherwise, if webRequestResult’s authCredentials is an object, then:
        1. Store webRequestResult’s authCredentials as a proxy-authentication entry.
    3. Otherwise, prompt the end user as appropriate ...

The HTTP-redirect fetch algorithm is monkey patched as follows:

  1. Call process beforeRedirect events given request and response.
  2. Let request be fetchParams’s request.

5. Context Menus API

enum ContextType {
    "all",
    "page",
    "frame",
    "selection",
    "link",
    "editable",
    "image",
    "video",
    "audio",
};

enum ItemType {
    "normal",
    "checkbox",
    "radio",
    "separator",
};

dictionary ContextMenusProperties {
    boolean checked;
    sequence<ContextType> contexts;
    sequence<(URLPattern or URLPatternInput)> documentURLPatterns;
    boolean enabled;
    DOMString parentId;
    sequence<(URLPattern or URLPatternInput)> targetURLPatterns;
    DOMString title;
    ItemType type;
};

dictionary ContextMenusCreateProperties : ContextMenusProperties {
    required DOMString id;
};

[Exposed=Window, IsolatedContext]
interface ContextMenus : EventTarget {
    Promise<undefined> create(ContextMenusCreateProperties properties);
    Promise<undefined> remove(DOMString id);
    Promise<undefined> removeAll();
    Promise<undefined> update(DOMString id, optional ContextMenusProperties properties = {});

    attribute EventHandler onclick;
    attribute EventHandler onshow;
};

[Exposed=Window, IsolatedContext]
interface MenuItemDetails {
    readonly attribute DOMString id;
    readonly attribute DOMString? parentMenuId;
    readonly attribute boolean? checked;
    readonly attribute boolean? wasChecked;
};

[Exposed=Window, IsolatedContext]
interface ContextMenusClickEvent : Event {
  readonly attribute MenuItemDetails menuItem;

  // Details about the frame the context menu is opened within.
  readonly attribute long frameId;
  readonly attribute USVString frameURL;
  readonly attribute USVString pageURL;

  // Details about the element the context menu is opened within the context of.
  readonly attribute boolean editable;
  readonly attribute USVString? linkURL;
  readonly attribute DOMString? mediaType;
  readonly attribute DOMString? selectionText;
  readonly attribute USVString? srcURL;
};

Each controlledframe has a contextMenus member, which is a ContextMenus. Each ContextMenus manages a context menu map whose keys are DOMStrings representing menu item ids, and whose values are ContextMenusProperties.

Each entry of the context menu map represents a context menu item. A context menu item represents an entry in a context menu. A context menu item should be shown to the user as the result of an implementation-defined action if determining whether the context menu item will be shown returns true given the relevant controlledframe and HTMLElement that the context menu was opened within, and the context menu item’s ContextMenusProperties.

Note: The appearance of a context menu is implementation-defined.

The values of the context menu map, which are ContextMenusProperties objects, control the conditions under which the context menu items are displayed, as well as the features and behaviors of the items.

This section is non-normative.

ContextMenusProperties has the following fields:

contexts

A list of different ContextType. If it is not empty, then the context menu item will not be shown unless the element that was clicked on to open the context menu equals one of the list items. If it is empty, then this check is ignored.

documentURLPatterns

A list of URLPatterns or URLPatternInputs. If it is not empty, then the context menu item will not be shown unless the URL of the embedded document matches a URLPattern with one of the list items. If it is empty, then this check is ignored.

targetURLPatterns

A list of URLPatterns or URLPatternInputs. If it is not empty, then the context menu item will not be shown unless the URL of the target of the context menu matches a URLPattern with one of the list items. If it is empty, then this check is ignored.

Note: Target is the "src" attribute of img/audio/video tags and the "href" of anchor tags.

parentId

The ID of the parent context menu item. The context menu item may appear under a sub-menu of the parent.

title

The title of the menu item. This is mandatory unless type is "separator".

type

The type of the menu item.

checked

Whether the context menu item is initially checked, if the type is checkbox.

enabled

Whether the context menu item is enabled.

The create(properties) method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the following steps in parallel.

  4. Let contextMenusMap be controlledframe’s context menu map.

  5. Let id be the properties["id"].

  6. If contextMenusMap[id] exists, reject p with a TypeError and abort these steps.

  7. Set contextMenusMap[id] to properties.

  8. Resolve an embedder promise given controlledframe and p.

The remove(id) method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the following steps in parallel.

  4. Let contextMenusMap be controlledframe’s context menu map.

  5. Remove contextMenusMap[id].

  6. Resolve an embedder promise given controlledframe and p.

The removeAll() method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the following steps in parallel.

  4. Let contextMenusMap be controlledframe’s context menu map.

  5. Clear contextMenusMap.

  6. Resolve an embedder promise given controlledframe and p.

The update(id, properties) method steps are:

  1. Let p be a new promise.

  2. Let controlledframe be this.

  3. Return p and run the following steps in parallel.

  4. Let contextMenusMap be controlledframe’s context menu map.

  5. If contextMenusMap[id] does not exist, reject p with a TypeError and abort these steps.

  6. Set contextMenusMap[id] to properties.

  7. Resolve an embedder promise given controlledframe and p.

This section is non-normative.

ContextTypes represent the context of a context menu, can be following values:

frame

Applies when the user context-clicks in a nested frame, such as an iframe.

selection

Applies when part of the document is selected.

link

Applies when the user context-clicks on a link.

editable

Applies when the user context-clicks an editable element, like a textarea.

image

Applies when the user context-clicks an image.

video

Applies when the user context-clicks a video element.

audio

Applies when the user context-clicks an audio element.

page

Applies when the user context-clicks in the page, but none of the other page contexts apply (for example, the click is not on an image or a nested iframe or a link).

all

Specifying all is equivalent to the combination of all other contexts.

To determine whether the context menu item will be shown given a controlledframe element controlledframe, an HTMLElement element, and a ContextMenusProperties properties:

  1. Let documentUrl be the URL of controlledframe’s embedded document.

  2. Let targetUrl be an empty string.

  3. If element is an instance of HTMLImageElement, then set targetUrl to the src attribute of element.

  4. If element is an instance of HTMLVideoElement, then set targetUrl to the src attribute of element.

  5. If element is an instance of HTMLAudioElement, then set targetUrl to the src attribute of element.

  6. If element is an instance of HTMLAnchorElement, then set targetUrl to the href attribute of element.

  7. If properties["contexts"] is not empty:

    1. Let matchesContext be a boolean value that is initially false.

    2. For each context in properties["contexts"]:

      1. If element matches context, set matchesContext to true and break;

        ContextType matching behavior isn’t specified, though the note above has some information on the desired behavior.

    3. If matchesContext is false, return false.

  8. If properties["documentURLPatterns"] is not empty:

    1. Let matchesDocumentUrl be a boolean value that is initially false.

    2. For each urlPattern in properties["documentURLPatterns"]:

      1. If urlPattern is a URLPatternInput, then set urlPattern to a new URLPattern given urlPattern.

      2. If documentUrl matches a URLPattern, given urlPattern, then set matchesDocumentUrl to true and break;

    3. If matchesDocumentUrl is false, return false.

  9. If properties["targetURLPatterns"] is not empty:

    1. Let matchesTargetUrl be a boolean value that is initially false.

    2. For each urlPattern in properties["targetURLPatterns"]:

      1. If urlPattern is a URLPatternInput, then set urlPattern to a new URLPattern given urlPattern.

      2. If targetUrl matches a URLPattern, given urlPattern, then set matchesTargetUrl to true and break;

    3. If matchesTargetUrl is false, return false.

  10. Return true.

Note: Different user agents may implement additional conditions.

5.1. Events

ContextMenus fires the following events:

Event name Interface Fired when...
show Event a context menu is displayed to the user.
click ContextMenusClickEvent a context menu item is selected by the user.

ContextMenus supports the following event handlers (and their corresponding event handler event types) as event handler IDL attributes:

Event handlers Event handler event types
onshow show
onclick click

When one or more of a ContextMenus contextMenus’s context menu items are shown to the user, dispatch a show event to contextMenus.

When a context menu item item is selected by a user, dispatch a click event given the ContextMenus item belongs to, the HTMLElement inside the embedding document where the context menu is opened at, and item.

To dispatch a show event given a ContextMenus contextMenus, dispatch a new Event named show to contextMenus.
To dispatch a click event given ContextMenus contextMenus, HTMLElement element, and menuItem as the context menu item that was clicked, run the following steps:
  1. Let id be the key of the context menu map’s entry that is associated with menuItem.

  2. Let properties be the result of getting the value of the entry in context menu map given the key id.

  3. Let event be a new ContextMenusClickEvent named click.

  4. Set event["menuItem"] to a new MenuItemDetails with the following fields:

    id

    id.

    parentMenuId

    properties["parentId"].

    checked

    Whether the context menu item is checked.

    wasChecked

    The state of a checkbox or radio item before it was clicked.

  5. Set the following fields of event:

    frameId

    The frameId of element’s node navigable.

    frameURL

    The URL of the element’s node navigable.

    pageURL

    The URL of the element’s traversable navigable.

    editable

    Whether the element selected is editable, such as a text input.

    linkURL

    If element is an instance of HTMLAnchorElement, element’s href attribute.

    mediaType

    One of "image", "video", or "audio", if any, based on element.

    selectionText

    The text for the context selection, if any.

    srcURL

    The "src" attribute of element, if it exists.

  6. Dispatch event at contextMenus.

6. Usage Overview

Lorem ipsum. Insert basic info and example here.

7. Motivating Applications

This section is non-normative.

7.1. Latency-sensitive applications in virtualized sessions

In virtualized environments, users typically have a local thin client that renders a full virtual desktop. The actual desktop execution environment will be running on a remote virtualization server. If the user’s browser navigates to a latency-sensitive application (such as a video app), the rendered content will have additional latency ("lag") that makes the experience difficult or impossible for the user. This also applies for applications that record the user, such as video conferencing applications. In these latency-sensitive applications, the virtual desktop application can render the latency-sensitive content locally and overlay it on top of the rendered remote content to reduce this latency. This use case is also known as "browser content redirection."

7.2. Embedding third party web content without restriction

In a kiosk environment, applications must load content from third parties and display that content on screens within their applications. A teacher may trigger the navigation event, or it may be configured by an administrator such as a shopping mall manager. The content may prohibit embedding by iframe through the use of X-Frame-Options and CSP. An controlled frame, however, should be able to load all content, even content that prohibits embedding by iframe.

7.3. Remote display and manipulation of web content

In a kiosk environment, applications must ensure that content continues to display on screens and may need to interrupt content with their own supplied behaviors. This behavior should work without local attendance by an administrator, and ideally can be managed remotely over the network. If content were to crash, for example, these applications should observe and respond to the crash by reloading the content in a fresh embedded view.

7.4. Clearing user content after each session

In some environments, someone only uses a single device for a brief time to complete their task, like ordering in a restaurant. When their task is complete, the embedder application should be able to clear all of the local user data associated with the task and then restart the embedded instance.

7.5. Monitor for idle sessions

While users interact with embedded content, the user may not explicitly end their session. This content may assume the user is present when they have actually finished or departed without completing the task. Embedder applications want to detect when users idle over their case’s threshold and begin a fresh session.

7.6. Arbitrarily blocking navigations

While displaying embedded web content that’s not authored by the embedder, pages may link to third party web content that’s disallowed. Allowing the embedder to edit elements in embedded content through arbitrary script injection into the web content can ensure navigation cannot occur to blocked pages. The embedder can also use the Controlled Frame API to capture navigation events and ensure that only pages to approved sites can be loaded within that controlled frame.

8. Security, Privacy, and Accessibility Considerations

This section is non-normative.

8.1. Security

Controlled Frame is based upon [Isolated-Web-Apps] (IWA) and integrates with core security specs

Since Controlled Frame is a particularly powerful API, using it or even having it available makes an app a target of various types of hacking. As a result, this API is limited to use in IWA which have additional safeguards in place to protect application developers and users. The Isolated Web App explainer has this to say:

"A user agent may also force an application to adopt this threat model if the developer needs access to APIs which would make the application an appealing target for XSS or server-side attacks."

Controlled Frame makes just such an appealing target, and to expose this with caution we’re opting into IWA to guard against certain attacks. Generally, IWAs provide strong security assurances that each of the resources in an application are secure both at rest and in-transit. You can read more about IWAs security and permissions in the IWA explainer and the IWAs [High-Watermark-Permissions] explainer.

Controlled Frame integrates with [Permissions-Policy] and [Permissions]. You can read more about Permissions Policy §  12. Privacy and Security and Permissions § E Security considerations (note the entry is currently sparse).

Attacking web sites could display content that doesn’t otherwise allow itself to be embedded and trick users on non-IWAs.

Planned mitigation:

An IWA may embed another IWA (or itself) via Controlled Frame to manipulate our IWA policies somehow (e.g. an Controlled Frame embedded IWA may detect it’s being embedded due to the absence of the "controlled-frame" policy-controlled feature).

Planned mitigation:

Controlled Frame could gain access to the powerful <controlledframe> element.

An IWA that’s not expected to use Controlled Frame may attempt to embed content.

Planned mitigation:

An IWA may attempt to embed content from non-https schemes, such as 'http:' or 'isolated-app:'

Planned mitigation:

Malicious Controlled Frame could access the embedder’s running process (eg. Spectre attack)

Planned mitigation:

Controlled Frame for a given "https origin" could interact or interfere with the user’s own storage data for that https origin

Planned mitigation:

Malicious Controlled Frame could overwrite embedder’s stored data

Planned mitigation:

Malicious Controlled Frame may detect it is embedded and attempt to attack the embedder application

Planned mitigation:

Ideas:

User may not be able to verify the origin of the page being viewed in the Controlled Frame

Ideas:

Controlled Frame may exploit vulnerabilities in out-of-date browser engine

Already addressed with:

8.2. Privacy

Controlled Frame integrates with Permissions Policy and Permissions. You can read more about Permissions Policy §  12. Privacy and Security. You can read more about Permissions § E Security considerations.

For Controlled Frame specifically, we’ve identified the following privacy considerations:

8.3. Accessibility

For Controlled Frame, we’ve identified the following accessibility considerations:

9. Acknowledgements

The following people contributed to the development of this document.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[Console]
Dominic Farolino; Robert Kowalski; Terin Stock. Console Standard. Living Standard. URL: https://console.spec.whatwg.org/
[CSS-PAGE-FLOATS-3]
Johannes Wilm. CSS Page Floats. URL: https://drafts.csswg.org/css-page-floats/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[FENCED-FRAME]
Fenced Frame. Draft Community Group Report. URL: https://wicg.github.io/fenced-frame/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[FileAPI]
Marijn Kruisselbrink. File API. URL: https://w3c.github.io/FileAPI/
[GEOLOCATION]
Marcos Caceres; Reilly Grant. Geolocation. URL: https://w3c.github.io/geolocation/
[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/
[ISOLATED-CONTEXTS]
Isolated Contexts. Draft Community Group Report. URL: https://wicg.github.io/isolated-web-apps/isolated-contexts.html
[MANIFEST-APP-INFO]
Aaron Gustafson. Web App Manifest - Application Information. URL: https://w3c.github.io/manifest-app-info/
[Permissions]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[Permissions-Policy]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[STORAGE]
Anne van Kesteren. Storage Standard. Living Standard. URL: https://storage.spec.whatwg.org/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[URLPATTERN]
Ben Kelly; Jeremy Roman; 宍戸俊哉 (Shunya Shishido). URL Pattern Standard. Living Standard. URL: https://urlpattern.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest Standard. Living Standard. URL: https://xhr.spec.whatwg.org/

Informative References

[COOKIES]
A. Barth. HTTP State Management Mechanism. April 2011. Proposed Standard. URL: https://httpwg.org/specs/rfc6265.html
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
[High-Watermark-Permissions]
Robbie McElrath. Isolated Web Apps High Watermark Permissions Explainer. URL: https://github.com/WICG/isolated-web-apps/blob/main/Permissions.md
[HTTP-CACHING]
R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. HTTP Caching. June 2022. Internet Standard. URL: https://httpwg.org/specs/rfc9111.html
[Isolated-Web-Apps]
Reilly Grant. Isolated Web Apps Explainer. URL: https://github.com/WICG/isolated-web-apps/blob/main/README.md

IDL Index

[Exposed=Window, IsolatedContext]
interface HTMLControlledFrameElement : HTMLElement {
    [HTMLConstructor] constructor();

    [CEReactions] attribute USVString src;
    attribute DOMString partition;

    readonly attribute WindowProxy? contentWindow;
    readonly attribute ContextMenus contextMenus;
    readonly attribute WebRequest request;

    // Navigation methods.
    Promise<boolean> back();
    Promise<boolean> canGoBack();
    Promise<boolean> forward();
    Promise<boolean> canGoForward();
    Promise<boolean> go(long relativeIndex);
    undefined reload();
    undefined stop();

    // Scripting methods.
    Promise<undefined> addContentScripts(sequence<ContentScriptDetails> contentScriptList);
    Promise<any> executeScript(optional InjectDetails details = {});
    Promise<undefined> insertCSS(optional InjectDetails details = {});
    Promise<undefined> removeContentScripts(optional sequence<DOMString> scriptNameList);

    // Configuration methods.
    Promise<undefined> clearData(
      optional ClearDataOptions options = {},
      optional ClearDataTypeSet types = {});
    Promise<boolean> getAudioState();
    Promise<long> getZoom();
    Promise<DOMString> getZoomMode();
    Promise<boolean> isAudioMuted();
    undefined setAudioMuted(boolean mute);
    Promise<undefined> setZoom(long zoomFactor);
    Promise<undefined> setZoomMode(DOMString zoomMode);

    // Capture methods.
    Promise<undefined> captureVisibleRegion(optional ImageDetails options = {});
    undefined print();

    // Events.
    attribute EventHandler onconsolemessage;
    attribute EventHandler oncontentload;
    attribute EventHandler ondialog;
    attribute EventHandler onloadabort;
    attribute EventHandler onloadcommit;
    attribute EventHandler onloadstart;
    attribute EventHandler onloadstop;
    attribute EventHandler onnewwindow;
    attribute EventHandler onpermissionrequest;
    attribute EventHandler onsizechanged;
    attribute EventHandler onzoomchange;
};

// One of |code| or |file| must be specified but not both.
dictionary InjectDetails {
  DOMString code;
  USVString file;
};

dictionary InjectionItems {
  DOMString code;
  sequence<USVString> files;
};

enum RunAt {
  "document-start",
  "document-end",
  "document-idle",
};

dictionary ContentScriptDetails {
  required DOMString name;
  InjectionItems js;
  InjectionItems css;
  required sequence<(URLPattern or URLPatternInput)> urlPatterns;
  sequence<(URLPattern or URLPatternInput)> excludeURLPatterns;
  boolean allFrames;
  boolean matchAboutBlank;
  RunAt runAt;
};

dictionary ClearDataOptions {
  long since;
};

dictionary ClearDataTypeSet {
  boolean cache;
  boolean cookies;
  boolean fileSystems;
  boolean indexedDB;
  boolean localStorage;
  boolean persistentCookies;
  boolean sessionCookies;
};

enum ZoomMode {
  "per-origin",
  "per-view",
  "disabled"
};


// One of |code| or |file| must be specified but not both.
dictionary ImageDetails {
  DOMString format;
  DOMString quality;
};

[Exposed=Window, IsolatedContext]
interface ConsoleMessage {
  readonly attribute long level;
  readonly attribute DOMString message;
};

[Exposed=Window, IsolatedContext]
interface ConsoleMessageEvent : Event {
  constructor(DOMString type, optional ConsoleMessageEventInit eventInitDict = {});
  readonly attribute ConsoleMessage consoleMessage;
};

dictionary ConsoleMessageEventInit: EventInit {
  ConsoleMessage? consoleMessage;
};


enum DialogType {
  "alert",
  "confirm",
  "prompt"
};

[Exposed=Window, IsolatedContext]
interface DialogController {
  undefined okay(optional DOMString response);
  undefined cancel();
};

[Exposed=Window, IsolatedContext]
interface DialogMessage {
  readonly attribute DialogType messageType;
  readonly attribute DOMString messageText;
  readonly attribute DialogController dialog;
};

[Exposed=Window, IsolatedContext]
interface DialogEvent : Event {
  constructor(DOMString type, optional DialogEventInit eventInitDict = {});
  readonly attribute DialogMessage dialogMessage;
};

dictionary DialogEventInit: EventInit {
  DialogMessage? dialogMessage;
};


enum WindowOpenDisposition {
  "ignore",
  "save_to_disk",
  "current_tab",
  "new_background_tab",
  "new_foreground_tab",
  "new_window",
  "new_popup"
};

[Exposed=Window, IsolatedContext]
interface NewWindowController {
  undefined attach(HTMLControlledFrameElement newControlledFrame);
  undefined discard();
};

[Exposed=Window, IsolatedContext]
interface NewWindow {
  readonly attribute NewWindowController window;
  readonly attribute USVString targetUrl;
  readonly attribute DOMString name;
  readonly attribute WindowOpenDisposition windowOpenDisposition;
};

[Exposed=Window, IsolatedContext]
interface NewWindowEvent : Event {
  constructor(DOMString type, optional NewWindowEventInit eventInitDict = {});
  readonly attribute NewWindow newWindow;
};

dictionary NewWindowEventInit: EventInit {
  NewWindow? newWindow;
};


enum PermissionType {
  "media",
  "geolocation",
  "pointerLock",
  "download",
  "filesystem",
  "fullscreen",
  "hid",
};

[Exposed=Window, IsolatedContext]
interface PermissionRequestControllerBase {
  undefined allow();
  undefined cancel();
};

[Exposed=Window, IsolatedContext]
interface MediaPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface GeolocationPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface PointerLockPermissionRequestController: PermissionRequestControllerBase {
  readonly attribute boolean lastUnlockedBySelf;
  readonly attribute boolean userGesture;
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface DownloadPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute DOMString requestMethod;
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface FileSystemPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface FullscreenPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString origin;
};

[Exposed=Window, IsolatedContext]
interface HidPermissionRequestController : PermissionRequestControllerBase {
  readonly attribute USVString url;
};

[Exposed=Window, IsolatedContext]
interface PermissionRequest {
  readonly attribute PermissionType permission;
  readonly attribute PermissionRequestControllerBase request;
};

[Exposed=Window, IsolatedContext]
interface PermissionRequestEvent : Event {
  constructor(DOMString type, optional PermissionRequestEventInit eventInitDict = {});
  readonly attribute PermissionRequest permissionRequest;
};

dictionary PermissionRequestEventInit: EventInit {
  PermissionRequest? permissionRequest;
};


[Exposed=Window, IsolatedContext]
interface SizeChange {
  readonly attribute unsigned long oldWidth;
  readonly attribute unsigned long oldHeight;
  readonly attribute unsigned long newWidth;
  readonly attribute unsigned long newHeight;
};

[Exposed=Window, IsolatedContext]
interface SizeChangedEvent : Event {
  constructor(DOMString type, optional SizeChangedEventInit eventInitDict = {});
  readonly attribute SizeChange sizeChange;
};

dictionary SizeChangedEventInit: EventInit {
  SizeChange? sizeChange;
};


[Exposed=Window, IsolatedContext]
interface ZoomChange {
  readonly attribute float oldZoomFactor;
  readonly attribute float newZoomFactor;
};

[Exposed=Window, IsolatedContext]
interface ZoomChangeEvent : Event {
  constructor(DOMString type, optional ZoomChangeEventInit eventInitDict = {});
  readonly attribute ZoomChange zoomChange;
};

dictionary ZoomChangeEventInit: EventInit {
  ZoomChange? zoomChange;
};


[Exposed=Window, IsolatedContext]
interface ContentLoadEvent : Event {
  constructor(DOMString type, optional EventInit eventInitDict = {});
};


[Exposed=Window, IsolatedContext]
interface LoadInfo {
  readonly attribute USVString url;
  readonly attribute boolean isTopLevel;
};

[Exposed=Window, IsolatedContext]
interface LoadAbortInfo : LoadInfo{
  readonly attribute long code;
  readonly attribute DOMString reason;
};

[Exposed=Window, IsolatedContext]
interface LoadRedirectInfo{
  readonly attribute USVString oldUrl;
  readonly attribute USVString newUrl;
  readonly attribute boolean isTopLevel;
};

[Exposed=Window, IsolatedContext]
interface LoadAbortEvent : Event {
  constructor(DOMString type, optional LoadAbortEventInit eventInitDict = {});
  readonly attribute LoadAbortInfo loadAbortInfo;
};

dictionary LoadAbortEventInit: EventInit {
  LoadAbortInfo? loadAbortInfo;
};


[Exposed=Window, IsolatedContext]
interface LoadCommitEvent : Event {
  constructor(DOMString type, optional LoadCommitEventInit eventInitDict = {});
  readonly attribute LoadInfo loadInfo;
};

dictionary LoadCommitEventInit: EventInit {
  LoadInfo? loadInfo;
};


[Exposed=Window, IsolatedContext]
interface LoadStartEvent : Event {
  constructor(DOMString type, optional LoadStartEventInit eventInitDict = {});
  readonly attribute LoadInfo loadInfo;
};

dictionary LoadStartEventInit: EventInit {
  LoadInfo? loadInfo;
};


[Exposed=Window, IsolatedContext]
interface LoadStopEvent : Event {
  constructor(DOMString type, optional LoadStopEventInit eventInitDict = {});
};

dictionary LoadStopEventInit: EventInit {
};


[Exposed=Window, IsolatedContext]
interface LoadRedirectEvent : Event {
  constructor(DOMString type, optional LoadRedirectEventInit eventInitDict = {});
  readonly attribute LoadRedirectInfo loadRedirectInfo;
};

dictionary LoadRedirectEventInit: EventInit {
  LoadRedirectInfo? loadRedirectInfo;
};


enum ResourceType {
  "main-frame",
  "sub-frame",
  "stylesheet",
  "script",
  "image",
  "font",
  "object",
  "xmlhttprequest",
  "ping",
  "csp-report",
  "media",
  "websocket",
  "other",
};

enum RequestedHeaders {
  "none",
  "cors",
  "all",
};

dictionary WebRequestInterceptorOptions {
  required sequence<(URLPattern or URLPatternInput)> urlPatterns;
  sequence<ResourceType> resourceTypes = [];
  boolean blocking = false;
  boolean includeRequestBody = false;
  RequestedHeaders includeHeaders = "none";
};

[Exposed=Window, IsolatedContext]
interface WebRequest {
  WebRequestInterceptor createWebRequestInterceptor(
      WebRequestInterceptorOptions options);

  Promise<undefined> interceptorBehaviorChanged();
};

[Exposed=Window, IsolatedContext]
interface WebRequestInterceptor : EventTarget {
  attribute EventHandler onauthrequired;
  attribute EventHandler onbeforeredirect;
  attribute EventHandler onbeforerequest;
  attribute EventHandler onbeforesendheaders;
  attribute EventHandler oncompleted;
  attribute EventHandler onerroroccurred;
  attribute EventHandler onheadersreceived;
  attribute EventHandler onsendheaders;
  attribute EventHandler onresponsestarted;
};

enum DocumentLifecycle {
  "prerender",
  "active",
  "cached",
  "pending-deletion",
};

enum FrameType {
  "outermost-frame",
  "fenced-frame",
  "sub-frame",
};

[Exposed=Window, IsolatedContext]
interface UploadData {
  readonly attribute ArrayBuffer? bytes;
  readonly attribute DOMString? file;
};

[Exposed=Window, IsolatedContext]
interface RequestBody {
  readonly attribute DOMString? error;
  readonly attribute any formData;
  readonly attribute FrozenArray<UploadData>? raw;
};

[Exposed=Window, IsolatedContext]
interface WebRequestRequest {
  readonly attribute DOMString method;
  readonly attribute DOMString id;
  readonly attribute ResourceType type;
  readonly attribute USVString url;
  readonly attribute USVString? initiator;
  readonly attribute Headers? headers;
  readonly attribute RequestBody? body;
};

[Exposed=Window, IsolatedContext]
interface AuthChallenger {
  readonly attribute DOMString host;
  readonly attribute long port;
};

[Exposed=Window, IsolatedContext]
interface WebRequestAuthDetails {
  readonly attribute AuthChallenger challenger;
  readonly attribute boolean isProxy;
  readonly attribute DOMString scheme;
  readonly attribute DOMString? realm;
};

[Exposed=Window, IsolatedContext]
interface WebRequestResponse {
  readonly attribute long statusCode;
  readonly attribute DOMString statusLine;
  readonly attribute boolean fromCache;
  readonly attribute Headers? headers;
  readonly attribute DOMString? ip;
  readonly attribute USVString? redirectURL;
  readonly attribute WebRequestAuthDetails? auth;
};

[Exposed=Window, IsolatedContext]
interface WebRequestEvent : Event {
  readonly attribute WebRequestRequest request;
  readonly attribute long frameId;
  readonly attribute FrameType? frameType;
  readonly attribute DOMString? documentId;
  readonly attribute DocumentLifecycle? documentLifecycle;
  readonly attribute DOMString? parentDocumentId;
  readonly attribute long? parentFrameId;
};

dictionary WebRequestAuthCredentials {
  required DOMString username;
  required DOMString password;
};

dictionary WebRequestAuthOptions {
  AbortSignal signal;
};

[Exposed=Window, IsolatedContext]
interface WebRequestAuthRequiredEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;

  undefined setCredentials(
    Promise<WebRequestAuthCredentials> credentials,
    optional WebRequestAuthOptions options = {});
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeRedirectEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeRequestEvent : WebRequestEvent {
  undefined redirect(USVString redirectURL);
};

[Exposed=Window, IsolatedContext]
interface WebRequestBeforeSendHeadersEvent : WebRequestEvent {
  undefined setRequestHeaders((Headers or HeadersInit) requestHeaders);
};

[Exposed=Window, IsolatedContext]
interface WebRequestCompletedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestErrorOccurredEvent : WebRequestEvent {
  readonly attribute DOMString error;
};

[Exposed=Window, IsolatedContext]
interface WebRequestHeadersReceivedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;

  undefined redirect(USVString redirectURL);
  undefined setResponseHeaders((Headers or HeadersInit) responseHeaders);
};

[Exposed=Window, IsolatedContext]
interface WebRequestResponseStartedEvent : WebRequestEvent {
  readonly attribute WebRequestResponse response;
};

[Exposed=Window, IsolatedContext]
interface WebRequestSendHeadersEvent : WebRequestEvent {};

enum ContextType {
    "all",
    "page",
    "frame",
    "selection",
    "link",
    "editable",
    "image",
    "video",
    "audio",
};

enum ItemType {
    "normal",
    "checkbox",
    "radio",
    "separator",
};

dictionary ContextMenusProperties {
    boolean checked;
    sequence<ContextType> contexts;
    sequence<(URLPattern or URLPatternInput)> documentURLPatterns;
    boolean enabled;
    DOMString parentId;
    sequence<(URLPattern or URLPatternInput)> targetURLPatterns;
    DOMString title;
    ItemType type;
};

dictionary ContextMenusCreateProperties : ContextMenusProperties {
    required DOMString id;
};

[Exposed=Window, IsolatedContext]
interface ContextMenus : EventTarget {
    Promise<undefined> create(ContextMenusCreateProperties properties);
    Promise<undefined> remove(DOMString id);
    Promise<undefined> removeAll();
    Promise<undefined> update(DOMString id, optional ContextMenusProperties properties = {});

    attribute EventHandler onclick;
    attribute EventHandler onshow;
};

[Exposed=Window, IsolatedContext]
interface MenuItemDetails {
    readonly attribute DOMString id;
    readonly attribute DOMString? parentMenuId;
    readonly attribute boolean? checked;
    readonly attribute boolean? wasChecked;
};

[Exposed=Window, IsolatedContext]
interface ContextMenusClickEvent : Event {
  readonly attribute MenuItemDetails menuItem;

  // Details about the frame the context menu is opened within.
  readonly attribute long frameId;
  readonly attribute USVString frameURL;
  readonly attribute USVString pageURL;

  // Details about the element the context menu is opened within the context of.
  readonly attribute boolean editable;
  readonly attribute USVString? linkURL;
  readonly attribute DOMString? mediaType;
  readonly attribute DOMString? selectionText;
  readonly attribute USVString? srcURL;
};

Issues Index

These steps are needed to initialize History.length in the new navigable. This is an existing issue in the HTML Standard.
This algorithm doesn’t work for Shared or Service Workers because embedderParent is only defined on a navigable, and it’s not always possible to go from a non-Window environment to a navigable.
ContextType matching behavior isn’t specified, though the note above has some information on the desired behavior.