Intent to Implement and ship: Mandatory `as` value for link rel preload

204 views
Skip to first unread message

Yoav Weiss

unread,
May 26, 2017, 5:52:19 AM5/26/17
to blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren

Contact emails

yo...@yoav.ws / ywe...@akamai.com

 

Spec

Related spec discussions:

https://github.com/w3c/preload/issues/80

https://github.com/whatwg/fetch/pull/547

https://github.com/whatwg/fetch/pull/549

https://github.com/whatwg/html/pull/2588

 

As this is a fairly small change to the API's surface, I don't think a TAG review is required, but let me know if you think otherwise.

 

Summary

Align the preload implementation to the spec on 3 fronts:

  • Avoid fetching a resource if `as` is missing or empty.
  • Avoid triggering an error event if `as` is invalid, missing or empty.
  • Properly reflect only a limited set of known values for `as`. 

Motivation

The motivation for this change is to avoid double downloads following developer confusion around empty `as` values, enable feature detection of supported `as` values, and simplify the HTML processing model. Details below:

  • Avoid fetching a resource if `as` is missing - we've seen many cases where developers omit the `as` value from their preload links, which often results in double downloads. We've also had developer feedback that an empty `as` value being mapped to XHR/fetch() feels magical. This change addresses that, by defining a "fetch" value for these cases and ignoring empty/missing `as` values.
  • Properly reflect a limited set of known `as` values - enable feature detection of supported `as` values (instead of the `onerror` based fallback, which we're deprecating.
  • Avoid triggering the "onerror" event when an invalid `as` value is encountered - simplify HTML's processing model.

 

Interoperability and Compatibility Risk

Interoperability risk is low, and I intend to implement similar changes in WebKit, and Firefox were part of the related spec discussions and are supportive of this change.


Compatibility risk is low. This is changing the behavior of a shipped feature, but:

  • Usage of onerror with preload (which we're removing here) is 0 when querying the HTTP archive.
    • Thanks to igrigorik@ for improving & running that query! :)
  • Almost all usage I've seen where people omit the `as` directive today is erroneous and likely to result in a double download. On top of that, preloading XHR and fetch() is riddled with issues at the moment, so breaking such preloads is likely to result in performance improvements until said issues are fixed.
  • It's unlikely that developers rely on our erroneous `as` reflection at the moment. 

Edge: No signals

Firefox: In development

Safari: In development

Web developers: Positive

 

Ongoing technical constraints

None.

 

Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)?

Yes.

 

OWP launch tracking bug

crbug.com/726647

CL & WPT tests are at https://codereview.chromium.org/2903653005/

 

Link to entry on the feature dashboard

This is a small change to the API surface so I don't think a dedicated entry is required.

 

Requesting approval to ship?

Yes, please! :)


PhistucK

unread,
May 26, 2017, 9:42:53 AM5/26/17
to Yoav Weiss, blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren

On Fri, May 26, 2017 at 12:52 PM, Yoav Weiss <yo...@yoav.ws> wrote:
  • Usage of onerror with preload (which we're removing here) is 0 when querying the HTTP archive.
  • Thanks to igrigorik@ for improving & running that query! :)
​It only counts inline event listeners. There might be usage in JavaScript​, but yes, that would be a pretty weird practice.

Would there be a perpetual console warning when the value is invalid? I think there should be, to be friendly to mistaken developers.




PhistucK

Malte Ubl

unread,
May 26, 2017, 10:03:35 AM5/26/17
to PhistucK, Yoav Weiss, blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren
There is currently (empirically) agreement between all browsers implementing preload that:
- if as attribute is empty
- and the resource is a script
- and the actual script is downloaded by a child iframe
- and the script it cacheable

that there is no double download.

Due to the unfortunate fact that preload is observing CSP rules according to the owning document, this is currently the only way to preload resources for child iframes that have a less strict CSP than their container (typical usage of an iframe as a sandbox), which is critical to increase concurrency in downloads of embeds.

Lets make sure that this change has a documented way of doing the above (just defaulting as to `fetch`?) and that this actually works without double downloads across implementations.

<link rel=preload href=script.js>
<script src=script.js></script>

currently does not double download if script.js is cacheable in any browser. An equivalent mechanism should continue to exist.


--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+unsubscribe@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABc02_Kp8ZvwAgezTzBBr5t5QAnHT0Jr1dzeiSmQabDnzmQ%2BFw%40mail.gmail.com.

Yoav Weiss

unread,
May 26, 2017, 11:57:43 AM5/26/17
to Malte Ubl, PhistucK, blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren
On Fri, May 26, 2017 at 4:03 PM Malte Ubl <malt...@google.com> wrote:
There is currently (empirically) agreement between all browsers implementing preload that:
- if as attribute is empty
- and the resource is a script
- and the actual script is downloaded by a child iframe
- and the script it cacheable

that there is no double download.

Which browsers did you test that on?
Did the test include the iframe being a cross-origin one? (so, did the parent frame and the iframe share a renderer?)

In general, what saves you from double download in this case is the "and the script is cacheable" bit, because the HTTP cache keeps the resource around regardless of type and requesting origin.

That's not guaranteed to work (e.g. I heard people talking about double keying the HTTP cache, based on resource URL as well as origin host), and will e.g. trigger the "unused resource" warning, at least in cases where the iframe and the parent frame don't share a renderer process.
 

Due to the unfortunate fact that preload is observing CSP rules according to the owning document, this is currently the only way to preload resources for child iframes that have a less strict CSP than their container (typical usage of an iframe as a sandbox), which is critical to increase concurrency in downloads of embeds.

Sounds like what you need is a better `<link rel=prefetch>`, with improved prioritization.
 

Lets make sure that this change has a documented way of doing the above (just defaulting as to `fetch`?) and that this actually works without double downloads across implementations.

<link rel=preload href=script.js>
<script src=script.js></script>

currently does not double download if script.js is cacheable in any browser. An equivalent mechanism should continue to exist.

I don't think preload should support that as it's a side-effect of current implementation. Prefetch is the answer.

Yoav Weiss

unread,
May 26, 2017, 11:59:31 AM5/26/17
to PhistucK, blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren
Yes, I have no plans to remove the console warning. 




PhistucK

Malte Ubl

unread,
May 26, 2017, 12:25:49 PM5/26/17
to Yoav Weiss, PhistucK, blink-dev, Ilya Grigorik, Domenic Denicola, Anne van Kesteren
Tested in Safari and Chrome. it would be sad to lose the functionality. Having prefetch do the right thing would be even better, of course. Right now prefetch doesn't work for the use case. It appears Chrome doesn't use the result if the prefetch request hasn't finished by the time a request to the same URL is made.

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+unsubscribe@chromium.org.

Yoav Weiss

unread,
May 26, 2017, 2:53:55 PM5/26/17
to blink-dev, yo...@yoav.ws, phis...@gmail.com, igri...@chromium.org, dom...@chromium.org, ann...@annevk.nl


On Friday, May 26, 2017 at 6:25:49 PM UTC+2, Malte Ubl wrote:
Tested in Safari and Chrome. it would be sad to lose the functionality. Having prefetch do the right thing would be even better, of course. Right now prefetch doesn't work for the use case. It appears Chrome doesn't use the result if the prefetch request hasn't finished by the time a request to the same URL is made.

That sounds like a bug in prefetch.
 

On Fri, May 26, 2017 at 8:59 AM, Yoav Weiss <yo...@yoav.ws> wrote:


On Fri, May 26, 2017 at 3:42 PM PhistucK <phis...@gmail.com> wrote:

On Fri, May 26, 2017 at 12:52 PM, Yoav Weiss <yo...@yoav.ws> wrote:
  • Usage of onerror with preload (which we're removing here) is 0 when querying the HTTP archive.
  • Thanks to igrigorik@ for improving & running that query! :)
​It only counts inline event listeners. There might be usage in JavaScript​, but yes, that would be a pretty weird practice. 

Would there be a perpetual console warning when the value is invalid? I think there should be, to be friendly to mistaken developers.

Yes, I have no plans to remove the console warning. 




PhistucK

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

Rick Byers

unread,
May 27, 2017, 8:06:28 AM5/27/17
to Yoav Weiss, Ilya Grigorik, Domenic Denicola, ann...@annevk.nl, phis...@gmail.com, blink-dev
One of our principles of web compat is that we try to avoid taking away useful functionality when there is not some other way to achieve it.  Yoav, is that prefetch bug something you could look into fixing before shipping this preload change?

Is there any web-platform-test coverage for this?

Yoav Weiss

unread,
May 29, 2017, 4:34:45 AM5/29/17
to Rick Byers, Ilya Grigorik, Domenic Denicola, ann...@annevk.nl, phis...@gmail.com, blink-dev
On Sat, May 27, 2017 at 2:06 PM Rick Byers <rby...@google.com> wrote:
One of our principles of web compat is that we try to avoid taking away useful functionality when there is not some other way to achieve it. 

IIUC, the same effect can be achieved by sending out an XHR/fetch() for said resource, instead of preloading it. That'd put said resource inside the HTTP cache (while submitting it to connect-src CSP restrictions, rather than script-src) for other renderers to take it from there.

 
Yoav, is that prefetch bug something you could look into fixing before shipping this preload change?


As this functionality is achievable using XHR, I don't think the prefetch issue should block the preload change.

 
Is there any web-platform-test coverage for this?

Not that I'm aware of. Malte, could you or your team create currently failing WPT tests expecting the behavior you need from `prefetch` and open a related issue with those tests attached?

PhistucK

unread,
May 29, 2017, 12:30:29 PM5/29/17
to Yoav Weiss, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev

On Mon, May 29, 2017 at 11:34 AM, Yoav Weiss <yo...@yoav.ws> wrote:
IIUC, the same effect can be achieved by sending out an XHR/fetch() for said resource, instead of preloading it. That'd put said resource inside the HTTP cache (while submitting it to connect-src CSP restrictions, rather than script-src) for other renderers to take it from there.

I strongly recommend against accepting this as a viable workaround. Not only does it mean that you have to expand your connect-src policy (to also include the ones in script-src), you are also encouraging inline scripts (because unless I am mistaken, there is no other effective way to execute XMLHttpRequest or fetch that does not require a script request and requesting a script defeats the entire start-loading-early concept).

PhistucK

Yoav Weiss

unread,
May 29, 2017, 1:59:16 PM5/29/17
to PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Mon, May 29, 2017 at 6:30 PM PhistucK <phis...@gmail.com> wrote:

On Mon, May 29, 2017 at 11:34 AM, Yoav Weiss <yo...@yoav.ws> wrote:
IIUC, the same effect can be achieved by sending out an XHR/fetch() for said resource, instead of preloading it. That'd put said resource inside the HTTP cache (while submitting it to connect-src CSP restrictions, rather than script-src) for other renderers to take it from there.

As Simon Pieters kindly pointed out, `<link rel=preload as=fetch href=foo onerror="this.as = ''">` is even a better way to maintain the current behavior, while addressing both the current implementation and the new behavior, which may be useful during the auto-update transition period.


I strongly recommend against accepting this as a viable workaround. Not only does it mean that you have to expand your connect-src policy (to also include the ones in script-src)

That's already what's happening today, as today `<link rel=preload>` with an empty `as` value is subject to `connect-src` restrictions.
 
, you are also encouraging inline scripts (because unless I am mistaken, there is no other effective way to execute XMLHttpRequest or fetch that does not require a script request and requesting a script defeats the entire start-loading-early concept).

The use case described does not require inline scripts. The scripts are requested from a different renderer (in an iframe) and get matches in the HTTP cache.

Simon Pieters

unread,
May 29, 2017, 4:02:26 PM5/29/17
to PhistucK, Yoav Weiss, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Mon, 29 May 2017 19:59:02 +0200, Yoav Weiss <yo...@yoav.ws> wrote:

> On Mon, May 29, 2017 at 6:30 PM PhistucK <phis...@gmail.com> wrote:
>
>>
>> On Mon, May 29, 2017 at 11:34 AM, Yoav Weiss <yo...@yoav.ws> wrote:
>>
>>> IIUC, the same effect can be achieved by sending out an XHR/fetch() for
>>> said resource, instead of preloading it. That'd put said resource
>>> inside
>>> the HTTP cache (while submitting it to connect-src CSP restrictions,
>>> rather
>>> than script-src) for other renderers to take it from there.
>>
>>
> As Simon Pieters kindly pointed out
> <http://logs.glob.uno/?c=freenode%23whatwg&s=today#c1029473>,

(More permanent link:
http://logs.glob.uno/?c=freenode%23whatwg&s=29+May+2017&e=29+May+2017#c1029473
)

> `<link
> rel=preload as=fetch href=foo onerror="this.as = ''">` is even a better
> way
> to maintain the current behavior, while addressing both the current
> implementation and the new behavior, which may be useful during the
> auto-update transition period.

That or using two <link>s if it is desirable to avoid onerror="":

<link rel=preload as=fetch href=foo> <!-- for new UAs -->
<link rel=preload href=foo> <!-- for legacy UAs -->

This works because the support for as=fetch is introduced at the same time
as the change to start ignoring as-less preload links, so the two links
are mutually exclusive -- assuming other browsers also make these two
changes at the same time.

(This is non-conforming markup since omitting as="" is indicative of a
mistake, so a conformance checker will give an error, but, this is a
temporary workaround. When the new behavior is widely supported it is
enough to use only an as=fetch link.)

Non-owner LGTM.

--
Simon Pieters
Opera Software

Jochen Eisinger

unread,
May 30, 2017, 9:26:28 AM5/30/17
to Simon Pieters, PhistucK, Yoav Weiss, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
lgtm1

The behavior Malte described sounds indeed like something that shouldn't be some undocumented side effect of specifying an empty as attribute.

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.

Philip Jägenstedt

unread,
Jun 1, 2017, 8:24:00 AM6/1/17
to Jochen Eisinger, Simon Pieters, PhistucK, Yoav Weiss, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
LGTM2

The change in behavior makes good sense to me, the chances that a "missing value default" would be the right fetch mode aren't high I'd wager. The compat risk seems low enough to just try this, I don't think use counters could help shed any light here.

To answer Rick's question, wpt are in https://codereview.chromium.org/2903653005/. However, some of the spec PRs are open, will you make sure they are merged roughly at the same time as the tests make it upstream? (There's also the options of writing tentative tests, like in https://chromium-review.googlesource.com/518156.)

Malte Ubl

unread,
Jun 1, 2017, 10:18:34 AM6/1/17
to Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Yoav Weiss, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
Yoav: Unfortunately the XHR method doesn't appear to seed the HTTP cache for scripts in Safari. It working might be an artefact of the preload implementation there?

Where is the right place to file an issue for prefetch?

To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+unsubscribe@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAARdPYeGexnavAZEZP8m2YWMcED77qJzjQvz6WLDbEJdG52Fow%40mail.gmail.com.

Yoav Weiss

unread,
Jun 1, 2017, 2:32:16 PM6/1/17
to Malte Ubl, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Thu, Jun 1, 2017 at 4:18 PM Malte Ubl <malt...@google.com> wrote:
Yoav: Unfortunately the XHR method doesn't appear to seed the HTTP cache for scripts in Safari. It working might be an artefact of the preload implementation there?

Not sure, tbh.
 

Where is the right place to file an issue for prefetch?

As it sounds like an implementation issue: crbug.com with the Blink>Loader component. Feel free to cc me on the issue.

Yoav Weiss

unread,
Jun 2, 2017, 2:14:57 AM6/2/17
to Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Thu, Jun 1, 2017 at 2:23 PM Philip Jägenstedt <foo...@chromium.org> wrote:
LGTM2

The change in behavior makes good sense to me, the chances that a "missing value default" would be the right fetch mode aren't high I'd wager. The compat risk seems low enough to just try this, I don't think use counters could help shed any light here.

To answer Rick's question, wpt are in https://codereview.chromium.org/2903653005/. However, some of the spec PRs are open, will you make sure they are merged roughly at the same time as the tests make it upstream?

The Fetch PRs have landed and Domenic is working on the HTML one. I'm hoping we'd be able to merge it tomorrow (PST).

Yoav Weiss

unread,
Jun 2, 2017, 2:46:47 AM6/2/17
to Malte Ubl, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Thu, Jun 1, 2017 at 8:32 PM Yoav Weiss <yo...@yoav.ws> wrote:
On Thu, Jun 1, 2017 at 4:18 PM Malte Ubl <malt...@google.com> wrote:
Yoav: Unfortunately the XHR method doesn't appear to seed the HTTP cache for scripts in Safari. It working might be an artefact of the preload implementation there?

Not sure, tbh.

I think the workaround that Simon proposed (use "fetch" as the `as` value, and then use "" as a fallback) will work for WebKit as well. Have you tried it?

Malte Ubl

unread,
Jun 2, 2017, 10:28:40 AM6/2/17
to Yoav Weiss, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
Can I try <link rel=preload as=fetch> in the current Safari Tech Preview already?

Malte Ubl

unread,
Jun 2, 2017, 10:39:32 AM6/2/17
to Yoav Weiss, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
I see. We can probably do that. I'm sad about the wasted CPU/RAM on an extra DOM node, but it would work.

On Fri, Jun 2, 2017 at 7:33 AM, Yoav Weiss <yo...@yoav.ws> wrote:
Not right now, but hopefully soon.

What Simon suggested was something like:
`<link rel=preload href=foo><link rel=preload href=foo as=fetch>`
which would trigger a single identical preload both before and after this change.

Yoav Weiss

unread,
Jun 2, 2017, 10:59:55 AM6/2/17
to Malte Ubl, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Rick Byers, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
On Fri, Jun 2, 2017 at 4:39 PM Malte Ubl <malt...@google.com> wrote:
I see. We can probably do that. I'm sad about the wasted CPU/RAM on an extra DOM node, but it would work.

FWIW, the extra node would be needed only as a temporary crutch during the transition period until this ships in stable implementations and legacy implementation market share is small enough.

Rick Byers

unread,
Jun 2, 2017, 1:12:14 PM6/2/17
to Yoav Weiss, Malte Ubl, Philip Jägenstedt, Jochen Eisinger, Simon Pieters, PhistucK, Ilya Grigorik, Domenic Denicola, Anne van Kesteren, blink-dev
LGTM3
Thanks for working through a transition plan here, and it certainly sounds like we'll be in a better place long term as a result of this change.
Reply all
Reply to author
Forward
0 new messages