Speculation Rules

Draft Community Group Report,

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

Abstract

A flexible syntax for defining what outgoing links can be prepared speculatively before navigation.

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. Speculation rules

1.1. Definitions

A speculation rule is a struct with the following items:

The only valid string for requirements to contain is "anonymous-client-ip-when-cross-origin".

A speculation rule set is a struct with the following items:

A pending external speculation rule resource is a struct with the following items:

A document rule predicate is one of the following:

A document rule conjunction is a struct with the following items:

A document rule disjunction is a struct with the following items:

A document rule negation is a struct with the following items:

A document rule URL pattern predicate is a struct with the following items:

A document rule CSS selector predicate is a struct with the following items:

The following strings are valid eagerness strings: "immediate", "eager", "moderate", "conservative".

1.2. The script element

Note: This section contains modifications to the corresponding section of [HTML].

To process speculation rules consistently with the existing script types, we make the following changes:

The following algorithms are updated accordingly:

We should consider whether we also want to make this execute even if scripting is disabled.

We should also incorporate the case where a src attribute is set.

We could fire error and load events if we wanted to.

The following steps are added as the script element’s HTML element removing steps, given removedNode and oldParent:
  1. If removedNode’s result is a speculation rule set, then:

    1. Let document be removedNode’s node document.

    2. Remove it from document’s list of speculation rule sets.

    3. Set removedNode’s already started flag to false.

    4. Set removedNode’s result to null.

    5. Consider speculation for document.

    This means that the rule set can be reparsed if the script is reinserted.
The following steps are added as the script element’s children changed steps for an element scriptElement.
  1. If scriptElement’s result is a speculation rule set, then:

    1. Let document be scriptElement’s node document.

    2. Let ruleSet be scriptElement’s result.

    3. Let newResult be the result of parsing speculation rules given scriptElement’s child text content, document, and document’s document base URL.

    4. Set scriptElement’s result to newResult.

    5. Replace ruleSet with newResult in document’s list of speculation rule sets.

    6. Consider speculation for document.

    This means that the rule set is reparsed immediately if inline changes are made.

1.3. Prepare the script element

Inside the prepare the script element algorithm we make the following changes:

1.4. The `Speculation-Rules` header

Note: This section contains modifications to [HTML].

The Speculation-Rules HTTP response header is a structured header whose value must be a list whose members must be strings which are valid URL strings.

To process the Speculation-Rules header given a document document and a response response:

  1. Optionally, return.

    The user agent could ignore this header, for example, if it does not intend to consider speculation for document.

  2. Let parsedList be the result of getting a structured field value given `Speculation-Rules` and "list" from response’s header list.

  3. If parsedList is null, then return.

  4. Let baseUrl be document’s document base URL.

  5. For each item of parsedList:

    1. If item is not a string, then continue.

    2. Let parsedURL be the result of parsing item with baseUrl.

    3. If parsedURL is failure, then continue.

    4. Let pendingResource be a pending external speculation rule resource with URL parsedURL and controller null.

    5. Append pendingResource to document’s list of pending external speculation rule resources.

  6. Process pending external speculation rule resources given document.

In create and initialize a Document object, before the step

  1. Return document.

add the following step

  1. Process the Speculation-Rules header given document and navigationParams’s response.

1.5. External speculation rule sets

To process pending external speculation rule resources given a document document:

  1. Let pendingResources be document’s list of pending external speculation rule resources.

  2. For each pendingResource of pendingResources:

    1. Optionally, cancel and discard pendingResource given document, then continue.

    2. If pendingResource’s controller is not null, then continue.

    3. Optionally, fetch pendingResource given document.

      The user agent schedules the fetch of external speculation rules at its discretion. That is, if it skips the fetch during this call of process pending external speculation rule resources, it might do the fetch during a later call.

To fetch a pending external speculation rule resource pendingResource given a document document:

  1. Let pendingResources be document’s list of pending external speculation rule resources.

  2. Assert: pendingResources contains pendingResource.

  3. Assert: pendingResource’s controller is null.

  4. Let settingsObject be document’s relevant settings object.

  5. Let requestURL be pendingResource’s URL.

  6. Let request be a new request whose URL is requestURL, client is settingsObject, mode is "cors", and destination is "speculationrules".

  7. Let controller be the result of fetching given request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence body:

    1. Remove pendingResource from pendingResources.

    2. If any of the following conditions are true, abort these steps:

    3. Otherwise, let text be the result of UTF-8 decoding body.

    4. Let ruleSet be the result of parsing speculation rules given text, document and response’s URL.

    5. If ruleSet is null, abort these steps.

      User agents are encouraged to report failures of the previous steps.

    6. Append ruleSet to document’s list of speculation rule sets.

  8. Set pendingResource’s controller to controller.

To cancel and discard a pending external speculation rule resource pendingResource given a document document:

  1. Let pendingResources be document’s list of pending external speculation rule resources.

  2. Assert: pendingResources contains pendingResource.

  3. Let controller be pendingResource’s controller.

  4. If controller is not null, abort controller.

  5. Remove pendingResource from pendingResources.

1.6. Parsing

The general principle here is to allow the existence of directives which are not understood, but not to accept into the rule set a rule which the user agent does not fully understand. This reduces the risk of unintended activity by user agents which are unaware of most recently added directives which might limit the scope of a rule.

To parse speculation rules given a string input, document document, and a URL baseURL, perform the following steps. They return a speculation rule set or null.
  1. Let parsed be the result of parsing a JSON string to an Infra value given input.

  2. If parsed is not a map, then return null.

  3. Let result be an empty speculation rule set.

  4. If parsed["prefetch"] exists and is a list, then for each prefetchRule of parsed["prefetch"]:

    1. If prefetchRule is not a map, then continue.

    2. Let rule be the result of parsing a speculation rule given prefetchRule, document and baseURL.

    3. If rule is null, then continue.

    4. If rule’s target navigable name hint is not null, then continue.

    5. Append rule to result’s prefetch rules.

  5. If parsed["prerender"] exists and is a list, then for each prerenderRule of parsed["prerender"]:

    1. If prerenderRule is not a map, then continue.

    2. Let rule be the result of parsing a speculation rule given prerenderRule, document, and baseURL.

    3. If rule is null, then continue.

    4. Append rule to result’s prerender rules.

  6. Return result.

To parse a speculation rule given a map input, a document document, and a URL baseURL, perform the following steps. They return a speculation rule or null.
  1. If input has any key other than "source", "urls", "where", "requires", "target_hint","referrer_policy", "relative_to", "eagerness", and "expects_no_vary_search" then return null.

  2. Let source be null.

  3. If input["source"] exists, then:

    1. Set source to input["source"].

  4. Otherwise, if input["urls"] exists and input["where"] does not exist, then:

    1. Set source to "list".

  5. Otherwise, if input["where"] exists and input["urls"] does not exist, then:

    1. Set source to "document".

  6. If source is neither the string "list" nor the string "document", then return null.

  7. Let urls be an empty list.

  8. Let predicate be null.

  9. If source is "list":

    1. If input["where"] exists, then return null.

    2. If input["urls"] does not exist, is not a list, or has any element which is not a string, then return null.

    3. If input["relative_to"] exists:

      1. Let relativeTo be input["relative_to"].

      2. If relativeTo is neither the string "ruleset" nor the string "document", then return null.

      3. If relativeTo is "document", then set baseURL to the document’s document base URL.

    4. For each urlString of input["urls"]:

      1. Let parsedURL be the result of parsing urlString with baseURL.

      2. If parsedURL is failure, then continue.

      3. If parsedURL’s scheme is not an HTTP(S) scheme, then continue.

      4. Append parsedURL to urls.

  10. If source is "document":

    1. If input["urls"] exists, then return null.

    2. If input["relative_to"] exists, then return null.

    3. If input["where"] does not exist, then set predicate to a document rule conjunction whose clauses is an empty list.

      Such a predicate will match all links.

    4. Otherwise, set predicate to the result of parsing a document rule predicate given input["where"], document and baseURL.

    5. If predicate is null, then return null.

  11. Let requirements be an empty ordered set.

  12. If input["requires"] exists, but is not a list, then return null.

  13. For each requirement of input["requires"]:

    1. If requirement is not the string "anonymous-client-ip-when-cross-origin", then return null.

    2. Append requirement to requirements.

  14. Let targetHint be null.

  15. If input["target_hint"] exists:

    1. If input["target_hint"] is not a valid navigable target name or keyword, then return null.

    2. Set targetHint to input["target_hint"].

  16. Let referrerPolicy be the empty string.

  17. If input["referrer_policy"] exists:

    1. If input["referrer_policy"] is not a referrer policy, then return null.

    2. Set referrerPolicy to input["referrer_policy"].

  18. Let eagerness be "immediate" if source is "list", and "conservative" otherwise.

  19. If input["eagerness"] exists:

    1. If input["eagerness"] is not one of the valid eagerness strings, then return null.

    2. Set eagerness to input["eagerness"].

  20. Let noVarySearchHint be the default URL search variance.

  21. If input["expects_no_vary_search"] exists:

    1. If input["expects_no_vary_search"] is not a string, then return null.

    2. Set noVarySearchHint to the result of obtaining a URL search variance hint given input["expects_no_vary_search"].

  22. Return a speculation rule with

    URLs

    urls

    predicate

    predicate

    requirements

    requirements

    target navigable name hint

    targetHint

    referrer policy

    referrerPolicy

    eagerness

    eagerness

    No-Vary-Search hint

    noVarySearchHint

To parse a document rule predicate given a value input, a document document and URL baseURL:
  1. If input is not a map, then return null.

  2. If input does not contain exactly one of "and", "or", "not", "href_matches" and "selector_matches", then return null. Otherwise, let predicateType be that key.

    This makes sure it is unambiguous what type of predicate is being used.

  3. If predicateType is "and" or "or", then:

    1. If input has any key other than predicateType, then return null.

      This, and other similar checks below, make sure that unexpected extra options cause the predicate to be invalid.

    2. Let rawClauses be the input[predicateType].

    3. If rawClauses is not a list, then return null.

    4. Let clauses be an empty list.

    5. For each rawClause of rawClauses:

      1. Let clause be the result of parsing a document rule predicate given rawClause, document and baseURL.

      2. If clause is null, then return null.

      3. Append clause to clauses.

    6. If predicateType is "and", then return a document rule conjunction whose clauses is clauses.

    7. If predicateType is "or", then return a document rule disjunction whose clauses is clauses.

  4. If predicateType is "not", then:

    1. If input has any key other than "not", then return null.

    2. Let rawClause be the input[predicateType].

    3. Let clause be the result of parsing a document rule predicate given rawClause, document and baseURL.

    4. If clause is null, then return null.

    5. Return a document rule negation whose clause is clause.

  5. If predicateType is "href_matches":

    1. If input has any key other than "href_matches" and "relative_to", then return null.

    2. If input["relative_to"] exists, then:

      1. Let relativeTo be input["relative_to"].

      2. If relativeTo is neither the string "ruleset" nor the string "document", then return null.

      3. If relativeTo is "document", set baseURL to the document’s document base URL.

    3. Let rawPatterns be input["href_matches"].

    4. If rawPatterns is not a list, then set rawPatterns to « rawPatterns ».

    5. Let patterns be an empty list.

    6. For each rawPattern of rawPatterns:

      1. Let serializedBaseURL be the serialization of baseURL.

      2. Let pattern be null.

      3. If rawPattern is a string, then:

        1. Set pattern to the result of constructing a URLPattern using the URLPattern(input, baseURL) constructor steps given rawPattern and serializedBaseURL. If those steps throw, catch the exception and return null.

      4. Otherwise, if rawPattern is a map, then:

        1. Let init be «[ "baseURL" → serializedBaseURL ]», representing a dictionary of type URLPatternInit.

        2. For each keyvalue of rawPattern:

          1. If key is not the identifier of a dictionary member of URLPatternInit or one of its inherited dictionaries, value is not a string, or the member’s type is not declared to be USVString, then return null.

            This will need to be updated if URLPattern gains members of other types.
          2. Set init[key] to value.

        3. Set pattern to the result of constructing a URLPattern using the URLPattern(input, baseURL) constructor steps given init. If those steps throw, catch the exception and return null.

      5. Otherwise, return null.

      6. Assert: pattern is a URLPattern.

      7. Append pattern to patterns.

    7. Return a document rule URL pattern predicate whose patterns is patterns.

  6. If predicateType is "selector_matches":

    1. If input has any key other than "selector_matches", then return null.

    2. Let rawSelectors be input["selector_matches"].

    3. If rawSelectors is not a list, then set rawSelectors to « rawSelectors ».

    4. Let selectors be an empty list.

    5. For each rawSelector of selectors:

      1. If rawSelector is not a string, then return null.

      2. Parse a selector from rawSelector. If the result is failure, then return null. Otherwise, let selector be the result.

      3. Append selector to selectors.

    6. Return a document rule CSS selector predicate whose selectors is selectors.

1.7. Processing model

A document has a list of speculation rule sets, which is an initially empty list.

A document has a list of pending external speculation rule resources, which is an initially empty list of pending external speculation rule resources.

Periodically, for any document document, the user agent may queue a global task on the DOM manipulation task source with document’s relevant global object to consider speculation for document.

The user agent will likely do when resources are idle and available, or otherwise the circumstances of its previous decision whether to start a speculation could have changed.

The user agent might still consider speculation when there are rule sets that have not been loaded yet.

The user agent may also queue a global task on the networking task source with document’s relevant global object to process pending external speculation rule resources given document.

A prefetch candidate is a struct with the following items:

A prerender candidate is a struct with the following items:

A prefetch candidate prefetchCandidate continues a prefetch record prefetchRecord if the following are all true:
To find matching links given a Document document and document rule predicate predicate:
  1. Let links be an empty list.

  2. For each shadow-including descendant descendant of document, in shadow-including tree order:

    1. If descendant is not an a with an href attribute or area element with an href attribute, continue.

      This corresponds to the elements which match :any-link pseudo-class, or which appear in the links collection.
    2. If descendant is not being rendered or is part of skipped contents, continue.

      Such links, though present in the document, aren’t available for the user to interact with and are unlikely to be good candidates. In addition, they may not have their style or layout computed, which might make CSS selector matching less efficient in user agents which skip some or all of that work for these elements.
    3. Let url be descendant’s url.

    4. If url is null, or its scheme is not an HTTP(S) scheme, continue.

    5. If predicate matches descendant, then append descendant to links.

  3. Return links.

To compute a speculative action referrer policy given a speculation rule rule and an a element, an area element, or null link:
  1. If rule’s referrer policy is not the empty string, then return rule’s referrer policy.

  2. If link is null, then return the empty string.

  3. If link’s rel attribute includes the noreferrer keyword, then return "no-referrer".

  4. Otherwise, return the current state of link’s referrerpolicy content attribute.

    The referrer policy computed based on link is intended to be equivalent to the referrer policy computation in following a hyperlink.

To consider speculation for a document document:
  1. Await a stable state. Steps in the synchronous section are marked with ⌛.

  2. ⌛ If document is not fully active, then return.

    It’s likely that we should also handle prerendered and back-forward cached documents.

  3. ⌛ Let prefetchCandidates be an empty list.

  4. ⌛ Let prerenderCandidates be an empty list.

  5. ⌛ For each ruleSet of document’s list of speculation rule sets:

    1. For each rule of ruleSet’s prefetch rules:

      1. ⌛ Let anonymizationPolicy be null.

      2. ⌛ If rule’s requirements contains "anonymous-client-ip-when-cross-origin", set anonymizationPolicy to a cross-origin prefetch IP anonymization policy whose origin is document’s origin.

      3. For each url of rule’s URLs:

        1. ⌛ Let referrerPolicy be the result of computing a speculative action referrer policy given rule and null.

        2. Append a prefetch candidate with URL url, anonymization policy anonymizationPolicy, referrer policy referrerPolicy, eagerness rule’s eagerness, and No-Vary-Search hint rule’s No-Vary-Search hint to prefetchCandidates.

      4. ⌛ If rule’s predicate is not null, then:

        1. ⌛ Let links be the result of finding matching links given document and rule’s predicate.

        2. For each link of links:

          1. ⌛ Let url be link’s url.

          2. ⌛ Let referrerPolicy be the result of computing a speculative action referrer policy given rule and link.

          3. Append a prefetch candidate with URL url, anonymization policy anonymizationPolicy, referrer policy referrerPolicy, and No-Vary-Search hint rule’s No-Vary-Search hint to prefetchCandidates.

    2. For each rule of ruleSet’s prerender rules:

      1. For each url of rule’s URLs:

        1. ⌛ Let referrerPolicy be the result of computing a speculative action referrer policy given rule and null.

        2. ⌛ Let prerenderCandidate be a new prerender candidate whose URL is url, target navigable name hint is rule’s target navigable name hint, referrer policy is referrerPolicy, and eagerness is rule’s eagerness.

        3. Append prerenderCandidate to prerenderCandidates.

      2. ⌛ If rule’s predicate is not null, then:

        1. ⌛ Let links be the result of finding matching links given document and rule’s predicate.

        2. For each link of links:

          1. ⌛ Let url be the link’s url.

          2. ⌛ Let target be rule’s target navigable name hint.

          3. ⌛ If target is null, set it to the result of getting an element’s target given link.

          4. ⌛ Let referrerPolicy be the result of computing a speculative action referrer policy given rule and link.

          5. ⌛ Let prerenderCandidate be a new prerender candidate whose URL is url, target navigable name hint is target, and referrer policy is referrerPolicy.

          6. Append prerenderCandidate to prerenderCandidates.

  6. For each prefetchRecord of document’s prefetch records:

    1. ⌛ If prefetchRecord’s label is not "speculation-rules", then continue.

    2. Assert: prefetchRecord’s state is not "canceled".

    3. ⌛ If no element of prefetchCandidates continues prefetchRecord, then cancel and discard prefetchRecord given document.

  7. End the synchronous section, continuing the remaining steps in parallel.

  8. For each prefetchCandidate of prefetchCandidates:

    1. The user agent may run the following steps:

      1. Let prefetchRecord be a new prefetch record whose URL is prefetchCandidate’s URL, anonymization policy is prefetchCandidate’s anonymization policy, referrer policy is prefetchCandidate’s referrer policy, No-Vary-Search hint is prefetchCandidate’s No-Vary-Search hint, and label is "speculation-rules".

      2. Prefetch given document and prefetchRecord.

  9. For each prerenderCandidate of prerenderCandidates:

    1. The user agent may start referrer-initiated prerendering given prerenderCandidate’s URL, document, and prerenderCandidate’s referrer policy.

      The user agent can use prerenderCandidate’s target navigable name hint as a hint to their implementation of the start referrer-initiated prerendering algorithm. This hint indicates that the web developer expects the eventual activation of the created prerendering traversable to be in place of a particular predecessor traversable: the one that would be chosen by the invoking the rules for choosing a navigable given prerenderCandidate’s target navigable name hint and document’s node navigable.

      This is just a hint. The target navigable name hint actually has no normative implications, after being parsed. It is still perfectly fine to activate in place of a different predecessor traversable that was not hinted at.

When selecting among prefetchCandidates and prerenderCandidates, user agents should consider their eagerness, in accordance with the following:

"immediate"

The author believes this is very likely to be worthwhile, and might also expect it to require significant lead time to complete. User agents should usually enact the candidate as soon as practical, subject only to considerations such as user preferences, device conditions, and resource limits.

"eager"

User agents should enact the candidate on even a slight suggestion that the user may navigate to this URL in the future. For instance, the user might have moved the cursor toward a link or hovered it, even momentarily, or paused scrolling when the link is one of the more prominent ones in the viewport. The author is seeking to capture as many navigations as possible, as early as possible.

"moderate"

User agents should enact the candidate if user behavior suggests the user may navigate to this URL in the near future. For instance, the user might have scrolled a link into the viewport and moved the cursor over it for some time. The author is seeking a balance between "eager" and "conservative".

"conservative"

User agents should enact the candidate only when the user is very likely to navigate to this URL at any moment. For instance, the user might have begun to interact with a link. The author is seeking to capture some of the benefits of speculative loading with a fairly small tradeoff of resources.

A user agent’s heuristics for enacting non-eager candidates could incorporate a No-Vary-Search hint. For example, a user hovering over a link whose URL and a candidate’s URL are equivalent modulo search variance given the candidate’s No-Vary-Search hint could indicate to the user agent that enacting it would be useful.

Notwithstanding the above, user agents should prioritize user preferences (express and implied, such as a low-data-usage mode) over eagerness expressed by the author.

We should also cancel speculated prerenders.

1.8. Document rule predicate matching

A document rule predicate predicate matches an Element el implementing the HTMLHyperlinkElementUtils mixin if the following steps return true:
  1. If predicate is a document rule conjunction, then:

    1. For each clause of predicate’s clauses:

      1. If clause does not match el, return false.

    2. Return true.

  2. If predicate is a document rule disjunction, then:

    1. For each clause of predicate’s clauses:

      1. If clause matches el, return true.

    2. Return false.

  3. If predicate is a document rule negation, then:

    1. If predicate’s clause matches el, return false.

    2. Return true.

  4. If predicate is a document rule URL pattern predicate, then:

    1. Let href be the result of running el’s href getter steps.

    2. For each pattern of predicate’s patterns:

      1. Match given pattern and href. If the result is not null, return true.

    3. Return false.

  5. If predicate is a document rule CSS selector predicate, then:

    1. For each selector of predicate’s selectors:

      1. Match selector against el with the scoping root set to el’s root. If the result is true, return true.

        During this step, user agents must apply the same privacy restrictions to the :visited pseudo-class as they would to other selector matching logic that could be observed by authors (e.g., querySelector(selectors)).

        This is important to prevent this from opening an avenue for attackers to gather information about a user’s browsing history, e.g., by using a selector such as :root:has(.sensitive-site:visited) .report-sensitive-site. See the Privacy Considerations section.
    2. Return false.

  6. Assert: This step is not reached.

2. Content Security Policy

Inline script use is restricted by [CSP], but there are cases where a developer doesn’t want to permit general inline scripts, but wants to permit inline speculation rules. So, we make the following change that extends [CSP] to support a new source keyword for inline speculation rules.

2.1. Directives

In Content Security Policy 3 § 2.3 Directives, we define several associated algorithms, and an inline check algorithm needs a patch to define how to choose the type argument.
  1. An inline check, which takes an Element, a type string, a policy, and a source string as arguments, and is executed during Content Security Policy 3 § 4.2.3 Should element’s inline type behavior be blocked by Content Security Policy? and during Content Security Policy 3 § 4.2.4 Should navigation request of type be blocked by Content Security Policy? for javascript: requests. This algorithm returns "Allowed" unless otherwise specified.

    The type string is set to "script speculationrules" if the Element is HTMLScriptElement and the type attribute is "speculationrules", or set to "script attribute" for JavaScript event handlers. Otherwise, it is set to "script".

2.2. Source Lists

In Content Security Policy 3 § 2.3.1 Source Lists, we change the following definition to have a new source, inline-speculation-rules.
keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
  / "'strict-dynamic'" / "'unsafe-hashes'"
  / "'report-sample'" / "'unsafe-allow-redirects'"
  / "'wasm-unsafe-eval'" / "'inline-speculation-rules'"

2.3. Should element’s inline type behavior be blocked by Content Security Policy?

Also, we make a following change for a note in Content Security Policy 3 § 4.2.3 Should element’s inline type behavior be blocked by Content Security Policy?.

Note: The valid values for type are "script", "script attribute", "script speculationrules", "style", and "style attribute".

2.4. script-src

Also we make the following change for Content Security Policy 3 § 6.1.10 script-src.
  1. Inline script blocks MUST pass through Content Security Policy 3 § 4.2.3 Should element’s inline type behavior be blocked by Content Security Policy?. Their behavior will be blocked unless every policy allows inline script, either implicitly by not specifying a script-src (or default-src) directive, or explicitly, by specifying "unsafe-inline", a nonce-source or a hash-source that matches the inline block , or specifying "inline-speculation-rules" on the type attribute being "speculationrules" .

2.5. Does a source list allow all inline behavior for type?

The algorithm needs a patch to handle the "script speculationrules" type.
  1. For each expression of list:

    1. If expression matches the nonce-source or hash-source grammar, return "Does Not Allow".

    2. If type is "script", "script attribute" or "navigation" and expression matches the keyword-source "'strict-dynamic'", return "Does Not Allow".

      Note: 'strict-dynamic' only applies to scripts, not other resource types. Usage is explained in more detail in Content Security Policy 3 § 8.2 Usage of "'strict-dynamic'".

    3. If type is "script speculationrules" and expression matches the keyword-source "'inline-speculation-rules'", set allow all inline to true.
    4. If expression is an ASCII case-insensitive match for the keyword-source "'unsafe-inline'", set allow all inline to true.

2.6. Does element match source list for type and source?

The algorithm needs patches to handle the "script speculationrules" type at the step 2 and 5.
  1. If type is "script", "script speculationrules", or "style", and Content Security Policy 3 § 6.7.3.1 Is element nonceable? returns "Nonceable" when executed upon element:

    1. For each expression of list:

      1. If expression matches the nonce-source grammar, and element has a nonce attribute whose value is expression’s base64-value part, return "Matches".

    Note: Nonces only apply to inline script and inline style, not to attributes of either element or to javascript: navigations.

  2. Let unsafe-hashes flag be false.

  3. For each expression of list:

    1. If expression is an ASCII case-insensitive match for the keyword-source "'unsafe-hashes'", set unsafe-hashes flag to true. Break out of the loop.

  4. If type is "script", "script speculationrules", "style", or unsafe-hashes flag is true:

2.7. Get the effective directive for request

The switch needs one additional case. At present, requests can only be issued in the case of the `Speculation-Rules` header, so script-src-elem is too specific and only script-src (or its fallback, default-src) applies.

"speculationrules"
  1. Return script-src.

3. Fetch

3.1. Destination

The string "speculationrules" is added to the list of valid destinations and also to the list of enumerators in RequestDestination.

4. Security considerations

4.1. Cross-site request forgery

This specification allows documents to cause HTTP requests to be issued.

When any supported action acts on a URL which is same origin to the document, then this does not constitute a risk of cross-site request forgery, since the request uses only the credentials available to the document.

Otherwise, requests are always issued without using any previously existing credentials. This limits the ambient authority available to any potentially forged request, and such requests can already be made through [FETCH], a subresource or frame, or various other means. Site operators are therefore already well-advised to use CSRF tokens or other mitigations for this threat.

Because links in a document could be selected using a document rule, if the document may contain user-generated markup then authors should construct their speculation rules to exclude such links that may have harmful side effects, e.g., by using a document rule CSS selector predicate to exclude links in regions with user-generated links or by using a document rule URL pattern predicate to allow only URL patterns known to be safe to fetch without side effects.

The `Speculation-Rules` header can also cause requests to be issued. These requests are unlikely to be useful in mounting a CSRF attack, because:

4.2. Cross-site scripting

This specification causes activity in response to content found in the document, so it is worth considering the options open to an attacker able to inject unescaped HTML.

Such an attacker is otherwise able to inject JavaScript, frames or other elements. The activity possible with this specification (requesting fetches etc) is generally less dangerous than arbitrary script execution, and comparable to other elements. It would, however, make it possible to cause prefetches of links in the document, and the existence of those prefetches could provide a vector for exfiltrating information about those links.

The same mitigations available to other features also apply here. In particular, the [CSP] script-src directive applies to the parsing of the speculation rules and the default-src directive applies to prefetch requests arising from the rules.

The possibility of leaking link URLs via this mechanism is additionally mitigated by the fact that prefetch and prerender to plaintext HTTP (other than to localhost) is not permitted, and so such an on-path attacker could not directly observe preloading request URLs, but would only have access to metadata and traffic analysis. This does not, however, replace standard XSS protections.

It’s generally not expected that user-generated content will be added as arbitrary response headers; server operators are already going to encounter significant trouble if this is possible. It is therefore unlikely that the `Speculation-Rules` header meaningfully expands the XSS attack surface. Nonetheless, the [CSP] script-src directive applies in this case as well.

4.3. Type confusion

In the case of speculation rules in an inline <script>, an application which erroneously parsed speculation rules as a JavaScript script (though user agents are instructed not to execute scripts who "type" is unrecognized) would either interpret it as the empty block {} or produce a syntax error, since the U+003A COLON (:) after the first key is invalid JavaScript. In neither case would such an application execute harmful behavior.

Since the parsing behavior of the <script> element has long been part of HTML, any modern HTML parser would not construct any non-text children of the element. There is thus a low risk of other text hidden inside a <script> element with type="speculationrules" which is parsed as part of the script content by compliant HTML implementations but as HTML tags by others.

Authors should, however, still escape any potentially attacker-controlled content inserted into speculation rules. In particular, it may be necessary to escape JSON syntax as well as, if the speculation rules are in an inline <script> tag, the closing </script> tag. [CSP] is a useful additional mitigation for vulnerabilities of this type.

Similarly, processing non-speculation rules resources as speculation rules is unlikely because this specification requires implementations to validate that its MIME type has an essence of "application/speculationrules+json".

4.4. IP anonymization

This specification allows authors to request prefetch traffic using IP anonymization technology provided by the user agent. The details of this technology are not a part of this specification; nonetheless some general principles apply.

To the extent IP anonymization is implemented using a proxy service, it is advisable to minimize the information available to the service operator and other entities on the network path. This likely involves, at a minimum, the use of [TLS] for the connection.

Site operators should be aware that, similar to virtual private network (VPN) technology, the client IP address seen by the HTTP server may not exactly correspond to the user’s actual network provider or location, and a traffic for multiple distinct subscribers may originate from a single client IP address. This may affect site operators' security and abuse prevention measures. IP anonymization measures may make an effort to use an egress IP address which has a similar geolocation or is located in the same jurisdiction as the user, but any such behavior is particular to the user agent and not guaranteed by this specification.

4.5. Mixed content

Speculation rules are either inline (so are delivered by the same origin as the document) or fetched via the `Speculation-Rules` header (in which case they are blockable as part of should fetching request be blocked as mixed content?).

The speculative actions initiated by this specification may also have similar protections. For example, prefetching of URLs which are not potentially trustworthy is not permitted at all, even from environments which are not themselves secure contexts.

5. Privacy considerations

5.1. Heuristics

Because the candidate prefetches and other actions are not required, the user agent can use heuristics to determine which actions would be best to execute. Because it may be observable to the document whether actions were executed, user agents must take care to protect privacy when making such decisions — for instance by only using information which is already available to the origin. If these heuristics depend on any persistent state, that state must be erased whenever the user erases other site data. If the user agent automatically clears other site data from time to time, it must erase such persistent state at the same time.

The use of origin here instead of site here is intentional. Origins generally form the basis for the web’s security boundary. Though same-site origins are generally allowed to coordinate if they wish, origins are generally not allowed access to data from other origins, even same-site ones.

Examples of inputs which would be already known to the document:

Examples of persistent data related to the origin (which the origin could have gathered itself) but which must be erased according to user intent:

Examples of device information which may be valuable in deciding whether prefetching is appropriate, but which must be considered as part of the user agent’s overall privacy posture because it may make the user more identifiable across origins:

5.2. Intent

While efforts have been made to minimize the privacy impact of prefetching, some users may nonetheless prefer that prefetching not occur, even though this may make loading slower. User agents are encouraged to provide a setting to disable prefetching features to accommodate such users.

5.3. Partitioning

Some user agents partition storage according to the site or origin of the top-level document. In order for prefetching and prerendering to be useful, it is therefore essential that prefetching or prerendering of a document either occur in the partition in which the navigation would occur (e.g., for a same-site URL) or in an isolated partition, so as to ensure that prefetching does not become a mechanism for bypassing the partitioning scheme.

The prefetch specification allows issuing HTTP requests which behave consistently with the partitioning scheme. If a navigation using its response would load a document in the same partition (approximately, the top-level site would not change), then partitioned state (e.g., cookies) can be sent, as they can with subresource requests and scripted fetches. If it would load a document in another partition, it would be inconsistent with the partitioning scheme to use partitioned state for the destination partition (since this would cross the boundary between partitions without a top-level navigation) and also inconsistent to use partitioned state within the originating partition (since this would result in the user seeing a document with different state than a non-prefetched navigation). Instead, a third, initially empty, partition is used for such requests. These requests therefore contain no partitioned state from either partition (though it may still be possible to fingerprint the user agent by other means).

However, the response to a prefetch request in an empty partition can only be used if:

  1. the response declares that the resource can be used even if an ordinary navigation would have sent credentials; or

  2. the destination partition contains no credentials which would have been included

Checking the latter requires examining the destination partition’s state. To avoid this being a workaround for the partitioning scheme, the prefetch must continue even if the existence of conflicting partitioned state will preclude it being used for navigation.

Redirects are possible between these two types of requests. A redirect from a same- to cross-partition URL could contain information derived from partitioned state in the originating partition; however, this is equivalent to the originating document fetching the same-partition URL itself and then issuing a request for the cross-partition URL. A redirect from a cross- to same-origin URL could carry state from the isolated partition, but since this partition has no prior state this does not enable tracking based on the user’s prior browsing activity on that site, and the document could construct the same state by issuing uncredentialed requests itself.

5.4. Identity joining

This specification describes a mechanism through which HTTP requests for later top-level navigation (in the case of prefetching) can be made without a user gesture. It is natural to ask whether it is possible for two coordinating sites to connect user identities.

Since existing credentials for the destination origin are not sent (assuming it is not same origin with the referrer), that site is limited in its ability to identify the user before navigation in a similar way to if the referrer site had simply used [FETCH] to make an uncredentialed request. Upon navigation, this becomes similar to ordinary navigation (e.g., by clicking a link that was not prefetched).

To the extent that user agents attempt to mitigate identity joining for ordinary fetches and navigations, they can apply similar mitigations to prefetched navigations.

It is imperative that the features in this specification do not allow sites to determine which other web sites a user has visited in the past.

Because a document rule CSS selector predicate can use arbitrary CSS selectors, it is critical that browsers apply the same privacy mitigations (which may not be covered by a specification) to the :visited pseudo-class as in other cases where it could be used by an attacker to extract information about the user’s browsing history. The matching algorithm requires this behavior. For more information, see Privacy and the :visited selector.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSP]
Mike West; Antonio Sartori. Content Security Policy Level 3. URL: https://w3c.github.io/webappsec-csp/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. URL: https://drafts.csswg.org/css-contain-2/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[FETCH-METADATA]
Mike West. Fetch Metadata Request Headers. URL: https://w3c.github.io/webappsec-fetch-metadata/
[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/
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing Standard. Living Standard. URL: https://mimesniff.spec.whatwg.org/
[MIXED-CONTENT]
Emily Stark; Mike West; Carlos IbarraLopez. Mixed Content. URL: https://w3c.github.io/webappsec-mixed-content/
[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
[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. URL: https://drafts.csswg.org/selectors/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[URLPATTERN]
Ben Kelly; Jeremy Roman; 宍戸俊哉 (Shunya Shishido). URL Pattern Standard. Living Standard. URL: https://urlpattern.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[TLS]
E. Rescorla. The Transport Layer Security (TLS) Protocol Version 1.3. August 2018. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8446

Issues Index

We should consider whether we also want to make this execute even if scripting is disabled.
We should also incorporate the case where a src attribute is set.
We could fire error and load events if we wanted to.
It’s likely that we should also handle prerendered and back-forward cached documents.
We should also cancel speculated prerenders.