Prerendering Revamped

Draft Community Group Report,

This version:
https://wicg.github.io/nav-speculation/prerendering.html
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

This document contains a collection of specification patches for well-specified prerendering.

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. Prerendering infrastructure

1.1. Extensions to the Document interface

We’d modify [HTML]'s centralized definition of Document as follows:

partial interface Document {
    readonly attribute boolean prerendering;

    // Under "special event handler IDL attributes that only apply to Document objects"
    attribute EventHandler onprerenderingchange;
};

The onprerenderingchange attribute is an event handler IDL attribute corresponding to the prerenderingchange event handler event type. (We would update the corresponding table in [HTML], which currently only contains onreadystatechange.)

As is customary for [HTML], the definition of prerendering would be located in another section of the spec; we’d place it in the new section introduced below:

1.2. Prerendering navigables

The following section would be added as a new sub-section of [HTML]'s Navigables section.

Every navigable has a loading mode, which is one of the following:

"default"

No special considerations are applied to content loaded in this navigable

"prerender"

This navigable is displaying prerendered content

By default, a navigable's loading mode is "default". A navigable whose loading mode is "prerender" is known as a prerendering navigable. A prerendering navigable that is also a top-level traversable is known as a prerendering traversable.

Although there are only two values for the loading mode, we use a flexible structure in anticipation of other future loading modes, such as those provided by fenced frames, portals, and uncredentialed (cross-site) prerendering. It’s not yet clear whether that anticipation is correct; if, as those features gain full specifications, it turns out not to be, we will instead convert this into a boolean.

Every prerendering traversable has a prerender initial response search variance, which is a URL search variance or null, and is initially null.

document.prerendering
Returns true if the page is being presented in a non-interactive "prerendering-like" context. In the future, this would include a visible document in a <portal> element, both when loaded into it or via predecessor adoption.

The prerendering getter steps are to return true if this has a non-null node navigable that is a prerendering navigable; otherwise, false.


Every Document has prerender records, which is a list of prerender records. This is used to fulfill navigations to a given URL by instead activating the corresponding prerendering traversable.

A prerender record is a struct with the following items:

A prerender record prerenderRecord matches a URL given a URL url if the following algorithm returns true:
  1. If prerenderRecord’s starting URL is equal to url, return true.

  2. Let searchVariance be prerenderRecord’s prerendering traversable's prerender initial response search variance.

  3. If searchVariance is not null:

    1. If prerenderRecord’s starting URL and url are equivalent modulo search variance given searchVariance, return true.

  4. Return false.

A prerender record prerenderRecord is expected to match a URL given a URL url if the following algorithm returns true:
  1. If prerenderRecord matches a URL given url, return true.

  2. If prerenderRecord’s prerendering traversable's prerender initial response search variance is null:

    1. Let expectedSearchVariance be prerenderRecord’s No-Vary-Search hint.

    2. If prerenderRecord’s starting URL and url are equivalent modulo search variance given expectedSearchVariance, return true.

  3. Return false.

Every Document has a post-prerendering activation steps list, which is a list where each item is a series of algorithm steps. For convenience, we define the post-prerendering activation steps list for any platform object platformObject as:

If platformObject is a node
  1. Return platformObject’s node document's post-prerendering activation steps list.

Otherwise
  1. Assert: platformObject’s relevant global object is a Window object.

  1. Return platformObject’s relevant global object's associated Document's post-prerendering activation steps list.

Every Document has an activation start time, which is initially a DOMHighResTimeStamp with a time value of zero.

User agents may choose to initiate prerendering without a referrer document, for example as a result of the address bar or other browser user interactions.

To start user-agent initiated prerendering given a URL startingURL:

  1. Assert: startingURL’s scheme is an HTTP(S) scheme.

  2. Let prerenderingTraversable be the result of creating a new top-level traversable given null and the empty string.

  3. Set prerenderingTraversable’s loading mode to "prerender".

  4. Navigate prerenderingTraversable to startingURL using prerenderingTraversable’s active document.

    We treat this initial navigations as prerenderingTraversable navigating itself, which will ensure all relevant security checks pass.

  5. Let record be a prerender record with starting URL startingURL and prerendering traversable prerenderingTraversable.

  6. When the user indicates they wish to commit a navigation to an activationURL which is a URL such that record matches a URL given activationURL:

    1. If the user indicates they wish to create a new user-visible top-level traversable while doing so (e.g., by pressing Shift+Enter after typing in the address bar):

      1. Update the successor for activation given prerenderingTraversable, startingURL, and activationURL.

      2. Update the user agent’s user interface to present prerenderingTraversable, e.g., by creating a new tab/window.

      3. Finalize activation for prerenderingTraversable given startingURL’s origin.

    2. Alternately, if the user indicates they wish to commit the navigation into an existing top-level traversable predecessorTraversable (e.g., by pressing Enter after typing in the address bar):

      1. Activate prerenderingTraversable in place of predecessorTraversable given "push", startingURL, and activationURL.

The user might never indicate such a commitment, or might take long enough to do so that the user agent needs to reclaim the resources used by the prerender for some more immediate task. In that case the user agent can destroy prerenderingTraversable.

To start referrer-initiated prerendering given a URL startingURL, a Document referrerDoc, a referrer policy referrerPolicy, and a URL search variance nvsHint:
  1. Assert: startingURL’s scheme is an HTTP(S) scheme.

  2. If referrerDoc’s node navigable is not a top-level traversable, then return.

    Currently, prerendering from inside a child navigable is not specified or implemented. Doing so would involve tricky considerations around how the prerendered navigable appears in the navigable tree.

  3. If referrerDoc’s browsing context is an auxiliary browsing context, then return.

    This avoids having to deal with any potential opener relationship between the prerendering traversable and referrerDoc’s browsing context's opener browsing context.

  4. If referrerDoc’s origin is not same site with startingURL’s origin, then return.

    Currently, cross-site prerendering is not specified or implemented, although we have various ideas about how it could work in this repository’s explainers.

  5. For each record of referrerDoc’s prerender records:

    1. If record’s starting URL is startingURL, then return.

  6. Let prerenderingTraversable be the result of creating a new top-level traversable.

  7. Set prerenderingTraversable’s loading mode to "prerender".

  8. Let prerenderRecord be a prerender record with starting URL startingURL, No-Vary-Search hint nvsHint, start time the current high resolution time for the relevant global object of referrerDoc, and prerendering traversable prerenderingTraversable.

  9. Append prerenderRecord to referrerDoc’s prerender records.

  10. Set prerenderingTraversable’s remove from referrer to be an algorithm which removes prerenderRecord from referrerDoc’s prerender records.

    As with all top-level traversables, the prerendering traversable can be destroyed for any reason, for example if it becomes unresponsive, performs a restricted operation, or if the user agent believes prerendering takes too many resources.

  11. Navigate prerenderingTraversable to startingURL using referrerDoc, with referrerPolicy set to referrerPolicy.

To update the successor for activation given a prerendering traversable successorTraversable, a URL startingURL, and a URL activationURL:

  1. Let successorDocument be successorTraversable’s active document.

  2. If startingURL equals successorDocument’s URL and startingURL does not equal activationURL:

    1. Assert: successorDocument can have its URL rewritten to activationURL.

    2. Let navigation be successorDocument’s relevant global object's navigation API.

    3. Let continue be the result of firing a push/replace/reload navigate event at navigation given "replace", activationURL, and true.

    4. If continue is true, run the URL and history update steps given successorDocument and activationURL.

    This allows for the URL of the prerender to match the URL actually navigated to, in the case of inexact matching based on `No-Vary-Search`.

To activate a prerendering traversable successorTraversable in place of a top-level traversable predecessorTraversable given a history handling behavior historyHandling, a URL startingURL, a URL activationURL, an optional navigation ID navigationId:

  1. Assert: successorTraversable’s active document's is initial about:blank is false.

  2. If navigationId is not given, then let navigationId be the result of generating a random UUID.

  3. Let referrerOrigin be predecessorTraversable’s active document's origin.

  4. Set predecessorTraversable’s ongoing navigation to navigationId.

    This will have the effect of aborting any other ongoing navigations of predecessorTraversable.

  5. In parallel, run these steps:

    1. Let unloadPromptCanceled be the result of checking if unloading is user-canceled for predecessorTraversable’s active document's inclusive descendant navigables.

    2. If unloadPromptCanceled is true, or predecessorTraversable’s ongoing navigation is no longer navigationId, then abort these steps.

    3. Queue a global task on the navigation and traversal task source given predecessorTraversable’s active window to abort predecessorTraversable’s active document.

    4. Update the successor for activation given successorTraversable, startingURL, and activationURL.

    5. Append session history traversal steps to predecessorTraversable to perform the following steps:

      1. Assert: successorTraversable’s current session history step is 0.

      2. Assert: successorTraversable’s session history entries's size is 1.

      3. Let successorEntry be successorTraversable’s session history entries[0].

      4. Remove successorEntry from successorTraversable’s session history entries.

        At this point, successorTraversable is empty and can be unobservably destroyed. (As long as the implementation takes care to keep successorEntry, including its document and that Document's browsing context, alive for the next step.)

      5. Finalize a cross-document navigation given predecessorTraversable, historyHandling, and successorEntry.

        Because we have moved the entire session history entry, including the contained document's browsing context, this means traversing across the associated history entries will cause a browsing context group switch, similar to what happens with the `Cross-Origin-Opener-Policy` header. Importantly, this will sever any opener relationships, in the same way as COOP. Such traversal includes both the actual activation process here, where we make successorEntry active, but also e.g. the user pressing the back button (or the page using history.back()) after activation, which will switch in the other direction.

      6. Update the user agent’s user interface to reflect this change, e.g., by updating the tab/window contents and the browser chrome.

      7. Finalize activation for successorTraversable given referrerOrigin.

      8. Perhaps we should do something with WebDriver BiDi here? See w3c/webdriver-bidi#321. Right now the pre-in parallel part of the navigate algorithm will send WebDriver BiDi navigation started as if it were a normal navigation, but then nothing will happen to indicate the navigation finishes.

To finalize activation of a top-level traversable traversable given an origin origin:
  1. For each navigable of traversable’s active document's inclusive descendant navigables, queue a global task on the navigation and traversal task source, given navigable’s active window, to perform the following steps:

    1. For each originhintSet in navigable’s prerender-scoped Accept-CH cache:

      1. Set Accept-CH cache[origin] to hintSet.

    2. Let doc be navigable’s active document.

    3. We should really propagate the loading mode change here, in the posted task. This is where implementations would update what is returned by document.prerendering. However, right now it lives on the traversable, so it gets magically updated when we move over the session history entry. Probably we need to move it to the Document.

    4. If doc’s origin is the same as origin, then set doc’s activation start time to the current high resolution time for doc’s relevant global object.

    5. Fire an event named prerenderingchange at doc.

    6. For each steps in doc’s post-prerendering activation steps list:

      1. Run steps.

        These steps might return something, like a Promise. That is just an artifact of how the spec is modified; such return values can always be ignored.

      2. Assert: running steps did not throw an exception.

      The order here is observable for Documents that share an event loop, but not for those in separate event loops.

A prerendering traversable has an associated remove from referrer, which is null or an algorithm with no arguments, initially set to null.

To ensure that the references for a prerendering traversable are cleared once it is destroyed, modify destroy a top-level traversable by appending the following step:

  1. If traversable is a prerendering traversable and traversable’s remove from referrer is not null, then call traversable’s remove from referrer.

1.3. Modifications to creating navigables

To ensure that any child navigables inherit their parent's loading mode, modify create a new child navigable by appending the following step:
  1. Set navigable’s loading mode to element’s node navigable's loading mode.

To find a matching complete prerender record given a Document predecessorDocument and URL url:
  1. Let recordToUse be null.

  2. For each record of predecessorDocument’s prerender records:

    1. If record’s prerendering traversable's active document's is initial about:blank is true, then continue.

    2. If record’s starting URL is equal to url:

      1. Set recordToUse to record.

      2. Break.

    3. If recordToUse is null and record matches a URL given url:

      1. Set recordToUse to record.

  3. If recordToUse is not null:

    1. Remove recordToUse from predecessorDocument’s prerender records.

  4. Return recordToUse.

To wait for a matching prerendering record given a navigable navigable, a URL url, a string cspNavigationType, and a POST resource, string, or null documentResource:
  1. Assert: this is running in parallel.

  2. If any of the following conditions hold:

    then return null.

  3. Let predecessorDocument be navigable’s active document.

  4. Let cutoffTime be null.

  5. While true:

    1. Let completeRecord be the result of finding a matching complete prerender record given predecessorDocument and url.

    2. If completeRecord is not null, return completeRecord.

    3. Let potentialRecords be an empty list.

    4. For each record of predecessorDocument’s prerender records:

      1. If all of the following are true, then append record to potentialRecords:

    5. If potentialRecords is empty, return null.

    6. Wait until the ongoing navigation of the prerendering traversable of any element of predecessorDocument’s prerender records changes.

    7. If cutoffTime is null and any element of potentialRecords has a prerendering traversable whose active document's is initial about:blank is false, set cutoffTime to the current high resolution time for the relevant global object of predecessorDocument.

    See also: wait for a matching prefetch record. The logic for blocking on ongoing prerenders is similar to the prefetch case.

Patch the navigate algorithm to allow the activation of a prerendering traversable in place of a normal navigation as follows:

In navigate, insert the following steps as the first ones after we go in parallel:
  1. Let matchingPrerenderRecord be the result of waiting for a matching prerendering record given navigable, url, cspNavigationType, and documentResource.

  2. If matchingPrerenderRecord is not null, then:

    1. Let matchingPrerenderedNavigable be matchingPrerenderRecord’s prerendering traversable.

    2. Let startingURL be matchingPrerenderRecord’s starting URL.

    3. Activate matchingPrerenderedNavigable in place of navigable given historyHandling, startingURL, url, and navigationId.

    4. Abort these steps.

In create navigation params by fetching, add the following step near the top of the algorithm:
  1. Let initiatorOrigin be entry’s document state's initiator origin.

Append the following steps after the first sub-step under "While true:":

  1. If navigable is a prerendering navigable and currentURL’s origin is not same site with initiatorOrigin, then:

    1. If navigable is a top-level traversable, then return null.

    2. Otherwise, the user agent must wait to continue this algorithm until navigable’s loading mode becomes "normal". At any point during this wait (including immediately), it may instead choose to destroy navigable’s top-level traversable and return null from this algorithm.

Append the following steps toward the end of the algorithm, after the steps which check locationURL:

  1. If navigable is a prerendering navigable, and responseOrigin is not same origin with initiatorOrigin, then:

    1. Let loadingModes be the result of getting the supported loading modes for response.

    2. If loadingModes does not contain `credentialed-prerender`, then return null.

      In the future we could possibly also allow the uncredentialed-prerender token to work here. However, since that is primarily intended for the cross site case, which isn’t specified or implemented yet, we don’t want to encourage its use in the wild, so for now we specify that prerendering fails if only the uncredentialed-prerender token is found.

In attempt to populate the history entry’s document, append the following after the steps which establish the value of failure, but before the other steps which act on it:
  1. If navigable is a prerendering navigable, and any of the following hold:

    • failure is true;

    • navigationParams’s request is null;

    • navigationParams’s request's current URL's scheme is not a HTTP(S) scheme;

    • navigationParams’s response does not support prefetch;

      Responses which are ineligible for prefetch, e.g. because they have an error status, are not eligible for prerender either. A future version of this specification might allow responses to more clearly delineate the two.
    • navigationParams’s response has a `Content-Disposition` header specifying the attachment disposition type; or

    • navigationParams’s response's status is 204 or 205,

    then:

    1. Destroy navigable’s top-level traversable.

    2. Return.

  2. If navigable is a prerendering traversable and navigable’s prerender initial response search variance is null:

    1. Set navigable’s prerender initial response search variance to the result of obtaining a URL search variance given navigationParams’s response.

In hand-off to external software, prepend the following step:
  1. If navigable is a prerendering navigable, then return without invoking the external software package.

We could also allow prerendering activations in place of redirects, not just in place of navigations. We’ve omitted that for now as it adds complexity and is not yet implemented anywhere to our knowledge.

2.3. Maintaining a trivial session history

Patch the navigate algorithm to ensure the session history of a prerendering navigable stays trivial by prepending the following step before all others:
  1. If navigable is a prerendering navigable, then set historyHandling to "replace".

Patch the URL and history update steps by adding the following step after step 1:
  1. If navigable is a prerendering navigable, then set historyHandling to "replace".

2.4. Interaction with worker lifetime

In The worker’s lifetime, modify the definition of active needed worker, to count workers with a prerendering navigable owner as not active:

A worker is said to be an active needed worker if any of its owners are either Document objects that are fully active and their node navigable is not a prerendering navigable, or active needed workers.

This means that the worker’s script would load, but the execution would be suspended until the document is activated.

2.5. Cleanup upon discarding a Document

Modify the destroy algorithm for Documents by appending the following step:

  1. Empty document’s post-prerendering activation steps list.

3. Interaction with other specifications and concepts

3.1. Interaction with Page Visibility

Documents in prerendering navigables always have a visibility state of "hidden".

We should probably explicitly update this, similar to how we need to update document.prerendering.

3.2. Interaction with system focus

Prerendering traversables never have system focus.

3.3. Extensions to the PerformanceNavigationTiming interface

Extend the PerformanceNavigationTiming interface as follows:

partial interface PerformanceNavigationTiming {
    readonly attribute DOMHighResTimeStamp activationStart;
};

The activationStart getter steps are:

  1. Return this's relevant global object's associated Document's activation start time.

3.4. Interaction with Client Hint Cache

We need to ensure that the Accept-CH cache, which is owned by user agent as a global storage, is not modified while prerendering, but is properly updated after activation. The following modifications ensure this.

Each prerendering navigable has a prerender-scoped Accept-CH cache, which is an ordered map of origin to client hints sets.

This stores which client hints each origin has opted into receiving, until it can be copied to the global Accept-CH cache when activation is finalized.

Modify the update the client hints set from cache algorithm, by replacing the second step with the following steps.
  1. Let navigable be settingsObject’s global object's navigable.

  2. Let originMatchingEntries be the entries in the Accept-CH cache whose origin is same origin with settingsObject’s origin.

  3. If navigable is a prerendering navigable, then:

    1. Let prerenderAcceptClientHintsCache be navigable’s prerender-scoped Accept-CH cache.

    2. Let origin be settingsObject’s origin.

    3. If prerenderAcceptClientHintsCache[origin] exists, then let originMatchingEntries be the entries in the prerenderAcceptClientHintsCache whose origin is same origin with origin.

Modify the create or override the cached client hints set algorithm, by updating the last step.
  1. Let navigable be settingsObject’s global object's navigable.

  2. If navigable is a prerendering navigable, set navigable’s prerender-scoped Accept-CH cache[origin] to hintSet.

  3. Otherwise, set Accept-CH cache[origin] to hintSet.

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

The following section would be added as a sub-section of [HTML]'s Loading web pages section.

In some cases, cross-origin web pages might not be prepared to be loaded in a novel context. To allow them to opt in to being loaded in such ways, the `Supports-Loading-Mode` HTTP response header can be used. This header is a structured header; if present its value must be one or more of the tokens listed below.

The parsing is actually done as a list of tokens, and unknown tokens will be ignored.

The `credentialed-prerender` token indicates that the response can be used to create a prerendering navigable, despite the prerendering being initiated by a cross-origin same-site referrer. Without this opt-in, such prerenders will fail, as outlined in § 2.2 Navigation fetch changes.

The `uncredentialed-prefetch` token indicates that the response is suitable to use even if a top-level navigation to this URL would ordinarily send credentials such as cookies. For instance, the response may be identical or it may be semantically equivalent (e.g., an HTML resource containing script which can update the document after navigation, when local user state is available).

To get the supported loading modes for a response response:

  1. If response is a network error, then return an empty list.

  2. Let slmHeader be the result of getting a structured field value given `Supports-Loading-Mode` and "list" from response’s header list.

  3. Return a list containing all elements of slmHeader that are tokens.

5. Preventing intrusive behaviors

Various behaviors are disallowed in prerendering navigables because they would be intrusive to the user, since the prerendered content is not being actively interacted with.

5.1. Downloading resources

Modify the download the hyperlink algorithm to ensure that downloads inside prerendering navigable are delayed until activation, by inserting the following before the step which goes in parallel:

  1. If subject’s node navigable is a prerendering navigable, then append the following step to subject’s post-prerendering activation steps list and return.

5.2. User prompts

Modify the cannot show simple dialogs algorithm, given a Window window, by prepending the following step:
  1. If window’s navigable is a prerendering navigable, then return true.

Modify the print() method steps by prepending the following step:
  1. If this's navigable is a prerendering navigable, then return.

5.3. Delaying async API results

Many specifications need to be patched so that, if a given algorithm invoked in a prerendering navigable, most of its work is deferred until the navigable’s top-level traversable is activated. This is tricky to do uniformly, as many of these specifications do not have great hygeine around using the event loop. Nevertheless, the following sections give our best attempt.

5.3.1. The [DelayWhilePrerendering] extended attribute

To abstract away some of the boilerplate involved in delaying the action of asynchronous methods until activation, we introduce the [DelayWhilePrerendering] Web IDL extended attribute. It indicates that when a given method is called in a prerendering navigable, it will immediately return a pending promise and do nothing else. Only upon activation will the usual method steps take place, with their result being used to resolve or reject the previously-returned promise.

The [DelayWhilePrerendering] extended attribute must take no arguments, and must only appear on a regular or static operation whose return type is a promise type or undefined, and whose exposure set contains only Window.

The method steps for any operation annotated with the [DelayWhilePrerendering] extended attribute are replaced with the following:
  1. Let realm be the current realm.

  2. If the operation in question is a regular operation, then set realm to the relevant realm of this.

  3. If realm’s global object's navigable is a prerendering navigable, then:

    1. Let promise be a new promise, created in realm.

    2. Append the following steps to this's post-prerendering activation steps list:

      1. Let result be the result of running the originally-specified steps for this operation, with the same this and arguments.

      2. Resolve promise with result.

    3. If this operation’s return type is a promise type, then return promise.

  4. Otherwise, return the result of running the originally-specified steps for this operation, with the same this and arguments.

5.3.2. Service Workers

Add [DelayWhilePrerendering] to update(), unregister(), register(scriptURL, options), postMessage(message, transfer), and postMessage(message, options).

This allows prerendered page to take advantage of existing service workers, but not have any effect on the state of service worker registrations.

5.3.3. BroadcastChannel

Add [DelayWhilePrerendering] to postMessage().

5.3.4. Geolocation API

Modify the getCurrentPosition() method steps by prepending the following step:
  1. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return.

Modify the watchPosition() method steps by prepending the following step:
  1. If this's relevant global object's navigable is a prerendering navigable, then:

    1. Let watchId be an implementation-defined unsigned long, and note it as a post-prerendering activation geolocation watch process ID.

    2. Append the following steps to this's post-prerendering activation steps list, with the modification that the watchId generated must use watchId as its ID, and return watchId.

Modify the clearWatch(watchId) method steps by prepending the following step:
  1. If this's relevant global object's navigable is a prerendering navigable, then:

    1. If watchId is a post-prerendering activation geolocation watch process ID, then remove its corresponding steps from this's post-prerendering activation steps list.

    2. Return.

5.3.5. Web Serial API

Add [DelayWhilePrerendering] to requestPort().

TODO: the below could probably be done by generalizing [DelayWhilePrerendering] to use owner document while in dedicated workers.

Modify the getPorts() method steps by inserting the following steps after the initial creation of promise:
  1. Let document be this's relevant global object's associated Document, if this's relevant global object is a Window, or this's relevant global object's owner document, if this's relevant global object is a DedicatedWorkerGlobalScope.

  2. If document is null, then return a promise rejected with a "SecurityError" DOMException.

  3. If document’s node navigable is a prerendering navigable, then append the following steps to document’s post-prerendering activation steps list and return promise.

5.3.6. Notifications API

Add [DelayWhilePrerendering] to requestPermission().

Modify the Notification() constructor steps by replacing the step which goes in parallel with the following:
  1. If this's relevant global object's navigable is a prerendering navigable, then append these steps to this's post-prerendering activation steps list. Otherwise, run these steps in parallel.

Modify the permission static getter steps by replacing them with the following:
  1. If the current global object's navigable is a prerendering navigable, then return "default".

    This allows implementations to avoid looking up the actual permission state, which might not be synchronously accessible especially in the case of prerendering navigables. Web developers can then call Notification.requestPermission(), which per the above modifications will only actually do anything after activation. At that time we might discover that the permission is "granted" or "denied", so the browser might not actually ask the user like would normally be the case with "default". But that’s OK: it’s not observable to web developer code.

  2. Otherwise, get the notifications permission state and return it.

5.3.7. Web MIDI API

Add [DelayWhilePrerendering] to requestMIDIAccess().

5.3.8. Idle Detection API

Add [DelayWhilePrerendering] to start().

The other interesting method, IdleDetector.requestPermission(), is gated on transient activation. However, even if permission was previously granted for the origin in question, we delay starting any idle detectors while prerendering.

5.3.9. Generic Sensor API

Modify the start() method steps by inserting the following steps after the state is set to "activating":
  1. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return.

  2. If this.[[state]] is "idle", then return.

    This ensures that if this portion of the algorithm was delayed due to prerendering, and in the meantime stop() was called, we do nothing upon activating the prerender.

  3. Assert: this.[[state]] is "activating".

5.3.10. Web NFC

Add [DelayWhilePrerendering] to write() and scan().

5.3.11. Battery Status API

Modify the getBattery() method steps by prepending the following step:
  1. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return this.[[BatteryPromise]].

5.3.12. Screen Orientation API

Modify the apply orientation lock algorithm steps by overwriting the first few steps, before it goes in parallel, as follows:
  1. Let promise be a new promise.

  2. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return promise.

  3. If the user agent does not support locking the screen orientation, then reject promise with a "NotSupportedError" DOMException and return promise.

  4. If the document's active sandboxing flag set has the sandboxed orientation lock browsing context flag set, or the user agent doesn’t meet the pre-lock conditions to perform an orientation change, then reject promise with a "SecurityError" DOMException and return promise.

  5. Set the document's [[orientationPendingPromise]] to promise.

Add [DelayWhilePrerendering] to unlock().

This latter modification is necessary to ensure that code that calls screen.orientation.lock() followed by screen.orientation.unlock() produces the expected results.

5.3.13. Gamepad

Modify the getGamepads() method steps by prepending the following step:
  1. If this's relevant global object's navigable is a prerendering navigable, then return an empty sequence.

Modify the discussion of the gamepadconnected and gamepaddisconnected events to specify that the user agent must not dispatch these events if the Window object’s navigable is a prerendering navigable.

Modify the gamepadconnected section to indicate that every Document document’s post-prerendering activation steps list should gain the following steps:
  1. If document is allowed to use the "gamepad" feature, and document’s relevant settings object is a secure context, and any gamepads are connected, then for each connected gamepad, fire an event named gamepadconnected at document’s relevant global object using GamepadEvent, with its gamepad attribute initialized to a new Gamepad object representing the connected gamepad.

5.3.14. Encrypted Media Extensions

Add [DelayWhilePrerendering] to requestMediaKeySystemAccess().

5.3.15. Media Autoplay

Modify the playing the media resource section to indicate that the the current playback position of a HTMLMediaElement must increase monotonically only when the Document is not prerendering.

5.3.16. Media Capture and Streams

Add [DelayWhilePrerendering] to getUserMedia(), getUserMedia() and enumerateDevices().

Modify the MediaDevices section by prepending the following step to the device change notification steps:
  1. If this's relevant global object's navigable is a prerendering navigable, then return.

5.3.17. Web Audio API

The concept, allowed to start, is used in the spec, but details are left implementation-defined. To restrict auto playback while prerendering, add the following rule in the AudioContext interface section.

The AudioContext is never allowed to start while prerendering.

Also modify AudioContext() constructor steps to have the following step before returning the constructed object.

  1. Else if context is not allow to start only due to the prerendering, then append the following steps to context’s post-prerendering activation steps list.

    1. Set the [[control thread state]] on context to running.

    2. Queue a control message to resume context.

Developers might call resume() while prerendering. In that case, the context is not allow to start, and appends a Promise to [[pending resume promises]]. The Promise will be resolved in the activation steps above.

The AudioScheduledSourceNode interface has a start(when) method and the AudioBufferSourceNode interface has a start(when, offset, duration) method that might affect [[control thread state]]. But they don’t as the context is not allowed to start and it prevents the algorithm from setting the [[control thread state]]. This would not matter because the node can automatically start when the context starts.

5.3.18. Audio Output Devices API

Add [DelayWhilePrerendering] to selectAudioOutput().

5.3.19. Push API

Add [DelayWhilePrerendering] to subscribe().

5.3.20. Background Fetch

Add [DelayWhilePrerendering] to fetch().

5.3.21. Storage API

Add [DelayWhilePrerendering] to persist().

5.3.22. WebUSB API

Add [DelayWhilePrerendering] to getDevices() and requestDevice().

5.3.23. Web Bluetooth

Add [DelayWhilePrerendering] to getDevices() and requestDevice().

5.3.24. WebHID API

Add [DelayWhilePrerendering] to getDevices() and requestDevice().

5.3.25. WebXR Device API

Add [DelayWhilePrerendering] to requestSession().

5.3.26. Credential Management

Add [DelayWhilePrerendering] to get(), store(), and create().

5.3.27. Web Speech API

Add [DelayWhilePrerendering] to speak(utterance), cancel(), pause(), and resume().

Add [DelayWhilePrerendering] to start(), stop(), and abort().

5.3.28. Web Locks API

Add [DelayWhilePrerendering] to request(name, callback), request(name, options, callback), and query().

5.3.29. Custom Scheme Handlers

Modify the registerProtocolHandler(scheme, url) method steps by overwriting the first few steps, before it goes in parallel, as follows:
  1. Let (normalizedScheme, normalizedURLString) be the result of running normalize protocol handler parameters with scheme, url, and this's relevant settings object.

  2. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return.

Modify the unregisterProtocolHandler(scheme, url) method steps by overwriting the first few steps, before it goes in parallel, as follows:
  1. Let (normalizedScheme, normalizedURLString) be the result of running normalize protocol handler parameters with scheme, url, and this's relevant settings object.

  2. If this's relevant global object's navigable is a prerendering navigable, then append the following steps to this's post-prerendering activation steps list and return.

5.4. Implicitly restricted APIs

Some APIs do not need modifications because they will automatically fail or no-op without a property that a prerendering navigable, its active window, or its active document will never have. These properties include:

We list known APIs here for completeness, to show which API surfaces we’ve audited.

APIs that require transient activation or sticky activation:

APIs that require system focus:

APIs that require the "visible" visibility state:

More complicated cases:

6. Security considerations

See Security considerations (Speculation Rules).

For the integration of this spec with No-Vary-Search, see No-Vary-Search Security considerations.

Add security considerations that are specific to prerendering. See issue #319.

7. Privacy considerations

See Privacy considerations (Speculation Rules).

For the integration of this spec with No-Vary-Search, see No-Vary-Search Privacy considerations.

Add privacy considerations that are specific to prerendering. See issue #319.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[AUDIO-OUTPUT]
Justin Uberti; Guido Urdaneta; youenn fablet. Audio Output Devices API. URL: https://w3c.github.io/mediacapture-output/
[BACKGROUND-FETCH]
Background Fetch. cg-draft. URL: https://wicg.github.io/background-fetch/
[BATTERY-STATUS]
Anssi Kostiainen. Battery Status API. URL: https://w3c.github.io/battery/
[CLIENT-HINTS-INFRASTRUCTURE]
Client Hints Infrastructure. cg-draft. URL: https://wicg.github.io/client-hints-infrastructure/
[CLIPBOARD-APIS]
Gary Kacmarcik; Anupam Snigdha. Clipboard API and events. URL: https://w3c.github.io/clipboard-apis/
[CREDENTIAL-MANAGEMENT-1]
Nina Satragno; Marcos Caceres. Credential Management Level 1. URL: https://w3c.github.io/webappsec-credential-management/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ENCRYPTED-MEDIA-2]
Joey Parrish; Greg Freedman. Encrypted Media Extensions. URL: https://w3c.github.io/encrypted-media/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[FILE-SYSTEM-ACCESS]
File System Access. cg-draft. URL: https://wicg.github.io/file-system-access/
[FULLSCREEN]
Philip Jägenstedt. Fullscreen API Standard. Living Standard. URL: https://fullscreen.spec.whatwg.org/
[GAMEPAD]
Steve Agoston; Matthew Reynolds. Gamepad. URL: https://w3c.github.io/gamepad/
[GENERIC-SENSOR]
Rick Waldron. Generic Sensor API. URL: https://w3c.github.io/sensors/
[GEOLOCATION]
Marcos Caceres; Reilly Grant. Geolocation. URL: https://w3c.github.io/geolocation/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[IDLE-DETECTION]
Reilly Grant. Idle Detection API. CG-DRAFT. URL: https://wicg.github.io/idle-detection/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[KEYBOARD-LOCK]
Keyboard Lock. cg-draft. URL: https://wicg.github.io/keyboard-lock/
[MEDIACAPTURE-STREAMS]
Cullen Jennings; et al. Media Capture and Streams. URL: https://w3c.github.io/mediacapture-main/
[NAVIGATION-TIMING-2]
Yoav Weiss; Noam Rosenthal. Navigation Timing Level 2. URL: https://w3c.github.io/navigation-timing/
No-Vary-Search. Draft Community Group Report. URL: https://wicg.github.io/nav-speculation/no-vary-search.html
[NOTIFICATIONS]
Anne van Kesteren. Notifications API Standard. Living Standard. URL: https://notifications.spec.whatwg.org/
[PAYMENT-REQUEST]
Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. Payment Request API. URL: https://w3c.github.io/payment-request/
[PICTURE-IN-PICTURE]
Francois Beaufort. Picture-in-Picture. URL: https://w3c.github.io/picture-in-picture/
[POINTERLOCK-2]
Mustaq Ahmed; Vincent Scheib. Pointer Lock 2.0. URL: https://w3c.github.io/pointerlock/
[PRESENTATION-API]
Mark Foltz. Presentation API. URL: https://w3c.github.io/presentation-api/
[PUSH-API]
Peter Beverloo; Martin Thomson; Marcos Caceres. Push API. URL: https://w3c.github.io/push-api/
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. Referrer Policy. URL: https://w3c.github.io/webappsec-referrer-policy/
[RFC8941]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. February 2021. Proposed Standard. URL: https://httpwg.org/specs/rfc8941.html
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. Screen Capture. URL: https://w3c.github.io/mediacapture-screen-share/
[SCREEN-ORIENTATION]
Marcos Caceres. Screen Orientation. URL: https://w3c.github.io/screen-orientation/
[SCREEN-WAKE-LOCK]
Kenneth Christiansen; Marcos Caceres. Screen Wake Lock API. URL: https://w3c.github.io/screen-wake-lock/
[SERIAL]
Web Serial API. Editor's Draft. URL: https://wicg.github.io/serial/
[SERVICE-WORKERS]
Jake Archibald; Marijn Kruisselbrink. Service Workers. URL: https://w3c.github.io/ServiceWorker/
[SPEECH-API]
Web Speech API. cg-draft. URL: https://wicg.github.io/speech-api/
[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/
[WEB-BLUETOOTH]
Jeffrey Yasskin. Web Bluetooth. URL: https://webbluetoothcg.github.io/web-bluetooth/
[WEB-LOCKS]
Joshua Bell; Kagami Rosylight. Web Locks API. URL: https://w3c.github.io/web-locks/
[WEB-NFC]
Web NFC API. Draft Community Group Report. URL: https://w3c.github.io/web-nfc/
[WEB-SHARE]
Matt Giuca; Eric Willigers; Marcos Caceres. Web Share API. URL: https://w3c.github.io/web-share/
[WEBAUDIO]
Paul Adenot; Hongchan Choi. Web Audio API. URL: https://webaudio.github.io/web-audio-api/
[WebCryptoAPI]
Mark Watson. Web Cryptography API. URL: https://w3c.github.io/webcrypto/
[WEBDRIVER-BIDI]
WebDriver BiDi. Editor's Draft. URL: https://w3c.github.io/webdriver-bidi/
[WEBHID]
WebHID API. Draft Community Group Report. URL: https://wicg.github.io/webhid/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBMIDI]
Chris Wilson; Michael Wilson. Web MIDI API. URL: https://webaudio.github.io/web-midi-api/
[WEBUSB]
WebUSB API. cg-draft. URL: https://wicg.github.io/webusb/
[WEBXR]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR Device API. URL: https://immersive-web.github.io/webxr/

Informative References

[POINTERLOCK]
Vincent Scheib. Pointer Lock. URL: https://w3c.github.io/pointerlock/

IDL Index

partial interface Document {
    readonly attribute boolean prerendering;

    // Under "special event handler IDL attributes that only apply to Document objects"
    attribute EventHandler onprerenderingchange;
};

partial interface PerformanceNavigationTiming {
    readonly attribute DOMHighResTimeStamp activationStart;
};

Issues Index

The concept of a fenced frame navigable is not yet defined but we need to link to it once it exists.
Add security considerations that are specific to prerendering. See issue #319.
Add privacy considerations that are specific to prerendering. See issue #319.