Html: Standardize Priority Hints

Created on 7 May 2018  路  12Comments  路  Source: whatwg/html

Discussion

Priority Hints is an API that lets developers signal to the browser the relative importance of a resource to a page, so that the browser might take this into consideration when prioritizing the request. The WICG repository for Priority Hints is here and the explainer spec is here. The explainer spec lays out some good use cases. Prioritization might take the form of modifying a request's H2 priority, delaying when a request is sent out, etc.

Developers have expressed interest in an API that lets them guide the browser's native prioritization logic in cases where the developer might know better than the browser, which resources are more important to a page than others. The spec describes a new attribute named importance that can be applied to _some_ resource-fetching HTML elements (as well as the fetch() API). The attribute can take the value of auto, low, or high, as currently denoted in the explainer spec. The attribute is just a _hint_, similar to image decoding. I propose supporting this importance _hint_ on the following elements:

  • <link>
  • <img>
  • <script>
  • <iframe>
  • ...others that make sense?

I'm owning the implementation of Priority Hints in Chrome, and it seems like it'd be good to try and gauge interest in this from other implementers and work on getting this standardized. IIRC, Priority Hints has gotten good feedback at TPAC, but @addyosmani can speak more for this.

/cc @addyosmani @yoavweiss @yutakahirano @bzbarsky @wanderview (other...?)


Spec Questions/Work

Assuming this seems like a good idea to the editors, I'm happy to lead the standardization effort of this both here and in the Fetch Standard. Here are a few questions I have at the moment regarding potential spec changes:

  • It seems that the attribute can be spec'ed as an enumerated attribute with missing & invalid value defaults as the auto mode. Seeing as how it spans multiple resource-fetching elements, it seems like a good idea to include it in the URLs & Fetching section, like the CORS settings attributes, is this the right idea?
  • (Also it seems it'll need to appear in the IDL of the list of supported resource-fetching elements that appear above, as well as their processing models where the importance can be communicated to the Fetch Standard)
  • ### Subresources

    • We'd like the importance value of scripts and iframes to trickle down to their subresources. I haven't dug around enough myself, but if someone could point me to where in the spec we can acknowledge properties of the parent element of subresources that would be nice.

  • ### Module Scripts

    • I know that loading modules has some different infrastructure, and preloading, due to this infra, was different-enough to warrant the creation of the rel value modulepreload, so I'm wondering what work will need to be done here in the fetching of a module graph.

  • I'll can follow up with an issue on the Fetch Standard to determine what changes will need made there, so we can discuss that here and I can just submit a PR separately (since the changes should be relatively small compared to those here).
additioproposal needs implementer interest needs tests

Most helpful comment

I don't think we want any loads triggered by a script, in perpetuity, inheriting the priority of the script, even if we could define how that would work. It seems quite reasonable to have a high-priority script that you want loaded quickly because it builds your UI, but that doesn't necessarily mean that you want every single resource load in that UI to be high-priority. In fact, chances are you don't.

For subframes the situation is also complicated. In particular, I can see prioritizing whatever gets the subframe to firing a load event, but going back to normal priority after that... Otherwise you would probably never want to use a priority hint on any iframe that has long-lived content that keeps making requests.

For the rest, I'm not sure whether the low/auto/high setup maps well to all UAs internal prioritization schemes. @mcmanus probably has a better idea of how this works in firefox nowadays or who would know.

All 12 comments

Seeing as how it spans multiple resource-fetching elements, it seems like a good idea to include it in the URLs & Fetching section, like the CORS settings attributes, is this the right idea?

Agreed.

if someone could point me to where in the spec we can acknowledge properties of the parent element of subresources that would be nice.

This seems generally hard, and somewhat unprecedented. Maybe @annevk has more of an idea.

One idea that comes to mind is to have the Fetch layer take care of this. So, add an algorithm to all environment settings objects to determine the default priority hint for that environment settings object (= realm = global). Then, if fetch sees "auto", it consults the request's client's default priority hint.

I'm wondering what work will need to be done here in the fetching of a module graph.

All of the module script fetching is centralized in https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts. You'll want to add something to the script fetch options struct, and update all places that construct instances of that struct.

What is a subresource of a script? As far as I can tell that kind of thing only works for imports. Anything else you don't really know what script it belongs to. (Worker scripts are different of course, perhaps you want this for them too?)

For <iframe> I think the thing you want to do is to copy the state to the document. So documents need to carry some global state (and maybe workers too) that everyone looks at before looking at their local state. Very similar to referrer policy and such.

Thanks for the insights @domenic. That sounds good at the Fetch layer.

Then, if fetch sees "auto", it consults the request's client's default priority hint.

The more I think about this the more tedious it seems it could become (for implementations), recursively looking at each request's client's priority hint wherever a default is found until we get either a non-default hint or a top-level client, but I guess that is sensible.

Ah yes, the script fetch options struct, I've definitely seen that before. Perfect.

@annevk Well I guess it would be nice to have things like:

  • fetch()
  • document.createElement('img').src = 'test'

...(maybe others) honor the <script>'s importance value if it exists. Not sure if this is entirely desirable or possible though. Extracting a hint via an environment settings object at the Fetch level (like in step 5 of main fetch) as previously mentioned seems to make this impossible, since we wouldn't have a way to determine in Fetch, that the request originated from a <script> to take a different action. For example, https://github.com/w3c/webappsec-referrer-policy/issues/96 doesn't imply that the "subresources" I allude to will be requested with <script>s referrerpolicy, but instead will be requested with the script's node document's Window object's environment settings object's referrer policy...that is a mouthful. So it seems we might not be able to/want to support this.

How do you define originating <script>? That is not a concept we have.

The <script> element in the following snippet would be what I loosely think as the "originating script":

<script importance=low> <!-- script is fetched with importance=low -->
    fetch("someResource"); // fetched as if RequestInit contains {importance: 'low'} due to the "parent" script
</script>

But I'm not sure how feasible this is. I guess to do this, we'd have to modify every place where we set request's client to an environment settings object to copy the previously-used environment settings object, and replace its importance with a paren't script's importance, if said parent exists, which seems really messy and not good. Plus again, referrer policy (once added to <script>s) will not behave in this way.

@domfarolino what about

<script importance=low> function lala() { return fetch("someResource"); } </script>
<script> lala(); </script>

? (To be clear, this has been discussed a number of times and nobody really wants to do the kind of stack tracing and JavaScript engine overhaul that'd be required.)

That's a good question. That's something I missed bringing up in https://github.com/WICG/priority-hints/issues/24 as I was looking for more complicated examples. I'm not really sure about that one honestly, and I'm sure we can find even more complicated situations than that. It sounds like we shouldn't do trickle-down request prioritization for requests made within scripts (for consistency and ease of adoption), would you agree?

Edit

Since accessing document.currentScript in that example from within the lala function would yield the script without importance, I guess there'd be no importance to trickle down in that case, but I'm really not sure if that's a good idea, or helps us out at all.

Note that there's no document.currentScript for module scripts. And yeah, I don't think we want to be doing this.

I don't think we want any loads triggered by a script, in perpetuity, inheriting the priority of the script, even if we could define how that would work. It seems quite reasonable to have a high-priority script that you want loaded quickly because it builds your UI, but that doesn't necessarily mean that you want every single resource load in that UI to be high-priority. In fact, chances are you don't.

For subframes the situation is also complicated. In particular, I can see prioritizing whatever gets the subframe to firing a load event, but going back to normal priority after that... Otherwise you would probably never want to use a priority hint on any iframe that has long-lived content that keeps making requests.

For the rest, I'm not sure whether the low/auto/high setup maps well to all UAs internal prioritization schemes. @mcmanus probably has a better idea of how this works in firefox nowadays or who would know.

It seems quite reasonable to have a high-priority script that you want loaded quickly because it builds your UI, but that doesn't necessarily mean that you want every single resource load in that UI to be high-priority

That's a good point.

I can see prioritizing whatever gets the subframe to firing a load event, but going back to normal priority after that

Hmm, yeah, there are certainly a lot of miscellaneous requests a frame might make, but I guess that would be the developer's choice whether it would be worth prioritizing the whole frame or not. There seems to be two problems here:

  • The first is that non-importance-adorned requests would automatically inherit importance=high, which might cause regressions in the parent page.
  • Second is, requesting in a frame, a page that uses priority hints basically makes those inner hints useless. For example, say in an <iframe> we request a page that fetches three images with importance=low. The developer of that inner page clearly meant for these to not be prioritized high, but our model ignores this, also maybe causing regressions.

I know I've talked with @addyosmani and @KenjiBaheux about having the parent's importance take precedence, but if we _only_ let it take precedence when the parent has importance=high. In other words, subresources can always lower their importance by overriding, but can never raise it? Just a thought... This doesn't fix the case where regular non-importance-adorned `

I'm late to the party, but let me add my two cents from web perf developer point of view.

Video platforms like YouTube, Dailymotion, Vimeo etc. have <iframe> embed endpoints.
The embeds typically contain a bunch of JS, some CSS, a poster image, and a video stream.

Those embeds are included in the 3rd party websites, and some of those websites might want to have the video loaded as fast as possible.

On the other hand, many of those parent pages also have dozens or even hundreds of subresources.
The final effect is that the requests from parent page and from the iframe are intermixed, resulting in slow load time of the frame, much slower than in isolation.

Hence <iframe importance=high> could be a big win for those kind of pages.

For subframes the situation is also complicated. In particular, I can see prioritizing whatever gets the subframe to firing a load event, but going back to normal priority after that...

Sounds good to me :+1:

Second is, requesting in a frame, a page that uses priority hints basically makes those inner hints useless. For example, say in an