Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Review: Speculation Rules (Prefetch) #721

Closed
1 task done
jeremyroman opened this issue Mar 16, 2022 · 33 comments
Closed
1 task done

Design Review: Speculation Rules (Prefetch) #721

jeremyroman opened this issue Mar 16, 2022 · 33 comments
Assignees
Labels
Missing: Multi-stakeholder support Lack of multi-stakeholder support Resolution: unsatisfied The TAG does not feel the design meets required quality standards Review type: later review Topic: fetch and preload Things that live on top of (but close to) networking. Topic: networking Topic: performance Venue: WICG

Comments

@jeremyroman
Copy link

Past reviews: Speculation Rules, Prerendering

Braw mornin' TAG!

I'm requesting a TAG review of Speculation Rules (prefetch).

Speculation Rules is a flexible syntax for defining what outgoing links/URLs are eligible to be prepared speculatively before navigation (e.g., prefetched).

In particular this request covers the use of this feature to cause prefetching. In particular, the specification attempts to define prefetching consistent with partitioned storage (cross-partition prefetches are isolated) and with IP anonymization (implementation-defined, but e.g. via a proxy service).

Further details:

  • I have reviewed the TAG's Web Platform Design Principles
  • Relevant time constraints or deadlines: no hard deadlines, but if things go well we may request shipping in Chromium in March or April
  • The group where the work on this specification is currently being done: WICG
  • The group where standardization of this work is intended to be done (if current group is a community group or other incubation venue): WHATWG
  • Major unresolved issues with or opposition to this specification: some concerns are tracked in https://github.com/WICG/nav-speculation/issues
  • This work is being funded by: Google

We'd prefer the TAG provide feedback as:

☂️ open a single issue in our GitHub repo for the entire review

@otherdaniel
Copy link

This proposal uses a <script> element to host JSON content that describes the prefech rules. This potentially conflicts with CORB and the proposed ORB security mechanisms. Both try to prevent loading JSON resources into unexpected contexts. And JSON in <script> is certainly unexpected.

It's not entirely clear to me whether there is actually a conflict or whether this is a near miss, but in either case I believe the interaction with CORB/ORB requires a close look. (Possibly CSP, also.)

Since this concern is merely about rule representation, there should be numerous ways to avoid the issues without touching the substance of the proposal: Using something other than <script>, or having a unique mimetype and strictly require it, or insisting that speculation rules are always inline and won't be fetched. One could also try to modify CORB/ORB in order to accommodate Speculation Rules. The explainer thankfully already touches on these issues, so I'm hopeful this can be resolved.

@jeremyroman
Copy link
Author

@otherdaniel As you've noted the explainer touches on this and the spec also has an issue to expand the section type confusion to cover MIME issues.

Given the precedent from import maps (which also uses <script>), I think this would likely resolve to requiring a MIME type whose essence is application/speculationrules+json explicitly. AFAIK this is consistent with the guidance for CORB/ORB; is there more work that is required there?

@otherdaniel
Copy link

I think this would likely resolve to requiring a MIME type whose essence is application/speculationrules+json explicitly. AFAIK this is consistent with the guidance for CORB/ORB; is there more work that is required there?

I agree, I think that's a viable solution.

(The "requiring" bit is important, though. The problems CORB/ORB try to solve ultimately stem from the fact that browsers try to be extra clever and accept content with missing or inappropriate MIME types. So the required MIME type should be a must, not a should. But I think that's what you meant)

@hadleybeeman
Copy link
Member

Linking to our early design review

@LeaVerou
Copy link
Member

I looked at this briefly during a breakout today, but we ran out of time before discussing with the rest of the group, so these are just my own thoughts, and do not necessarily represent TAG consensus (yet):

  • I do agree that the current syntax for prefetch/prerender is clumsy and tedious, and the Quicklink example is quite compelling. Although, with the current syntax, a library would still be needed to do what Quicklink does, since there is no criteria for "is this link in the user's viewport?"
  • Any syntax for speculatively prefetching a lot of stuff brings up concerns about sustainability etc. It's a lot of wasted bandwidth, which for some users may be prohibitively expensive. If we are to make this a whole lot easier, which will lead to even more wasted bandwidth, there should be a way for users to opt out.
  • I agree it's weird to use a different language (JSON) to essentially annotate HTML elements but I also agree that extending HTML with these annotations would be clumsy.
  • On syntax: Cramming the entire logic for a conditional in a property name is not very extensible. E.g. if_not_selector_matches is essentially a microsyntax for doing negation and specifying what this criteria is going to match on (selector, href, etc). These could be entirely independent if the conditionals are an array of object literals, with one object literal per conditional. This would also allow for additional matching metadata in the future, which you may need for other criteria. E.g. if a proximity to cursor criteria is introduced, you may want to specify distance, velocity etc, if a viewport criteria is introduced you may want to specify offset etc. With the current syntax, each criteria only takes a single argument.

A rule may include a score between 0.0 and 1.0 (inclusive), defaulting to 0.5, which is a hint about how likely the user is to navigate to the URL. It is expected that UAs will treat this monotonically (i.e., all else equal, increasing the score associated with a rule will make the UA speculate no less than before for that URL, and decreasing the score will not make the UA speculate where it previously did not). However, the user agent may select a link with a lower author-assigned score than another if its heuristics suggest it is a better choice.

I would imagine the likelihood of a link being clicked might change throughout the user's interaction, so I wonder if this would make more sense as an <a> attribute? That way it can be updated by script to account for things like is it in the viewport, is the cursor close to it etc or any other arbitrary thing that makes it more or less likely to be clicked.

@tomayac
Copy link

tomayac commented Apr 19, 2022

  • Any syntax for speculatively prefetching a lot of stuff brings up concerns about sustainability etc. It's a lot of wasted bandwidth, which for some users may be prohibitively expensive. If we are to make this a whole lot easier, which will lead to even more wasted bandwidth, there should be a way for users to opt out.

One way apps could make this more sustainable could be by checking for the Save-Data header or the navigator.conection.saveData bit.

In tomayac/netinfo/README.md, I have brought up the idea of exposing the fact whether a network is metered via a new navigator.conection.metered bit, which would be more aligned with the way Android handles this: save data is for foreground things like sending lower-res images, and metered network is for background things like not syncing data (or in the concrete case not prefetching).

@jeremyroman
Copy link
Author

I looked at this briefly during a breakout today, but we ran out of time before discussing with the rest of the group, so these are just my own thoughts, and do not necessarily represent TAG consensus (yet):

  • I do agree that the current syntax for prefetch/prerender is clumsy and tedious, and the Quicklink example is quite compelling. Although, with the current syntax, a library would still be needed to do what Quicklink does, since there is no criteria for "is this link in the user's viewport?"

I'm hopeful that since this is more about heuristics for picking the best links to prefetch/prerender rather than correctness, that user agents will be able to do a decent job of this by default, appropriate for the device's form factor etc. For example, on a UA with a cursor input device (like a desktop computer), hover might be a really good signal (instant.page uses this), whereas on a UA with touch input but a small viewport (like a mobile phone), hover might not be available but the viewport is a really strong signal of user intent.

If that evolution doesn't prove as fruitful as I hope we might benefit from having authors expressly tell UAs which signals are strong hints about user intent.

  • Any syntax for speculatively prefetching a lot of stuff brings up concerns about sustainability etc. It's a lot of wasted bandwidth, which for some users may be prohibitively expensive. If we are to make this a whole lot easier, which will lead to even more wasted bandwidth, there should be a way for users to opt out.

I agree that users should be able to opt out, for a variety of reasons including cost and privacy. User agents are in a position to help with this too, for example by not prefetching (or by prefetching only very high-probability URLs) when the user is on a metered connection or has low battery life.

This is one advantage of giving a place for UAs to help developers out with this decision -- if we don't, developers can do it anyway, but using APIs that don't make it easy for the UA to intercede on the user's behalf because it's less distinguishable from fetches critical to the immediate user intent.

  • I agree it's weird to use a different language (JSON) to essentially annotate HTML elements but I also agree that extending HTML with these annotations would be clumsy.

It's certainly imperfect but JSON has great tooling in both server- and client-side web technologies, and there is similar use of it in import maps and web bundles.

  • On syntax: Cramming the entire logic for a conditional in a property name is not very extensible. E.g. if_not_selector_matches is essentially a microsyntax for doing negation and specifying what this criteria is going to match on (selector, href, etc). These could be entirely independent if the conditionals are an array of object literals, with one object literal per conditional. This would also allow for additional matching metadata in the future, which you may need for other criteria. E.g. if a proximity to cursor criteria is introduced, you may want to specify distance, velocity etc, if a viewport criteria is introduced you may want to specify offset etc. With the current syntax, each criteria only takes a single argument.

That's a fair point. If you never need two conditions of the same type, then I do think you can get somewhat far with the syntax sketch I had there (it's not specified or implemented yet), but your proposal certainly has its advantages. What I am hoping to avoid is making a boolean algebra microsyntax of arbitrary complexity (or having to write a little language that needs a parser, even) -- but I'm not strongly attached to this yet.

{
  "prefetch": [
    { "source": "document", "if": [{"not": {"href_matches": "..."}}, {"selector_matches": "..."}] }
  ]
}

Definitely a tough balance to strike so that it's as simple as possible while still being useful enough. At some point I start to be reminded of JSON Schema.

Filed WICG/nav-speculation#160 for this question.

A rule may include a score between 0.0 and 1.0 (inclusive), defaulting to 0.5, which is a hint about how likely the user is to navigate to the URL. It is expected that UAs will treat this monotonically (i.e., all else equal, increasing the score associated with a rule will make the UA speculate no less than before for that URL, and decreasing the score will not make the UA speculate where it previously did not). However, the user agent may select a link with a lower author-assigned score than another if its heuristics suggest it is a better choice.

I would imagine the likelihood of a link being clicked might change throughout the user's interaction, so I wonder if this would make more sense as an <a> attribute? That way it can be updated by script to account for things like is it in the viewport, is the cursor close to it etc or any other arbitrary thing that makes it more or less likely to be clicked.

Very possibly! I think it depends a little on whether developers find it more useful to give live-updating hints as the user interacts or whether they just want to provide a bit of a bump to built-in heuristics. This can to some extent be emulated by developers defining e.g. classes for "low likelihood", "medium likelihood", "high likelihood" and then keying off those in rules. But that's awkward if this is a common case, in which case this absolutely should become an attribute as you suggest.

Filed WICG/nav-speculation#159 for this question.

@torgo torgo added Review type: CG early review An early review of general direction from a Community Group Venue: WICG labels Apr 25, 2022
@torgo
Copy link
Member

torgo commented Apr 25, 2022

Question: is this an "early review"? What is the time-line? Also it says in Chrome status that you're in the middle of an origin trial. Has there been any feedback from this that you can share? Also it still shows no signal from other implementers. Can you let us know any multistakeholder status?

@torgo torgo added the Missing: Multi-stakeholder support Lack of multi-stakeholder support label Apr 25, 2022
@jeremyroman
Copy link
Author

#611 was the corresponding early review. We're hoping to ship in Chrome relatively soon.

So far most of the feedback I'm aware of can largely be bucketed as:

We've asked on multiple occasions for engagement from other vendors, though there hasn't been a lot. The WICG proposal for this repository captures some of that. We've requested Mozilla and WebKit positions, without response.

@kjmcnee
Copy link

kjmcnee commented Mar 1, 2023

Are there risks authors or users should know about if the inverse of this was the case? ie. a strict policy by default is overridden by a lax explicit prefetch policy? (perhaps because of a misconfiguration, or because different people configure the server headers to those who author the pages?)

If a lax policy is specified in the rule and it's for a same-site prefetch, that's the policy we use. If it's cross-site however, a lax explicit policy would prevent the prefetch attempt due to the sufficiently-strict referrer policy requirement.

So the risk would be that authors cause their prefetch attempts to be ignored. For debuggability, in the chromium implementation, we surface when an attempt is ignored due to this requirement in DevTools.

@jeremyroman
Copy link
Author

Hi again, and thanks for the feedback.

I assume that you're referring to the "where" condition syntax specifically1. For further context on that, we did previously revise it to be more general (see #160, #177) in response to #721 (comment). I did look at options leaning more heavily on CSS (and have taken another look now), but I still don't think it is a great fit here.

Speculation rules provide page authors with the ability to determine, across the page, what sorts of links are suitable to preload and what sorts are not. When that's determined by URL it looks a little bit like Content Security Policy or service worker scopes; when it's determined by page structure, CSS selectors are a useful tool. I actually would tend to expect the former to be more common, and so I'd like for it to have good ergonomics. I'm concerned putting it inside CSS syntax might hurt ergonomics in the URL pattern case2. The existing case of explicit URLs (especially for navigation other than to existing links) is not solved by a pure CSS solution.

For those speculation rules that do target links, I definitely want to be reactive to changes in document structure in the same way that CSS is – which is why the current proposal does use the selector syntax to describe that structure and Chromium's implementation relies on the style engine for invalidation. More deeply integrating this concept (related to navigation speculation) into CSS implementations and specifications seems from our experience more likely to increase the coupling with, and thus burden to, CSS implementations and specifications.

Fundamentally, this control over preloading doesn't seem presentational to me. Even though it leverages the structure and semantics of the document using selectors, it doesn't affect the appearance (or aural output, etc) of the page. The precedent we most had in mind when creating this was import maps, which also have wide-ranging effects on a page, via a JSON specifier syntax embedded in a <script> element. The JavaScript querySelectorAll API is another example of leveraging the CSS selector syntax to structurally match the document without being presentational in nature.


1 The JSON syntax generally has been previously discussed, but import maps (which are similarly declarative about behavior on the page) are comparable.

2 I tried to expand on the idea of adding a :link-href pseudo-class which would do URL pattern matching on the href (after accounting for the document's base URL) of elements which match :any-link.

Proposed syntax:

{"and": [
  {"href_matches": "/*\\?*", "relative_to": "document"},
  {"not": {"href_matches": "/logout?*", "relative_to": "document"}},
  {"not": {"selector_matches": ".no-prefetch *"}}]}

CSS selector:

:link-href("/*\\?*" relative-to document):not(:link-href("/logout?*" relative-to document), .no-prefetch *)

CSS selector (quoted):

":link-href(\"/*\\\\?*\" relative-to document):not(:link-href(\"/logout?*\" relative-to document), .no-prefetch *)"

It would be possible, but a little more awkward yet, to permit the dictionary-style (rather than shorthand) URLPattern construction, since there isn't precedent for embedding a dictionary inside a pseudo-class (existing ones generally take very few parameters).

While I could conceive of this being useful in style (e.g., to automatically style cross-origin or insecure links differently), I haven't previously heard demand for this in the CSS ecosystem. I think including this in CSS might actually increase, rather than reduce, the work required to specify, implement and test.

@toyoshim
Copy link

toyoshim commented Mar 9, 2023

Hi. We have delta updates on how the speculation rules should interact with Content Security Policy.

Explainer: https://github.com/WICG/nav-speculation/blob/main/triggers.md#content-security-policy

We added Content Security Policy section to clarify how the speculation rules interact with existing Content Security Policy, and explain the new source keyword "inline-speculation-rules".

We also added Content Security Policy section to the speculation rules spec, in order to explain the motivation and to show spec patches for Content Security Policy.
Spec (diff): https://storage.googleapis.com/spec-previews/WICG/nav-speculation/pull/245/diff/speculation-rules.html

Tests:

Chrome Status: https://chromestatus.com/feature/5182859125456896

In short, we clarify how the speculation rules are handled in CSP, and provide a new source keyword to permit safe inline speculation rules without allowing unsafe inline script under the strict CSP environment. Here is an example use.

<meta http-equiv="Content-Security-Policy" content="script-src 'inline-speculation-rules'">

<!-- this just works!! -->
<script type="speculationrules">
...
</script>

<!-- this causes a CSP violation -->
<script>
console.log('hello.');
</script>

@hober
Copy link
Contributor

hober commented Apr 20, 2023

The explainer says:

Currently, like import maps, script tags are only used for specifying speculation rules inline; future extensions may allow a src attribute to load external rule sets.

But then you allow external rule sets to be loaded with a Speculation-Rules HTTP header.

This seems inconsistent. If external rule sets are to be discouraged, why have the HTTP header? If they aren't to be discouraged, why not support linking to them in markup?

(Several minutes after writing the above, having gotten farther down the explainer document, I found this text which goes into this a bit. Maybe link to this directly so that people who wonder about this inconsistency can click to the rationale?)

@cynthia
Copy link
Member

cynthia commented Apr 20, 2023

Hi,

@cynthia, @LeaVerou, and @hober took a look at this during our Tokyo F2F today.

We are sympathetic to the requirements this sets out to fulfill. The complexity of document rules is concerning. While having solutions that can cover the whole spectrum of use-cases is nice, significant added complexity will have adverserial effects on adoption - and whenever possible we value simpler solutions that an average developer could easily understand and make use of.

If you could propose a simpler approach that could cover say, 80% of the use cases as an alternative - we would love to see this. One example that came up in our discussion was an attribute on <a> elements instead of an entirely separate technology. After all, more complex approaches can always be added later, if the need arises.

One bit about eagerness - it would be useful to state (maybe not normatively?) that ideally implementations should provide a way for the users to set their prefetch preferences, and user preferences should be treated as higher priority than the page-declared eagerness preference - in particular in low-data/bandwidth scenarios.

@jeremyroman
Copy link
Author

Thank you for taking a look. Responses below (not necessarily in order):


This seems inconsistent. If external rule sets are to be discouraged, why have the HTTP header? If they aren't to be discouraged, why not support linking to them in markup?
(Several minutes after writing the above, having gotten farther down the explainer document, I found this text which goes into this a bit. Maybe link to this directly so that people who wonder about this inconsistency can click to the rationale?)

It isn't discouraged; it simply isn't supported yet. The rationale is linked from the words "future extensions" in the explainer excerpt originally quoted. I can certainly rephrase that sentence if you think the link could be made more apparent, or it could be otherwise clarified.


it would be useful to state (maybe not normatively?) that ideally implementations should provide a way for the users to set their prefetch preferences, and user preferences should be treated as higher priority than the page-declared eagerness preference

I agree; the specification says so in two places right now, here in the context of eagerness:

"eager"
The author believes this is very likely to be worthwhile. User agents should usually enact the candidate, subject only to considerations such as user preferences, device conditions, and resource limits.

and here in the context of privacy:

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.

I've added additional normative text to be more explicit.


If you could propose a simpler approach that could cover say, 80% of the use cases as an alternative - we would love to see this. One example that came up in our discussion was an attribute on elements instead of an entirely separate technology. After all, more complex approaches can always be added later, if the need arises.

Would such a feature be useful for some authors? I do agree it might be, if only because it's very easy to explain. I think it would be quite straightforward to add as a "shorthand".

I'm less confident that it is sufficient to address 80% of use cases well, though, for a few reasons.

Firstly, while adopting such a feature for a single link would be very easy, updating many code paths which emit tags, or existing static content which includes tags, would be extremely tedious for many authors, both large and small. While this can to some extent be done dynamically with script, this is significant work (and there are some non-obvious edge cases), and browsers already have much of the infrastructure to do this matching efficiently. Similarly (though more extremely), an author library which replaced CSS rules and cascade with explicit assignment of styles to individual elements would be possible but tricky to get right and harder yet to make perform well.

Secondly, I expect that many authors would find this technology easier to adopt with support from a service provider or product, such as a CDN, hosted CMS, or application proxy. They are well-positioned to reason about the side effects that requests to particular URLs may have, but not necessarily in a position to modify documents, especially their dynamic content.

For example, a hosted CMS might know that particular URLs simply fetch a blog post or product detail page without side effects. A CDN might be configured to allow prefetching only cached HTML resources, so any same-origin link can be prefetched safely (since both hitting the cache and rejecting the request cannot have side effects on the origin server). These providers can then provide a turnkey solution to their customers, in a way that would be much more difficult if they had to modify each link in the DOM.

@plinss plinss removed the Progress: pending external feedback The TAG is waiting on response to comments/questions asked by the TAG during the review label May 14, 2023
@rhiaro rhiaro removed their assignment May 15, 2023
@torgo torgo added the Progress: propose closing we think it should be closed but are waiting on some feedback or consensus label May 15, 2023
@LeaVerou
Copy link
Member

Hi again,

Thank you for your detailed response. We revisited this in a breakout today.

We understand the arguments for a separate syntax; we did not argue that this is not useful. However, we think that the increase in complexity that adding an entirely separate JSON-based syntax adds to the Web Platform should be comensurate with the benefit developers get from it. Similar cases in the past that warranted this kind of increase in complexity have been JS import maps, or PWA manifests. We don't feel the benefit developers get from this is in the same ballpark, to justify this increase in complexity. Furthermore, considering the lack of multi-stakeholder support, it seems like the resulting fragmentation could create additional developer complexity and confusion.

We would like to suggest that you work with additional stakeholders to see if you can both garner additional support and find a less complex design.

See also:

@atanassov
Copy link

Thank you for the continued persistence in working with us on this review. Based on all of the above feedback from our earlier breakout session and early review concerns, we're closing this for now.

We note the continuation of our concerns that started in the early review. They weren't addressed but were instead made more complicated by the added/extended syntax.

We think a new feature must pass a high bar of end-user benefit to compensate for that added developer complexity. We don't see enough benefit. Right now, there is some discussion of the user need in the explainer but given the web developer pull for this is not documented and additional stakeholders have not been supportive, we're unconvinced that this proposal passes this bar.

Again, thank you for working with TAG and please feel free to bring this back when it evolves significantly to address our concerns.

@atanassov atanassov added Resolution: unsatisfied The TAG does not feel the design meets required quality standards and removed Progress: propose closing we think it should be closed but are waiting on some feedback or consensus labels May 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Missing: Multi-stakeholder support Lack of multi-stakeholder support Resolution: unsatisfied The TAG does not feel the design meets required quality standards Review type: later review Topic: fetch and preload Things that live on top of (but close to) networking. Topic: networking Topic: performance Venue: WICG
Projects
None yet
Development

No branches or pull requests