Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Specify processing model in terms of Fetch #86

Closed
Tracked by #38
annevk opened this issue Mar 14, 2019 · 26 comments · Fixed by whatwg/html#8111
Closed
Tracked by #38

Specify processing model in terms of Fetch #86

annevk opened this issue Mar 14, 2019 · 26 comments · Fixed by whatwg/html#8111

Comments

@annevk
Copy link
Member

annevk commented Mar 14, 2019

I thought we had a dedicated issue for this in this repository, but I can't find it.

#63 and #66 are related though.

@jyasskin
Copy link
Member

jyasskin commented Mar 18, 2019

@yoavweiss, is this on your plate, perhaps as whatwg/html#4115?

@yoavweiss
Copy link
Contributor

yoavweiss commented Mar 20, 2019

Prefetch processing is on my plate: whatwg/fetch#881 and whatwg/html#4115. I plan to update the HTML PR this week.

@yoavweiss
Copy link
Contributor

/cc @noamr

@noamr
Copy link
Contributor

noamr commented Feb 25, 2022

My current action plan based on this discussion:

  • Define <link rel=prefetch> as something that applies only to the requests coming from the current origin (including workers, service-workers, etc and not limited to current document)
  • Reference nav-speculation/prefetch as the ongoing effort for cross-origin navigation prefetch (described here as prenavigate)
  • Defer cross-origin subresource prefetching to a later stage

This would make a clear distinction between preload & prefetch:

  • preload is high-priority, same-document
  • prefetch is low-priority, same-origin

@noamr
Copy link
Contributor

noamr commented Feb 27, 2022

I've researched how implementations handle prefetch today. I can spec something but because implementations do things very differently, it would be better to reach a consensus first.

  • WebKit fetches the link immediately, and saves the result in the link memory cache. It does not send error/load events. Something along the lines of:
const cache = new WeakMap();
function prefetch(link) {
   for (const l of document.querySelectorAll('link')) {
      if (cache.has(l)
         return cache.get(l);
   }
   cache.set(link, fetch(link.href));
}
  • Gecko saves the prefetches in a queue, and pops it one-by-one after document load. It also fires load and error events, and coalesces same-URL requests:
const prefetchQueue = [];
let prefetching = false;
function prefetchNext() {
     prefetching = true;
     if (!prefetchQueue.length)
         return;
     const links = prefetchQueue.pop();
     fetch(links[0].href, {internalOptions: { priority: 'low' }})
         .then(() => links.forEach(l => .dispatchEvent('load')))
         .catch(() =>  links.forEach(l => .dispatchEvent('error')))
}

function prefetch(link) {
    for (const p of prefetchQueue) {
       if (p[0].href === link.href) {
         p.push(link);
         return;
       }
    }

  window.prefetchQueue.push([link]);
  if (prefetching)
     prefetchNext();
}

window.addEventListener('load', prefetchNext);
  • Chromium fetches the resource immediately but in "low priority". It fires load / error events. Prefetched resources stay in HTTP cache for 5 minutes without requiring validation.
function prefetch(link) {
   fetch(link.href, { internalOptions: { priority: 'low', remainInCacheWithoutValidationMinutes: 5 }})
       .then(() => { link.dispatch('load') })
       .catch(() => { link.dispatch('error') })
}

So to summarize:

User-Agent Coalesce requests ("memory cache") Wait until document load Fire events Remain in cache without validation
Chromium No No Yes Yes
Gecko Yes Yes Yes No
WebKit Yes No No No

I believe that we should reach a consensus about making the above table interoperable - otherwise prefetch means something different based on which user-agent serves the document.

@noamr
Copy link
Contributor

noamr commented Feb 28, 2022

Straw-man proposal for prefetch behavior:

  • Coalesces requests (like Gecko & WebKit)
  • Waits until document onload (like Gecko)
  • Fires load & error events (like Gecko & Chromium)
  • Remains in cache without requiring validation for X minutes (like Chromium)

@yoavweiss
Copy link
Contributor

yoavweiss commented Feb 28, 2022

  • Waits until document onload (like Gecko)

This is something I tried to tackle when running into priority issues, but it got push back due to the possibility of regressing certain usage patterns. Might be worthwhile to take a second look and try to gather data.
/cc @pmeenan

@noamr
Copy link
Contributor

noamr commented Feb 28, 2022

  • Waits until document onload (like Gecko)

This is something I tried to tackle when running into priority issues, but it got push back due to the possibility of regressing certain usage patterns. Might be worthwhile to take a second look and try to gather data. /cc @pmeenan

It's also possible to say that the definition of "low priority" can be UA-specific, and leave only the other 3 options (fire events, coalesce requests, remain in cache without validation).

I believe those 3 need to be specified to reach any kind of interoperability with prefetch, with the onload thing at a slightly lower priority.

@yoavweiss
Copy link
Contributor

This was discussed as the WG call.

Notable comments/conclusions:

  • Prefetch's behavior RE inflight requests should be specified as well.
  • The cross-origin navigation prefetch scenario is complex and arbitrarily caching there can result in unintended consequences. That's defined as part of speculation rules

@noamr
Copy link
Contributor

noamr commented Mar 4, 2022

Another proposal, alternative to the chromium 5-minute rule:
prefetched subresources are the same as preloads, except that they are loaded in low priority (which could mean several things), and they are made available as preloads for the next navigation if it's same-origin.
This would be an observable-enough rule to make prefetches count...

@yoavweiss
Copy link
Contributor

/cc @bdekoz @yutakahirano @achristensen07 @sefeng211 for opinions on the latest proposal.

@noamr
Copy link
Contributor

noamr commented Mar 9, 2022

To move things forward, I created a strawman HTML PR in that spirit: whatwg/html#7693

@yutakahirano
Copy link

cc: @nyaxt @nhiroki

@domenic
Copy link

domenic commented Mar 22, 2022

I'm trying to understand the current landscape a bit better. #86 (comment) is helpful for some detailed issues but I lack a higher-level picture. Some questions:

  • What are web developers using <link rel=prefetch> for today? (Possible answers include: priming same-origin HTTP cache, priming cross-origin HTTP cache, subresources vs. documents, speeding up next navigation, speeding up subresource fetches for the current document similar to <link rel=preload> ...)

  • Relatedly, what use cases does <link rel=prefetch> work for today? (@noamr's comment doesn't really indicate whether the memory cache in question survives across navigations or across origins, so it's hard to tell what works today, and whether his strawperson proposal expands or reduces those possibilities.)

  • What use cases are not covered by <link rel=preload>, that we might want to try to cover with <link rel=prefetch>? How well do those line up with the above?

  • What impact does cache partitioning have on today's usages? (E.g., I could imagine some of the things web developers are trying to do, or that prefetch has historically done, just don't work at all in browsers with partitioned caches.)

@noamr
Copy link
Contributor

noamr commented Mar 22, 2022

I'm trying to understand the current landscape a bit better. #86 (comment) is helpful for some detailed issues but I lack a higher-level picture. Some questions:

  • What are web developers using <link rel=prefetch> for today? (Possible answers include: priming same-origin HTTP cache, priming cross-origin HTTP cache, subresources vs. documents, speeding up next navigation, speeding up subresource fetches for the current document similar to <link rel=preload> ...)

Speeding up the next navigations by loading some of its subresources in advance. But probably some more specific data would be useful here.

  • Relatedly, what use cases does <link rel=prefetch> work for today? (@noamr's comment doesn't really indicate whether the memory cache in question survives across navigations or across origins, so it's hard to tell what works today, and whether his strawperson proposal expands or reduces those possibilities.)

Currently prefetch is a low priority fetch into the regular HTTP cache. In Chromium it survives in the cache for 5 minutes without validation. So it survives across navigations but not across origins (double-keyed etc).

  • What use cases are not covered by <link rel=preload>, that we might want to try to cover with <link rel=prefetch>? How well do those line up with the above?

Preload only works for current document.

  • What impact does cache partitioning have on today's usages? (E.g., I could imagine some of the things web developers are trying to do, or that prefetch has historically done, just don't work at all in browsers with partitioned caches.)

It makes prefetch only work for subresources across same-origin navigations.

@domenic
Copy link

domenic commented Mar 22, 2022

Currently prefetch is a low priority fetch into the regular HTTP cache. In Chromium it survives in the cache for 5 minutes without validation.

So it works for same-origin navigational fetches too, right? Not just subresources?

What about in other browsers; what use cases does it work for today in them?

So it survives across navigations but not across origins (double-keyed etc).

Could it be used to speed up navigations of double-keyed subframes? E.g. if I do <link rel="prefetch" href="https://other.example/foo"> and then navigate my other.example subframe to that URL, or to some page that references that URL as a subresource, it sounds like that would work. Right?

@noamr
Copy link
Contributor

noamr commented Mar 22, 2022

Currently prefetch is a low priority fetch into the regular HTTP cache. In Chromium it survives in the cache for 5 minutes without validation.

So it works for same-origin navigational fetches too, right? Not just subresources?

Yes, that use case overlaps somewhat with nav-speculation.

What about in other browsers; what use cases does it work for today in them?

This was actually introduced in Gecko, and the Mozilla-specific FAQ talks about roughly the same use-cases. @bdekoz can perhaps shed more light.

There's a lot of conversation here about how WebKit sees the feature.
The feature had somewhat of a different angle to it before partitioned caching, also according to that WebKit bug.

So it survives across navigations but not across origins (double-keyed etc).

Could it be used to speed up navigations of double-keyed subframes? E.g. if I do <link rel="prefetch" href="https://other.example/foo"> and then navigate my other.example subframe to that URL, or to some page that references that URL as a subresource, it sounds like that would work. Right?

Right.

@jeremyroman
Copy link

jeremyroman commented Mar 22, 2022

Yeah, I wasn't aware had been so widely evangelized for subresources (I'd mainly read articles advocating its use for documents). Alexandre's comments on the WebKit bug, for instance, are surely related to instant.page which injects them for document URLs. If we leave such use cases in the cold here, it'll regress those sites and at a minimum benefit from a migration path to something suitable for navigations.

Do you know offhand if we have any way of distinguishing these in <link rel=prefetch>; i.e. do authors provide as, and do browsers respect it for prefetch links? (Even providing as isn't really sufficient if the URL is cross-site because we can't tell if it's intended for a subframe on a same-site navigation, or for use in a top-level navigation, and thus can't know which partition it's targeted for.) My impression is not.

It feels like this should end up behaving at least similarly to Link rel preload headers discovered in the process of a navigational prefetch, which necessitates some form of further integration at least.

By the way, I'm not sure whether we care, but technically there's also a fun edge case if a resource is Vary: Referer where the prefetch link's fetch is sent with a different referrer than the real subresource fetch would be.

@noamr
Copy link
Contributor

noamr commented Mar 23, 2022

Yeah, I wasn't aware had been so widely evangelized for subresources (I'd mainly read articles advocating its use for documents). Alexandre's comments on the WebKit bug, for instance, are surely related to instant.page which injects them for document URLs. If we leave such use cases in the cold here, it'll regress those sites and at a minimum benefit from a migration path to something suitable for navigations.

True, It was originally more evangelized for documents. But with partitioned cache a lot of those sites in the cold have already regressed.

Do you know offhand if we have any way of distinguishing these in <link rel=prefetch>; i.e. do authors provide as, and do browsers respect it for prefetch links? (Even providing as isn't really sufficient if the URL is cross-site because we can't tell if it's intended for a subframe on a same-site navigation, or for use in a top-level navigation, and thus can't know which partition it's targeted for.) My impression is not.

There is no as for prefetch.

It feels like this should end up behaving at least similarly to Link rel preload headers discovered in the process of a navigational prefetch, which necessitates some form of further integration at least.

Right, they should behave somewhat similarly. But it should also behave similarly to how it is today - adding it to the HTTP cache and maintaining it there for a while (5 minutes in chrome, I propose: until the next same-origin navigation).

By the way, I'm not sure whether we care, but technically there's also a fun edge case if a resource is Vary: Referer where the prefetch link's fetch is sent with a different referrer than the real subresource fetch would be.

True, though only with unsafe-url as prefetch is origin-partitioned. The case here shouldn't differ from a regular HTTP cache hit/miss.

@jeremyroman
Copy link

Considering checking what sorts of MIME types are typically seen in link rel=prefetch responses to contextualize this.

@jeremyroman
Copy link

Yeah, I wasn't aware had been so widely evangelized for subresources (I'd mainly read articles advocating its use for documents). Alexandre's comments on the WebKit bug, for instance, are surely related to instant.page which injects them for document URLs. If we leave such use cases in the cold here, it'll regress those sites and at a minimum benefit from a migration path to something suitable for navigations.

True, It was originally more evangelized for documents. But with partitioned cache a lot of those sites in the cold have already regressed.

Even in UAs with partitioned cache, same-site documents still work today, no?

aarongable pushed a commit to chromium/chromium that referenced this issue Mar 23, 2022
It isn't obvious what sort of resource this is typically used to
prefetch, and getting a rough estimate of this data from the field would
be helpful in understanding how this feature is used and how it can be
specified and evolved.

Some uncertainty arose in
w3c/resource-hints#86

Change-Id: I62d7e37c60aef7b5072d440a697642bfbe3816fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546102
Reviewed-by: Ian Clelland <iclelland@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984502}
@jeremyroman
Copy link

By the way, I'm not sure whether we care, but technically there's also a fun edge case if a resource is Vary: Referer where the prefetch link's fetch is sent with a different referrer than the real subresource fetch would be.

True, though only with unsafe-url as prefetch is origin-partitioned. The case here shouldn't differ from a regular HTTP cache hit/miss.

Well firstly I think there is at least a case to be made for site (because that's how partitioning works).

Secondly, nit it's not just unsafe-url because many referrer policies, including the default strict-origin-when-cross-origin do send the full path. But agreed that this isn't a big issue if we respect Vary generally.

@noamr
Copy link
Contributor

noamr commented Mar 24, 2022

I ran an HTTP Archive query to cross-cut Purpose: prefetch with mime-type

SELECT any_value (url), count(*) as count, mimeType FROM 'httparchive.summary_requests.2021_03_01_mobile' where reqOtherHeaders like "%prefetch%" and req_host is not NULL group by mimeType order by count desc LIMIT 10000

Results:
40% Javascript
29% HTML
13% CSS

  • lots of small ones

A random example from HA for a page that prefetches JS:
https://www.desty.app/

https://docs.google.com/spreadsheets/d/1umTYeg5EEM1OOP-NK76cMTc_Uj4Q38QB0Lo_NxxAnhE/edit#gid=541161129

@yoavweiss
Copy link
Contributor

May be interesting to look into caching as well (similar to this past query)

@noamr
Copy link
Contributor

noamr commented Mar 25, 2022

May be interesting to look into caching as well (similar to this past query)

Adjusting for cache, the numbers are similar and about 90% of the responses are cacheable.

@yoavweiss
Copy link
Contributor

@yutakahirano - given @noamr's numbers here, would it make sense for Chromium to simplify the caching implementation around non-cacheable prefetches? Would it be worthwhile to add counters for cases where such a non-cachable resources are used?

mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
It isn't obvious what sort of resource this is typically used to
prefetch, and getting a rough estimate of this data from the field would
be helpful in understanding how this feature is used and how it can be
specified and evolved.

Some uncertainty arose in
w3c/resource-hints#86

Change-Id: I62d7e37c60aef7b5072d440a697642bfbe3816fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546102
Reviewed-by: Ian Clelland <iclelland@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984502}
NOKEYCHECK=True
GitOrigin-RevId: 5d98fbfd97bb9d589364a22ce29b41f714bbc492
domenic pushed a commit to whatwg/html that referenced this issue Jan 17, 2023
Prefetch is simply a fetch, which populates the HTTP cache, with no post-processing of the resource and with a special header Sec-Purpose: prefetch. (The latter is specified in whatwg/fetch#1576.)

Closes #5229.
Closes w3c/resource-hints#86.
Closes w3c/resource-hints#74.
Closes whatwg/fetch#1008.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants