Page MenuHomePhabricator

Evaluate alternatives for @embed
Open, LowPublic

Description

This follows task T118514: Consider disabling data URI embedding in CSSMin for ResourceLoader and T121730: Audit use of @embed and remove where not needed (2019).


We currently have @embed which embeds the data inside the stylesheet. While this seems fine for critical-style images, we should consider providing an alternative for non-critical images.

Ideas:

  • @push annotation - would make use of HTTP/2 server push.
  • @preload annotation - would add the url in a preload link header.

The problem with non-critical images is that removing embedding without an alternative, plain delivery has the problem of being discovered late and requires additional roundtrips at a point in time when the browser is supposed to render it immediately. (e.g. :hover, :active, class changes, creating new elements, opening dialogs, etc.)

Event Timeline

Krinkle raised the priority of this task from to Needs Triage.
Krinkle updated the task description. (Show Details)
Krinkle subscribed.
Krinkle set Security to None.
Gilles renamed this task from Evaluate implementing a @push annotation in CSSMin to Evaluate alternatives for @embed.Dec 6 2016, 3:39 PM

My concern about @preload is that preloading is only supported in Chrome right now, and that removing @embed in favor of @preload would/might degrade performance for non-Chrome browsers. As you point out, this is an issue for hover images, and also for cases like toolbars where large runs of image-styled elements appear at once (and the browser fetches a dozen 24x24px images all in separate requests). I'd argue that some of these are actually good candidates for embedding, especially the ones that are small, used in groups, and are SVGs (since SVGs are text, and CSS is also text).

(A similar concern applies for @push, but I forget which browsers support HTTP/2 these days; is it widespread enough that we can defensibly degrade performance for HTTP/1.1 clients?)

I think for now HTTP/2 Push support is still in the mid to long-term future for us. There are many layers that may need to support it first (PHP/HHVM, Apache, Varnish Cache, Nginx SSL termination), and there are unresolved issues (e.g. don't push if the client has it cached already).

Instead, I'm recommend we focus on preload instead. Especially because I expect that, once Push support settles, it can use preload headers to decide what resources may be pushed. The W3C Preload spec has non-normative text suggesting this (https://w3c.github.io/preload/#server-push-http-2) and CDNs like CloudFare already follow it (example).

I agree with the browser support concern, which is why I don't think we should remove support for @embed in general. Part of task T121730 is to establish conventions for when it is appropiate to use. We haven't reached a conclusion there, but here's a few things to think about:

  • Only embed images required during page load quality?
  • Only embed images required above the fold?
  • Only embed images relating to an immediate user interaction?

After reading the first two rules, one thing that comes to mind is images used by on-hover styles. We want visual changes to apply immediately when users hover a button having hover styles.

On the other hand, we always prefer CSS to shape and style buttons, and don't require images for render. (Regardless of hover.) Buttons may have icons, but space for these is reserved ahead of time so the initial image may show up a little later. This is similar how any other <img>` on the page have their space reserved while things download.

We first added @embed to these icons to reduce HTTP request overhead and as a way to improve compression by essentially turning the stylesheet into a "Sprite" image. (https://www.mediawiki.org/wiki/ResourceLoader/Features#Embedding) The intent there was not for images to block appearance of text, it was to reduce the total page load time (the point after which both text and any images have been loaded).

When looking at the details of @preload there are two important takeaways:

  • It makes a high-priority request. (Similar priority as critical stylesheets.)
  • If the resource in question is not used within a few seconds of window.onload, browsers may emit a warning to developers.

We already saw this when accidentally preloading the site's standard logo on the mobile site, which uses a different file. (T100999)

The resource /static/images/project-logos/enwiki-2x.png was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing.

If we think about @embed as a synchronous way of performing @preload, it makes sense that @embed would have the same (if not, a narrower) set of use cases where its use is appropiate.

Using @preload for hover images, then, is inappropriate given that its use is not guaranteed, and would yield one of those warnings.

However, we've got one more thing to consider: Prefetch. Prefetch is part of the Resource Hints spec. The same spec that also brought us dns-prefetch and preconnect. (We already use dns-prefetch for CentralNotice (to meta.wikimedia.org) and CentralAuth(to login.wikimedia.org, when logged-out). Prefetch works similar to Preload, but is implemented with a lower fetch priority:

  • Prefetch is meant for specifying resources that "might be required by the next navigation". (Lower priority, start optimistically as soon as page load finishes)
  • Preload is meant for critical resources that are required by the current page load. (High priority, will compete for bandwidth of HTML, CSS and images.)

Preload is quite new (Chrome-only), released for in Chrome/Android/Opera around April 2016. No other browsers have landed support yet, except WebKit/Safari. Landed in the Safari Nightly in February 2017 but not yet released to stable. http://caniuse.com/#search=preload

Prefetch has been around a little longer. Firefox (2006), Chrome (2010), Android (2013), Opera (2013), IE 10 , Edge. Apart from Opera Mini, the main browser missing support is Safari (and Mobile Safari). http://caniuse.com/#search=prefetch