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).
-
Let hypotheticalEnvironment be the result of creating a reserved client given navigable, response’s URL, and null.
-
Let hypotheticalPartitionKey be the result of determining the network partition key given hypotheticalEnvironment.
-
If hypotheticalPartitionKey is equal to sourcePartitionKey or there are no credentials associated with URL and hypotheticalPartitionKey, then return false.
-
Return true.
An exchange record is a struct with the following items:
A redirect chain is a list of exchange records.
-
Assert: redirectChain’s last item’s request is the same as request and its response is null.
-
Set redirectChain’s last item’s request to a clone of request.
The cloning ensures that any modifications made to request during further redirects do not affect the stored request, which later needs to be consulted at activation time (during create navigation params from a prefetch record) to get a full picture of the original redirect chain. The only currently known case where a request field is mutated and is also consulted during activation is the referrer field, but for specification robustness we clone the entire request.
Since prefetches are only ever performed for
`GET`requests, the request body is always null, so cloning is relatively straightforward. -
Set redirectChain’s last item’s response to response.
Each Document has prefetch records, which is a list of prefetch records.
A prefetch record is a struct with the following items:
-
tags, an ordered set of speculation rule tags
-
URL, a URL
-
anonymization policy, a prefetch IP anonymization policy
-
referrer policy, a referrer policy
-
No-Vary-Search hint, a URL search variance
-
source, a string
This is intended for use by a specification or implementation-defined feature to identify which prefetches it created. It might also associate other data with this struct. -
prerendering traversable, a prerendering traversable, "
to be created", or null (null by default) -
prerendering target navigable name hint, a valid navigable target name or keyword or null (null by default)
-
state, which is "
ongoing" (the default), "completed", or "canceled""canceled" indicates that the prefetch was aborted by the author or user, or terminated by the user agent. -
fetch controller, a fetch controller (a new fetch controller by default)
-
redirect chain, a redirect chain (empty by default)
-
start time, a
DOMHighResTimeStamp(0.0 by default) -
expiry time, a
DOMHighResTimeStamp(0.0 by default) -
source partition key, a network partition key or null (the default)
-
isolated partition key, a network partition key whose first item is an opaque origin and which represents a separate partition in which cross-partition state can be temporarily stored, or null (the default)
-
had conflicting credentials, a boolean (initially false)
A request which would have ordinarily sent credentials but could not due to cross-partition prefetch causes a prefetch to be abandoned.
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 cancel and discard records from the prefetch records even if they are not expired, e.g., due to resource constraints. Since completed records with expiry times in the past will never be matching prefetch records, they can be removed with no observable consequences.
-
If prefetchRecord’s URL is equal to url, return true.
-
If prefetchRecord’s response is not null:
-
Let searchVariance be the result of obtaining a URL search variance given prefetchRecord’s redirect chain[0]'s response.
It is important that we check the 0th response, not the last response (i.e. not prefetchRecord’s response), because the prefetch record’s URL is that of the first request/response pair, and so it is only that first response’s `
No-Vary-Search` header that should impact matching. -
If prefetchRecord’s URL and url are equivalent modulo search variance given searchVariance, return true.
-
-
Otherwise, return false.
-
If prefetchRecord matches a URL given url, return true.
-
If prefetchRecord’s response is null:
-
Let searchVariance be prefetchRecord’s No-Vary-Search hint.
-
If prefetchRecord’s URL and url are equivalent modulo search variance given searchVariance, return true.
-
-
Otherwise, return false.
Document document, perform the following steps.
-
Assert: prefetchRecord is in document’s prefetch records.
-
Set prefetchRecord’s state to "
canceled". -
Abort prefetchRecord’s fetch controller. This will cause any ongoing fetch to be canceled and yield a network error.
-
If prefetchRecord’s prerendering traversable is a traversable navigable, then destroy it.
-
Remove prefetchRecord from document’s prefetch records.
-
Trigger a prefetch status updated event given document’s node navigable, prefetchRecord, and "
failure" status.
This means that even a completed prefetch or prerender will not be activated. However, the process of prefetching or prerendering might have modified the HTTP cache, making subsequent navigations faster anyway.
Document document, perform the following steps.
-
Assert: document is fully active.
-
Let currentTime be the current high resolution time for the relevant global object of document.
-
Let expiryTime be currentTime + 300000 (i.e., five minutes).
-
Remove all elements of document’s prefetch records which have the same URL as prefetchRecord and whose state equals "
completed". -
Set prefetchRecord’s state to "
completed" and expiry time to expiryTime. -
Trigger a prefetch status updated event given document’s node navigable, prefetchRecord, and "
ready" status.
-
Let exactRecord be null.
-
Let inexactRecord be null.
-
For each record of sourceSnapshotParams’s prefetch records:
-
If record’s URL is equal to url:
-
Set exactRecord to record.
-
-
If inexactRecord is null and record matches a URL given url:
-
Set inexactRecord to record.
-
-
Let recordToUse be exactRecord if exactRecord is not null, otherwise inexactRecord.
-
If recordToUse is not null:
-
Let currentTime be the current high resolution time for navigable’s active document.
-
If recordToUse’s expiry time is less than currentTime:
-
Trigger a prefetch status updated event given navigable, recordToUse, and "
failure" status. -
Return null.
-
-
For each exchangeRecord of recordToUse’s redirect chain:
-
If conflicting credentials exist for exchangeRecord’s response given navigable and recordToUse’s source partition key:
-
Trigger a prefetch status updated event given navigable, recordToUse, and "
failure" status. -
Return null.
-
This handles the case where there were no cross-partition credentials initially, but there are now. User agents could 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. -
-
Return recordToUse.
-
-
Return null.
It’s not obvious, but this doesn’t actually require that the prefetch have received the complete body, just the response headers. In particular, a navigation to a prefetched response might nonetheless not load instantaneously.
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.
-
Assert: this is running in parallel.
-
Let timeout be null.
-
Optionally, set timeout to an implementation-defined duration representing the maximum time the implementation is willing to wait for an ongoing prefetch to respond, before it gives up and restarts the navigation.
Choosing a good timeout is difficult, and might depend on the exact initiator of the navigation or the prefetch (e.g., its speculation rule eagerness, or whether it will be used for a prerender). In general, a timeout is best avoided, as it is rare that restarting the navigation as a non-prefetch will give a better result than awaiting the ongoing prefetch. But some implementations have found that in some situations, a timeout on the order of seconds can give better results.
-
Let startTime be the unsafe shared current time.
-
Run these steps, but abort when timeout is not null and the unsafe shared current time − startTime is greater than timeout:
-
While true:
-
Let completeRecord be the result of finding a matching complete prefetch record given navigable, sourceSnapshotParams, and url.
-
If completeRecord is not null, return completeRecord.
-
Let potentialRecords be an empty list.
-
For each record of sourceSnapshotParams’s prefetch records:
-
If all of the following are true:
-
record’s state is "
ongoing"; -
record is expected to match a URL given url; and
-
record’s expiry time is greater than the current high resolution time for navigable’s active window,
-
Each iteration of the loop recomputes potentialRecords, in a way so that a subsequent iteration’s potentialRecords will be a subset of the previous iteration’s potentialRecords. This follows from how sourceSnapshotParams’s prefetch records is a static snapshot, and none of these three conditions can flip from false to true.
An equivalent strategy would be to build a single initial instance of potentialRecords, and remove items from it as they no longer meet the criteria.
-
-
If potentialRecords is empty, return null.
-
Wait until the state of any element of sourceSnapshotParams’s prefetch records changes.
-
-
-
Return null.
Because sourceSnapshotParams’s prefetch records are a snapshot, prefetches that start after a navigation cannot be activated as part of that navigation.
-
For each candidate of candidates:
-
If prefetchRecord does not match a URL given candidate’s URL, then continue.
-
If candidate is a prefetch candidate and prefetchRecord’s anonymization policy does not equal candidate’s anonymization policy, then continue.
-
If candidate is a prerender candidate and prefetchRecord’s prerendering traversable is null, then continue.
-
Return true.
-
-
Return false.
Document document has a matching prefetch record given a prefetch record recordUnderConsideration and an optional boolean checkPrerender (default false) if the following algorithm returns true:
-
For each record of document’s prefetch records:
-
If checkPrerender is true:
-
If record’s prerendering traversable is null, then continue.
-
If record’s prerendering target navigable name hint is not equal to recordUnderConsideration’s prerendering target navigable name hint, then the user agent may continue.
User agents which create separate prerendering traversables depending on the prerendering target navigable name hint will continue here, since they consider such records to be distinct. Whereas, user agents which are able to activate an existing prerendering traversable regardless of the prerendering target navigable name hint will consider such records equivalent.
-
-
Let recordNVS be null.
-
If record’s response is not null, then set recordNVS to the result of obtaining a URL search variance given record’s response.
-
Otherwise, set recordNVS to record’s No-Vary-Search hint.
-
If recordUnderConsideration’s No-Vary-Search hint is not equal to recordNVS, then continue.
-
If recordUnderConsideration’s URL and record’s URL are equivalent modulo search variance given recordUnderConsideration’s No-Vary-Search hint, then return true.
-
Return false.
See the discussion `No-Vary-Search` comparison in the HTML Standard.
NavigationTimingType navTimingType, a request request, a prefetch record record, a target snapshot params targetSnapshotParams, and a source snapshot params sourceSnapshotParams, perform the following steps.
-
Let responseOrigin be null.
-
Let responseCOOP be null.
-
Let coopEnforcementResult be the result of creating a cross-origin opener policy enforcement result for navigation given navigable’s active document and documentState’s initiator origin.
-
Let finalSandboxFlags be an empty sandboxing flag set.
-
Let responsePolicyContainer be null.
-
Let urlList be an empty list.
-
Let response be record’s response.
-
For each exchangeRecord in record’s redirect chain:
-
Let redirectChainRequest be exchangeRecord’s request.
-
Let redirectChainResponse be exchangeRecord’s response.
-
Set responsePolicyContainer to the result of creating a policy container from a fetch response given redirectChainResponse and redirectChainRequest’s reserved client.
-
Set finalSandboxFlags to the union of targetSnapshotParams’s sandboxing flags and responsePolicyContainer’s CSP list’s CSP-derived sandboxing flags.
-
Set responseOrigin to the result of determining the origin given redirectChainResponse’s URL, finalSandboxFlags, documentState’s initiator origin, and null.
-
Set responseCOOP to the result of obtaining a cross-origin opener policy given redirectChainResponse and redirectChainRequest’s reserved client.
-
Set coopEnforcementResult to the result of enforcing a response’s cross-origin opener policy given navigable’s active browsing context, redirectChainResponse’s URL, responseOrigin, responseCOOP, coopEnforcementResult, and redirectChainRequest’s referrer.
-
If finalSandboxFlags is not empty and responseCOOP’s value is "
unsafe-none", then set response to an appropriate network error and break.
-
-
If request’s URL is not equal to urlList[0], then insert request’s URL into urlList after the 0th item.
In this case, we are navigating to request’s URL, but fulfilling it with a prefetch that came from a response whose URL is urlList[0], due to `
No-Vary-Search`. We treat this as if there was a redirect from the 0th response to URL. If, after this insertion, urlList’s size is 2, then the resultingDocumentwill use the navigated-to URL. Otherwise, if the size is greater, then this will have no effect.Consider the case where we prefetch/page?param=1, and the server responds withNo-Vary-Search: params=("param")and a 200 response code.Later, if we navigate to
/page?param=2, the prefetch will be used. This step will cause urlList to become «/page?param=1,/page?param=2», so the resultingDocumentwill have/page?param=2as its URL.