Prefetch

Draft Community Group Report,

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

Abstract

Extensions to WHATWG Fetch for prefetching with partitioning in mind.

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. Concepts

In light of storage partitioning, this specification defines prefetch for navigations which would occur within the same partition (for example, top-level navigations within the same site) and for navigations which would occur in a separate partition (for example, top-level navigations to a different site).

An exchange record is a struct with the following items:

These records can be used to defer checks that would ordinarily happen during a navigate fetch, and to check for modified credentials.

A redirect chain is a list of exchange records.

To append a request request to a redirect chain redirectChain:
  1. Let cookieData be an empty list.

  2. If the user agent is not configured to block cookies for request, then set cookieData be the result of gathering cookie data for request’s URL in the cookie store associated with request.

  3. Let authenticationEntry be null.

  4. If there’s an authentication entry for request and request’s current URL does not include credentials, then set authenticationEntry to that authentication entry.

  5. Append a new exchange record whose request is a copy of request, response is null, cookie data is cookieData, and authentication entry is authenticationEntry to redirectChain.

To update the response for a redirect chain redirectChain given a request request and response response:
  1. Assert: redirectChain is not empty.

  2. Assert: redirectChain’s last element’s request is request and its response is null.

  3. Set redirectChain’s last element’s response to response.

A redirect chain redirectChain has updated credentials if the following steps return true:
  1. For each exchangeRecord of redirectChain:

    1. Let request be exchangeRecord’s request.

    2. Let cookieData be an empty list.

    3. If the user agent is not configured to block cookies for request, then set cookieData to the result of gathering cookie data for request’s URL in the cookie store associated with request.

    4. If cookieData is not identical to exchangeRecord’s cookie data, then user agents must return true. User agents may also return true if a modification to the cookies applicable to request has occurred in some other way (even though the cookie data may ultimately be identical).

      This gives freedom to use a slightly coarser algorithm, such as monitoring whether the cookies for the URL have been modified at all, or storing a hash, timestamp or revision number instead of the full cookie data.
    5. Let authenticationEntry be null.

    6. If there’s an authentication entry for request and request’s current URL does not include credentials, then set authenticationEntry to that authentication entry.

    7. If authenticationEntry is not identical to exchangeRecord’s authentication entry, then user agents must return true. User agents may also return true if a modification to the authentication entries applicable to request has occurred in some other way.

      This provides similar implementation freedom to that described above for cookies.
  2. Return false.


Each Document has a prefetch buffer, which is a list of prefetch records.

A prefetch record is a struct with the following items:

A prefetch record's response is the response of the last element of its redirect chain, or null if that list is empty.

The user agent may remove elements from the prefetch buffer even if they are not expired, e.g., due to resource constraints. Since records with expiry times in the past are never returned, they can be removed with no observable consequences.

To store a prefetch record given a Document document, URL url, referrer policy referrerPolicy, sandboxing flag set sandboxFlags, redirect chain redirectChain, and network partition key or null isolatedPartitionKey, perform the following steps.
  1. Assert: document is fully active.

  2. Let currentTime be the current high resolution time for the relevant global object of document.

  3. Let expiryTime be currentTime + 300000 (i.e., five minutes).

  4. Remove all elements whose URL equals url and referrer policy equals referrerPolicy from document’s prefetch buffer.

  5. Append a prefetch record with URL url, referrer policy referrerPolicy, sandbox flags present true if sandboxFlags is not empty and false otherwise, redirect chain redirectChain, expiry time expiryTime, and isolated partition key isolatedPartitionKey to document’s prefetch buffer.

To find a matching prefetch record given a Document document, URL url, referrer policy referrerPolicy and sandboxing flag set sandboxFlags, perform the following steps.
  1. Assert: document is fully active.

  2. Let currentTime be the current high resolution time for the relevant global object of document.

  3. For each record of document’s prefetch buffer:

    1. If record’s URL is not equal to url or record’s referrer policy is not equal to referrerPolicy, then continue.

    2. If record’s sandbox flags present is false and sandboxFlags is not empty, then continue.

      Strictly speaking, it would still be possible for this to be valid if sandbox flags have been added to the container since prefetch but those flags would not cause an error due to cross origin opener policy. This is expected to be rare and so isn’t handled.
    3. Remove record from document’s prefetch buffer.

    4. If record’s expiry time is less than currentTime, return null.

    5. If record’s redirect chain has updated credentials, return null.

    6. Return record.

  4. Return null.

It might be possible to use cache response headers to determine when a response can be used multiple times, but given the short lifetime of the prefetch buffer it’s unclear whether this is worthwhile.

To perform prefetch response checks given a navigation id navigationId, a request request, prefetch record record, two browsing contexts sourceBrowsingContext and browsingContext, sandboxing flag set sandboxFlags, two policy containers historyPolicyContainer and initiatorPolicyContainer, an origin incumbentNavigationOrigin, and a history handling behavior historyHandling, perform the following steps.
  1. Let responseOrigin be null.

  2. Let responseCOOP be null.

  3. Let currentContextIsSource be the result of whether browsingContext’s active document is same origin with sourceBrowsingContext’s active document.

  4. Let coopEnforcementResult be a new cross-origin opener policy enforcement result whose needs a browsing context group switch is false, would need a browsing context group switch due to report-only is false, url is browsingContext’s active document's URL, current origin is browsingContext’s active document's origin, cross-origin opener policy is browsingContext’s active document's cross-origin opener policy, and current context is navigation source is currentContextIsSource.

  5. Let finalSandboxFlags be an empty sandboxing flag set.

  6. Let hasCrossOriginRedirects be false.

  7. Let urlList be an empty list.

  8. For each exchangeRecord in record’s redirect chain:

    1. Let response be exchangeRecord’s response.

    2. Append response’s URL to urlList.

    3. If response’s URL's origin is not the same as request’s URL, then set hasCrossOriginRedirects to true.

    4. Set finalSandboxFlags to the union of browsingContext’s sandboxing flags and response’s forced sandboxing flag set.

    5. Set responseOrigin to the result of determining the origin given browsingContext, response’s URL, finalSandboxFlags, and incumbentNavigationOrigin.

    6. If request’s reserved client is not null and response’s URL's origin is not the same as request’s reserved client's creation URL's origin, then:

      1. Run the environment discarding steps for request’s reserved client.

      2. Set request’s reserved client to null.

    7. If request’s reserved client is null, then:

      1. Let topLevelCreationURL be response’s URL.

      2. Let topLevelOrigin be null.

      3. If browsingContext is not a top-level browsing context, then:

        1. Let parentEnvironment be browsingContext’s container's relevant settings object.

        2. Set topLevelCreationURL to parentEnvironment’s top-level creation URL and topLevelOrigin to parentEnvironment’s top-level origin.

      4. Set request’s reserved client to a new environment whose id is a unique opaque string, target browsing context is browsingContext, creation URL is response’s URL, top-level creation URL is topLevelCreationURL, and top-level origin is topLevelOrigin.

    8. If browsingContext is a top-level browsing context, then:

      1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request’s reserved client.

      2. Assert: If sandboxFlags is not empty, then responseCOOP’s value is "unsafe-none".

      3. Set coopEnforcementResult to the result of enforcing a response’s cross-origin opener policy given browsingContext, response’s URL, finalSandboxFlags, and incumbentNavigationOrigin.

  9. Set request’s URL list to urlList.

  10. Let responsePolicyContainer be the result of creating a policy container from a fetch response given response and request’s reserved client.

  11. Let resultPolicyContainer be the result of determining navigation params policy container given response’s URL, historyPolicyContainer, initiatorPolicyContainer, null, and responsePolicyContainer.

  12. Let navigationParams be a new navigation params whose id is navigationId, request is request, response is record’s response, origin is responseOrigin, policy container is resultPolicyContainer, final sandboxing flag set is finalSandboxFlags, cross-origin opener policy is responseCOOP, COOP enforcement result is coopEnforcementResult, reserved environment is request’s reserved client, browsing context is browsingContext, history handling is historyHandling, and has cross-origin redirects is hasCrossOriginRedirects.

  13. Return navigationParams.

A prefetch IP anonymization policy is either null or a cross-origin prefetch IP anonymization policy.

A cross-origin prefetch IP anonymization policy has an origin, which is an origin.

A prefetch IP anonymization policy policy requires anonymity for request request if the following steps return true:
  1. If policy is a cross-origin prefetch IP anonymization policy:

    1. If request’s URL's origin is the same as policy’s origin, then return false.

    2. Return true.

  2. Assert: policy is null.

  3. Return false.

2. HTML Patches

This is an abstraction of the existing process a navigate fetch. It includes behavior like redirect handling that is particular to navigational fetches, including those that are relate to a speculate future navigation, rather than an immediate one.

To perform a common navigational fetch given a request request, string navigationType, browsing context browsingContext, environment or null forceEnvironment, algorithm preRedirectHook (which takes URLs currentURL and locationURL) algorithm shouldBlockNavigationRequest (which takes a request, navigation type string, and environment, and returns "Blocked" or "Allowed"), algorithm shouldBlockNavigationResponse (which takes a request and a response, and returns "Blocked" or "Allowed"), perform the following steps.

  1. Let response be null.

  2. Set request’s mode to "navigate" and redirect mode to "manual".

  3. Assert: request’s reserved client is null.

  4. Let environment be null.

  5. Let locationURL be null.

  6. Let currentURL be request’s current URL.

  7. While true:

    1. If locationURL is non-null, then:

      1. Run preRedirectHook given currentURL and locationURL.

      2. Set currentURL to locationURL.

    2. If environment is not null and currentURL’s origin is not the same as environment’s creation URL's origin, then:

      1. Run the environment discarding steps for environment.

      2. Set environment to null.

    3. If environment is null, then:

      1. Let topLevelCreationURL be currentURL.

      2. Let topLevelOrigin be null.

      3. If browsingContext is not a top-level browsing context, then:

        1. Let parentEnvironment be browsingContext’s container's relevant settings object.

        2. Set topLevelCreationURL to parentEnvironment’s top-level creation URL and topLevelOrigin to parentEnvironment’s top-level origin.

      4. Set environment to a new environment whose id is a unique opaque string, target browsing context is browsingContext, creation URL is currentURL, top-level creation URL is topLevelCreationURL, and top-level origin is topLevelOrigin.

      5. If forceEnvironment is null, set request’s reserved client to environment.

      6. Otherwise, set request’s reserved client to forceEnvironment.

      These steps ensure that environment is an environment which the correct network partition key, active service worker, etc., depending on whether it’s a top-level navigation and if not, what the top-level site is.
    4. If the result of shouldBlockNavigationRequest given request, navigationType, and environment is "Blocked", then set response to a network error and break.

    5. If response is null, fetch request.

    6. Otherwise, perform HTTP-redirect fetch using request and response.

    7. Wait for the task on the networking task source to process response and set response to the result.

    8. If the result of shouldBlockNavigationResponse given request and response is "Blocked", then set response to a network error and break.

    9. If response is not a network error, browsingContext is a child browsing context, and the result of performing a cross-origin resource policy check with browsingContext’s container document's origin, browsingContext’s container document's relevant settings object, request’s destination, response, and true is blocked, then set response to a network error and break.

      Here we’re running the cross-origin resource policy check against the parent browsing context rather than browsingContext. This is because we care about the same-originness of the embedded content against the parent context, not the navigation source.
    10. Set locationURL to response’s location URL given currentURL’s fragment.

    11. If locationURL is not a URL whose scheme is an HTTP(S) scheme, the break.

      By the end of this loop we will be in one of these scenarios:
      • response is a network error.

      • locationURL is failure, because of an unparseable `Location` header.

      • locationURL is null, because we successfully fetched a non-network error HTTP(S) response with no `Location` header.

      • locationURL is a URL with a non-HTTP(S) scheme.

  8. If forceEnvironment is not null, run the environment discarding steps for environment.

  9. Return (response, locationURL).

Given this, the non-prefetch case becomes, with the small addition of prefetch logic:

To process a navigate fetch, given a navigation id navigationId, request request, two browsing contexts sourceBrowsingContext and browsingContext, a string navigationType, a sandboxing flag set sandboxFlags, two policy containers historyPolicyContainer and initiatorPolicyContainer, a boolean allowedToDownload, a boolean hasTransientActivation, an origin incumbentNavigationOrigin, and a history handling behavior historyHandling:
  1. Set request's client to sourceBrowsingContext’s active document's relevant settings object, destination to "document", credentials mode to "include", use-URL-credentials flag, and replaces client id to browsingContext’s active document's relevant settings object's id.

  2. If hasTransientActivation is true, then set request’s user-activation to true.

  3. If browsingContext’s container is non-null:

    1. If browsingContext’s container has a browsing context scope origin, then set request’s origin to that browsing context scope origin.

    2. Set request’s destination to browsingContext’s container's local name.

  4. Let prefetchRecord be the result of finding a matching prefetch record given browsingContext’s active document, request’s URL, request’s referrer policy, and sandboxFlags.

    This step, and the following condition, is added by this specification.
  5. If prefetchRecord is not null, then:

    1. Let navigationParams be the result of performing prefetch response checks given navigationId, request, prefetchRecord, sourceBrowsingContext, browsingContext, sandboxFlags, historyPolicyContainer, initiatorPolicyContainer, incumbentNavigationOrigin, and historyHandling.

    2. If prefetchRecord’s isolated partition key is not null, then copy prefetch cookies given prefetchRecord’s isolated partition key and navigationParams’s reserved environment.

      This copy is complete before continuing, in the sense that subresource fetches, document.cookie, etc. can observe the cookies.
    3. Process a navigate response with navigationType, allowedToDownload, hasTransientActivation, and navigationParams.

    4. Return.

  6. Let responseOrigin be null.

  7. Let responseCOOP be null.

  8. Let currentContextIsSource be the result of whether browsingContext’s active document is same origin with sourceBrowsingContext’s active document.

  9. Let coopEnforcementResult be a new cross-origin opener policy enforcement result whose needs a browsing context group switch is false, would need a browsing context group switch due to report-only is false, url is browsingContext’s active document's url, current origin is browsingContext’s active document's origin, cross-origin opener policy is browsingContext’s active document's cross-origin opener policy, and current context is navigation source is currentContextIsSource.

  10. Let finalSandboxFlags be an empty sandboxing flag set.

  11. Let hasCrossOriginRedirects be false.

  12. Let preRedirectHook be the following steps, given currentURL and locationURL:

    1. If locationURL’s origin is not the same as currentURL’s origin, then set hasCrossOriginRedirects to true.

  13. Let shouldBlockNavigationRequest be the following steps, given request request, string navigationType and environment environment:

    1. Assert: request’s reserved client is environment.

    2. Return the result of Should navigation request of type be blocked by Content Security Policy?, given request and navigationType.

  14. Let shouldBlockNavigationResponse be the following steps, given request and response:

    1. Set finalSandboxFlags to the union of browsingContext’s sandboxing flags and response’s forced sandboxing flag set.

    2. Set responseOrigin to the result of determining the origin given browsingContext, request’s URL, finalSandboxFlags, and incumbentNavigationOrigin.

    3. If browsingContext is a top-level browsing context, then:

      1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request’s reserved client.

      2. If sandboxFlags is not empty and responseCOOP’s value is not "unsafe-none", then return "Blocked".

        This results in a network error as one cannot simultaneously provide a clean slate to a response using cross-origin opener policy and sandbox the result of navigating to that response.
      3. Set coopEnforcementResult to the result of enforcing a response’s cross-origin opener policy given browsingContext, request’s URL, finalSandboxFlags, and incumbentNavigationOrigin.

    4. Return "Allowed".

  15. Let (response, locationURL) be the result of performing a common navigational fetch given request, navigationType, browsingContext, null (for forceEnvironment), preRedirectHook, shouldBlockNavigationRequest and shouldBlockNavigationResponse.

  16. If locationURL is a URL:

    1. Assert: locationURL’s scheme is not a fetch scheme and not "javascript".

    2. Process a navigate URL scheme given locationURL, browsingContext, and hasTransientActivation, and return.

  17. Let responsePolicyContainer be the result of creating a policy container from a fetch response given response and request’s reserved client.

  18. Let resultPolicyContainer be the result of determining navigation params policy container given response’s URL, historyPolicyContainer, initiatorPolicyContainer, null, and responsePolicyContainer.

  19. Let navigationParams be a new navigation params whose id is navigationId, request is request, response is response, origin is responseOrigin, policy container is resultPolicyContainer, final sandboxing flag set is finalSandboxFlags, cross-origin opener policy is responseCOOP, COOP enforcement result is coopEnforcementResult, reserved environment is request’s reserved client, browsing context is browsingContext, history handling is historyHandling, and has cross-origin redirects is hasCrossOriginRedirects.

  20. Run process a navigate response with navigationType, allowedToDownload, hasTransientActivation, and navigationParams.

3. Prefetch algorithms

These algorithms are based on process a navigate fetch.

Check Service Worker integration

To partitioned prefetch given a Document document, URL url, referrer policy referrerPolicy, and prefetch IP anonymization policy anonymizationPolicy, perform the following steps.
  1. Assert: url’s scheme is an HTTP(S) scheme.

  2. Let partitionKey be the result of determining the network partition key given document’s relevant settings object.

  3. Let browsingContext be document’s browsing context.

  4. Let sandboxFlags be the result of determining the creation sandboxing flags given browsingContext and browsingContext’s container.

  5. Let request be a request as follows:

    URL

    url

    referrer policy

    referrerPolicy

    initiator

    "prefetch"

    This causes the prefetch-src [CSP] directive to apply as part of fetch.
    destination

    "document"

    credentials mode

    "include"

    use-URL-credentials flag

    (set)

    client

    document’s relevant settings object

    header list
  6. Let redirectChain be an empty redirect chain.

  7. Let shouldBlockNavigationRequest be the following steps, given request request, string navigationType and environment environment:

    1. Assert: navigationType is "other".

    2. Assert: request’s reserved client is environment.

    3. Let purpose be a List containing the Token "prefetch".

    4. If anonymizationPolicy requires anonymity for request, then:

      1. Add a parameter whose key is "anonymous-client-ip" and whose value is true to the "prefetch" token in purpose.

      2. The user agent must use a connection which anonymizes the client IP address (e.g., using a proxy) when fetching request, or return "Blocked".

        At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.

    5. Set a structured field value given (`Sec-Purpose`, purpose) in request’s header list.

      Implementations might also send vendor-specific headers, like Chromium’s `Purpose`/`prefetch`, Mozilla’s `X-moz`/`prefetch`, and WebKit’s `X-Purpose`/`preview`, for compatibility with existing server software. Over time we hope implementers and server software authors will adopt a standard header.
    6. Let proposedPartitionKey be the result of determining the network partition key given request.

    7. If partitionKey is not equal to proposedPartitionKey, then return "Blocked".

      It might be possible to "downgrade" to uncredentialed prefetch in this case.
    8. If request’s URL is not potentially trustworthy, then return "Blocked".

      This is intended to both reduce the likelihood of prefetch traffic being visible to an on-path attacker, and to encourage the use of cryptographic schemes over public networks.
    9. If request cannot be fetched given anonymizationPolicy for an implementation-defined reason, then return "Blocked".

      This explicitly acknowledges that implementations might have additional restrictions. For instance, anonymized traffic might not be possible to some hosts, such as those that are not publicly routable and those that have traffic advice declining private prefetch traffic.
    10. Append request to redirectChain.

    11. Return "Allowed".

  8. Let shouldBlockNavigationResponse be the following steps, given request request and response response:

    1. Let responseCOOP be the result of obtaining a cross-origin opener policy given response and request’s reserved client.

    2. If sandboxFlags is not empty and responseCOOP’s value is not "unsafe-none", then return "Blocked".

    3. Update the response for redirectChain given request and response.

      This allows enforcing a response’s cross-origin opener policy to be deferred, since this has visible side effects such as queuing violation reports.
    4. Return "Allowed".

  9. Let (response, locationURL) be the result of performing a common navigational fetch given request, "other", browsingContext, null, an empty algorithm, shouldBlockNavigationRequest, and shouldBlockNavigationResponse.

  10. If locationURL is failure or a URL whose scheme is not an HTTP(S) scheme, then set response to a network error.

  11. TODO: navigate-to, frame-src, XFO enforcement should probably be left to navigation, but what about Content-Disposition?

  12. If response is a network error, then return.

  13. Assert: response is response of redirectChain’s last element.

  14. If response does not support prefetch, then return.

  15. Store a prefetch record given document, url, referrerPolicy, sandboxFlags, redirectChain and null.

The list of sufficiently strict speculative navigation referrer policies is a list containing the following: "", "strict-origin-when-cross-origin", "strict-origin", "same-origin", "no-referrer".

To uncredentialed prefetch given a Document document, URL url, referrer policy referrerPolicy, and prefetch IP anonymization policy anonymizationPolicy, perform the following steps.
  1. Assert: url’s scheme is an HTTP(S) scheme.

  2. If referrerPolicy is not in the list of sufficiently strict speculative navigation referrer policies, then return.

  3. Let browsingContext be document’s browsing context.

  4. Let sandboxFlags be the result of determining the creation sandboxing flags given browsingContext and browsingContext’s container.

  5. Let isolationOrigin be a new opaque origin.

    This is used to ensure a distinct network partition key is used.
  6. Let isolatedEnvironment be a new environment whose id is a unique opaque string, target browsing context is browsingContext, creation URL is about:blank, top-level creation URL is about:blank, and top-level origin is isolationOrigin.

  7. Let isolatedPartitionKey be the result of determining the network partition key given isolatedEnvironment.

  8. Let originalPartitionKey be the result of determining the network partition key given document’s relevant settings object.

  9. Let request be a request as follows:

    URL

    url

    referrer policy

    referrerPolicy

    initiator

    "prefetch"

    This causes the prefetch-src [CSP] directive to apply as part of fetch.
    destination

    "document"

    credentials mode

    "include"

    Though credentials are included, they will be isolated such that no credentials are present to begin with.
    cache mode

    "no-store"

    client

    document’s relevant settings object

  10. Let originsWithConflictingCredentials be an empty ordered set.

  11. Let redirectChain be an empty redirect chain.

  12. Let shouldBlockNavigationRequest be the following steps, given request request, string navigationType and environment environment:

    1. Assert: request’s reserved client is isolatedEnvironment and not environment.

    2. Assert: navigationType is "other".

    3. Let purpose be a List containing the Token "prefetch".

    4. If anonymizationPolicy requires anonymity for request, then:

      1. Add a parameter whose key is "anonymous-client-ip" and whose value is true to the "prefetch" token in purpose.

      2. The user agent must use a connection which anonymizes the client IP address (e.g., using a proxy) when fetching request, or return "Blocked".

        At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.

    5. Set a structured field value given (`Sec-Purpose`, purpose) in request’s header list.

      Implementations might also send vendor-specific headers, like Chromium’s `Purpose`/`prefetch`, Mozilla’s `X-moz`/`prefetch`, and WebKit’s `X-Purpose`/`preview`, for compatibility with existing server software. Over time we hope implementers and server software authors will adopt a standard header.
    6. Let hypotheticalPartitionKey be the result of determining the network partition key given environment.

    7. If originalPartitionKey is equal to hypotheticalPartitionKey, then return "Blocked".

      The prefetch would end up in the same partition as the prefetch came from in this case. environment represents the environment that would ordinarily be used during navigation fetch.
    8. If there are credentials associated with request’s current URL and hypotheticalPartitionKey, then append request’s current URL's origin to originsWithConflictingCredentials.

    9. If request’s URL is not potentially trustworthy, then return "Blocked".

      This is intended to both reduce the likelihood of prefetch traffic being visible to an on-path attacker, and to encourage the use of cryptographic schemes over public networks.
    10. If request cannot be fetched given anonymizationPolicy for an implementation-defined reason, then return "Blocked".

      This explicitly acknowledges that implementations might have additional restrictions. For instance, anonymized traffic might not be possible to some hosts, such as those that are not publicly routable and those that have traffic advice declining private prefetch traffic.
    11. Append request to redirectChain.

    12. Return "Allowed".

  13. Let shouldBlockNavigationResponse be the following steps, given request request and response response:

    1. Let responseCOOP be the result of obtaining a cross-origin opener policy given response and request’s reserved client.

    2. If sandboxFlags is not empty and responseCOOP’s value is not "unsafe-none", then return "Blocked".

    3. Update the response for redirectChain given request and response.

      This allows enforcing a response’s cross-origin opener policy to be deferred, since this has visible side effects such as queuing violation reports.
    4. Return "Allowed".

  14. Let (response, locationURL) be the result of performing a common navigational fetch given request, "other", browsingContext, isolatedEnvironment, an empty algorithm, shouldBlockNavigationRequest, and shouldBlockNavigationResponse.

  15. If locationURL is failure or a URL whose scheme is not an HTTP(S) scheme, then set response to a network error.

  16. TODO: navigate-to, frame-src, XFO enforcement should probably be left to navigation, but what about Content-Disposition?

  17. If response is a network error, then return.

  18. If originsWithConflictingCredentials is not empty, then return.

    This means that if any origin along the redirect chain had credentials, the prefetch is discarded. This reduces the chance of the user observing a logged-out page when they are logged in.
  19. Assert: response is response of redirectChain’s last element.

  20. If response does not support prefetch, then return.

  21. Store a prefetch record given document, url, referrerPolicy, sandboxFlags, redirectChain and isolatedPartitionKey.

This ends up setting the `Cache-Control` and `Pragma` request headers, which is contrary to what Chromium does today when skipping cache here. One approach would be to add a flag similar to prevent no-cache cache-control header modification flag. It would also be possible to have a cache that can be copied into the ordinary cache, like Chromium does for cookies.
Update this to include the `Supports-Loading-Mode` mechanism to allow responses to continue despite cookies.
To prefetch given a Document document, URL url, referrer policy referrerPolicy, and prefetch IP anonymization policy anonymizationPolicy, perform the following steps.
  1. Let partitionKey be the result of determining the network partition key given document’s relevant settings object.

  2. Let topLevelOrigin be url’s origin if document’s browsing context is a top-level browsing context, and document’s relevant settings object's top-level origin otherwise.

  3. Let topLevelSite be the result of obtaining a site, given topLevelOrigin.

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

  5. If partitionKey is equal to (topLevelSite, secondKey), then partitioned prefetch given document, url, referrerPolicy and anonymizationPolicy.

  6. Otherwise, uncredentialed prefetch given document, url, referrerPolicy and anonymizationPolicy.

This determines whether the navigation would use the same network partition key.

If it would, the prefetch is restricted to the same partition, and redirects which would leave the partition cause the prefetch to fail.

Otherwise it is uncredentialed, and redirects which would return to the original partition (thus ought to have credentials) will cause the prefetch to fail.

A response response supports prefetch if the following steps return true:
  1. Let status be response’s status.

  2. Assert: status is not a redirect status.

  3. If status is not an ok status, then return false.

    In particular, this means that error responses aren’t stored and will be retried when a navigation occurs. This increases the likelihood of navigations succeeding if the error was transient or due to the request being for prefetch. It also gives server software a simple way to refuse to handle requests which carry a `Sec-Purpose` request header indicating prefetch.
  4. Return true.

A future draft of this specification is expected to provide a way for responses to be marked as eligible or ineligible explicitly.

4. Cookies

[COOKIES] defines "cookies" which can be set using the `Set-Cookie` response header field. Because uncredentialed prefetch forces a separate network partition key to be used, it’s necessary to copy these cookies into the ordinary partition as though they had been received at the time of navigation.

To copy prefetch cookies given a network partition key isolatedPartitionKey and an environment environment, perform the following steps.
Though formally there is only one cookie store in [COOKIES], some browsers partition cookie stores so as to separate, for example, cookies with the same domain when loaded with different top-level sites. See Client-Side Storage Partitioning (Privacy CG).
  1. Let isolatedCookieStore be the cookie store associated with isolatedPartitionKey.

  2. For each cookie cookie in isolatedCookieStore.

    1. Remove cookie from isolatedCookieStore.

    2. A user agent may ignore a cookie in its entirety. If so, continue.

      This is consistent with [COOKIES] expressly permitting this when receiving a cookie.
    3. Let topLevelSite be null.

    4. If environment’s target browsing context is a top-level browsing context:

      1. Set topLevelSite to the result of obtaining a site given tuple origin ("https", cookie’s domain, null, null).

        The use of the "https" scheme and null port here is arbitrary because cookies are visible across schemes and ports, in contrast to the usual same origin policy. The user agent’s choice of associated cookie store, therefore, cannot be sensitive to either.
      When performing a prefetch in a top-level browsing context, the request (including all redirects) is preparing for a top-level navigation. environment’s top-level site changes as redirects are followed, and since a redirect might be cross-site, environment’s top-level site might have changed since a given cookie was received. However, since the navigation is top-level, the origin delivering the cookie would have been the top-level site at the time. Since the cookie’s domain has to be same-site with an origin delivering it, the cookie’s domain can be used to determine the correct top-level site.
    5. Otherwise:

      1. Let topLevelOrigin be environment’s top-level origin.

      2. If topLevelOrigin is null, then set topLevelOrigin to environment’s top-level creation URL's origin.

      3. Assert: topLevelOrigin is an origin.

      4. Set topLevelSite be the result of obtaining a site given topLevelOrigin.

      When performing a prefetch in a nested browsing context, the top-level site is determined by the top-level browsing context that contains it. Since that doesn’t change as redirects are followed, environment can be used to establish the top-level site.
    6. Let secondKey be null or an implementation-defined value.

      secondKey is expected to match the value it would have had if this response had been processed as part of an ordinary navigation in environment’s target browsing context.
    7. Let destinationPartitionKey be (topLevelSite, secondKey).

    8. Let cookieStore be the cookie store associated with destinationPartitionKey.

    9. Let newCookie be a copy of cookie.

    10. Set newCookie’s creation-time and last-access-time to the current date and time.

    11. If cookieStore contains a cookie existingCookie with the same name, domain and path as the newly created cookie:

      1. Set the creation-time of newCookie to existingCookie’s creation-time.

      2. Remove existingCookie from cookieStore.

    12. Insert newCookie into cookieStore.

      This remove-and-insert pattern is consistent with what happens when receiving a cookie.
To gather cookie data for URL url in cookie store cookieStore:
  1. Let cookieList be the set of cookies from cookieStore that meet all of the following requirements:

    • one of the following applies:

    • url’s path path-matches the cookie’s path

    • if the cookie’s secure-only-flag is true, then url’s scheme is "https"

  2. Sort cookieList lexicographically by name, and by value if names are equal.

    Since both are octet sequences, Unicode collation rules do not apply.
  3. Let cookieData be an empty list.

  4. For each cookie in cookieList:

    1. Append (cookie’s name, cookie’s value) to cookieData.

  5. Return cookieData.

This is essentially how the `Cookie` header is computed, except that a few details are specialized to this case, the last-access-time is not modified, and the sort order is changed to ensure it is unique.

Note that some changes to cookies would not affect this (and would not affect the `Cookie` header), such as updates to the creation-time, last-access-time, path, and various flags. Despite this, the has updated credentials algorithm permits user agents to treat such changes as modifications.

5. The Sec-Purpose HTTP request header

The `Sec-Purpose` HTTP request header specifies that the request serves one or more purposes other than requesting the resource for immediate use by the user.

The header field is an [RFC8941] Structured Header whose value must be a a List. Its ABNF is:

Sec-Purpose = sf-list

It may contain a Item member which is the Token "prefetch". If so, this indicates the request’s purpose is to download a resource it is anticipated will be fetched shortly.

TODO: Are there normative implications of this that should be specified here?

The following parameters are defined for the "prefetch" token:

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[COOKIES]
A. Barth. HTTP State Management Mechanism. April 2011. Proposed Standard. URL: https://httpwg.org/specs/rfc6265.html
[CSP]
Mike West. Content Security Policy Level 3. 29 June 2021. WD. URL: https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HR-TIME-2]
Ilya Grigorik. High Resolution Time Level 2. 21 November 2019. REC. URL: https://www.w3.org/TR/hr-time-2/
[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/
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. Referrer Policy. 26 January 2017. CR. URL: https://www.w3.org/TR/referrer-policy/
[RFC8941]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. February 2021. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8941
[SECURE-CONTEXTS]
Mike West. Secure Contexts. 18 September 2021. CR. URL: https://www.w3.org/TR/secure-contexts/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/

Issues Index

It might be possible to use cache response headers to determine when a response can be used multiple times, but given the short lifetime of the prefetch buffer it’s unclear whether this is worthwhile.
Check Service Worker integration
At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.
It might be possible to "downgrade" to uncredentialed prefetch in this case.
At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an implementation-defined manner using some kind of proxy or relay. Ideally this would be plumbed down to obtain a connection, and possibly even the mechanism could be further standardized.
This ends up setting the `Cache-Control` and `Pragma` request headers, which is contrary to what Chromium does today when skipping cache here. One approach would be to add a flag similar to prevent no-cache cache-control header modification flag. It would also be possible to have a cache that can be copied into the ordinary cache, like Chromium does for cookies.
Update this to include the `Supports-Loading-Mode` mechanism to allow responses to continue despite cookies.
TODO: Are there normative implications of this that should be specified here?