Bug 39455 - <link> elements with media queries that do not affect the current page can be delayed
Summary: <link> elements with media queries that do not affect the current page can be...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Page Loading (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Enhancement
Assignee: Antti Koivisto
URL:
Keywords: BrowserCompat, InRadar
: 243424 248724 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-05-20 16:24 PDT by Joseph Pecoraro
Modified: 2023-02-08 01:00 PST (History)
9 users (show)

See Also:


Attachments
[IMAGE] Web Inspector Shows Waiting for a useless media="print" resource (145.63 KB, image/png)
2010-05-20 16:24 PDT, Joseph Pecoraro
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Joseph Pecoraro 2010-05-20 16:24:07 PDT
Created attachment 56644 [details]
[IMAGE] Web Inspector Shows Waiting for a useless media="print" resource

HTML5 describes the <link> element's media attribute here:
http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#attr-link-media

> However, if the link is an external resource link, then the media attribute
> is prescriptive. The user agent must apply the external resource when the
> media attribute's value matches the environment and the other relevant
> conditions apply, and must not apply it otherwise.

If the browser is unaffected by the media queries in the <link media="...">
then it is safe to assume that the content is not immediately necessary
and time could be better spent download other resources. Also, it may
not be necessary to prevent the DOMContentReady or onload event's
for the resource.

In the attached image, the "print.css" file is a link with media="print". These
styles do not affect the document, yet it was downloaded and delayed some
events from firing.
Comment 1 Timothy Hatcher 2010-05-20 16:27:14 PDT
This also happens for type="alternate stylesheet".
Comment 2 Alexey Proskuryakov 2010-05-20 16:57:04 PDT
Firing load events before alternate stylesheets have loaded can easily have compatibility consequences - a script running from onload may want to switch stylesheets, for example. Even media="print" stylesheets are exposed via CSSOM.

Loading them after other resources sounds like a great idea to me.
Comment 3 Alexey Proskuryakov 2011-01-31 21:59:00 PST
Antti, is this fixed now?
Comment 4 Karl Dubost 2022-08-30 18:37:57 PDT
Antti, Alexei, 
Should we close this one in favor of Bug 243424
Comment 5 Ahmad Saleem 2022-11-09 17:30:46 PST
*** Bug 243424 has been marked as a duplicate of this bug. ***
Comment 6 Radar WebKit Bug Importer 2022-11-09 22:50:20 PST
<rdar://problem/102178226>
Comment 7 Vadim Makeev 2022-11-27 14:35:50 PST
I’d like to move my initial comment from the bug, closed as duplicate, so it won’t be lost.

Imagine a simple situation, two CSS files linked in the head:

    <link rel="stylesheet" href="screen.css">
    <link rel="stylesheet" href="print.css" media="print">

Currently, WebKit will wait for both of them to be loaded before rendering anything at all. It makes sense since CSS is a render-blocking resource.

But the browser won’t use print.css for rendering because the implicit “screen” media type doesn’t match the “print” value. And it’s clear from the HTML parsing stage: this resource is not render-blocking.

Both Firefox and Chromium will keep loading print.css with lower priority and will start rendering once screen.css is available. It won’t save traffic but will make the page appear faster.

The same could be used not only for printing but for many different applications, that would make CSS performance better:

1. Color scheme: dark.css should not block rendering if the current scheme is light

<link rel="stylesheet" href="light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="dark.css" media="(prefers-color-scheme: dark)">

2. It could be possible to split CSS into separate files based on breakpoints

    <link rel="stylesheet" href="base.css">
    <link rel="stylesheet" href="mobile.css" media="(max-width: 767px)">
    <link rel="stylesheet" href="tablet.css" media="(min-width: 768px) and (max-width: 1023px)">
    <link rel="stylesheet" href="desktop.css" media="(min-width: 1024px)">

3. It could be possible to offload some enhancements based on device properties or user preferences:

    <link rel="stylesheet" href="retina.css" media="(min-resolution: 2dppx)">
    <link rel="stylesheet" href="heavy.css" media="(prefers-reduced-data: no-preference)">
    <link rel="stylesheet" href="animation.css" media="(prefers-reduced-motion: no-preference)">

Well, it’s currently possible, but not in WebKit.

Here’s the demo with breakpoints:
https://pepelsbey.dev/articles/conditionally-adaptive/demo/index.html

And the article, exploring the idea to use this behavior:
https://pepelsbey.dev/articles/conditionally-adaptive/
Comment 8 Vadim Makeev 2022-11-27 14:37:55 PST
Here’s another reason for this behavior to be aligned with the rest of the browsers.

There’s a popular technique of CSS lazy loading introduced in Filament Group blog in 2019 https://www.filamentgroup.com/lab/load-css-simpler/

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

I see it used and mentioned here and there as one of the best and simplest option. The article states:

> …the browser will load the stylesheet without delaying page rendering, asynchronously

Which is not the case for Safari: it will be Render-blocking. No one seems to expect such incompatible behavior.
Comment 9 Alexey Proskuryakov 2022-12-04 18:08:41 PST
*** Bug 248724 has been marked as a duplicate of this bug. ***
Comment 10 Vadim Makeev 2023-02-06 04:23:41 PST
There’s another use case for this behavior that might affect performance on some websites. This quite popular CSS lazy-loading trick doesn’t work in Safari:

<link
    rel="stylesheet"
    href="critical.css"
>
<link
    rel="stylesheet"
    href="deferred.css"
    media="print"
    onload="this.media='all'"
>
<noscript>
    <link
        rel="stylesheet"
        href="deferred.css"
    >
</noscript>

All other browsers will render styles from critical.css and keep loading deferred.css in the background to apply them once they’re loaded. But Safari will wait for deferred.css and not render anything until it’s loaded.

See more details in my the post https://pepelsbey.dev/articles/lazy-loading-safari/
Comment 11 Antti Koivisto 2023-02-07 03:42:29 PST
Pull request: https://github.com/WebKit/WebKit/pull/9746
Comment 12 Antti Koivisto 2023-02-07 03:47:31 PST
The issue is the we allow stylesheets with non-matching media delay the visually non-empty milestone. This only comes into play if the page is simple enough that it doesn't hit the milestone by having enough visual content to reach the threshold.
Comment 13 Vadim Makeev 2023-02-07 06:05:41 PST
Nice! Does it affect CSS files loaded via @import? There’s a similar issue in Chromium https://bugs.chromium.org/p/chromium/issues/detail?id=1001078

These two should work the same:

<link rel="stylesheet" href="/style.css" media="(prefers-color-scheme: light)">

<style>
  @import url(import.css) (prefers-color-scheme: light)
</style>
Comment 14 Antti Koivisto 2023-02-07 06:28:33 PST
(In reply to Vadim Makeev from comment #13)
> Nice! Does it affect CSS files loaded via @import? There’s a similar issue
> in Chromium https://bugs.chromium.org/p/chromium/issues/detail?id=1001078
> 
> These two should work the same:
> 
> <link rel="stylesheet" href="/style.css" media="(prefers-color-scheme:
> light)">
> 
> <style>
>   @import url(import.css) (prefers-color-scheme: light)
> </style>

No, worth a separate bug.
Comment 15 Anthony Ricaud 2023-02-07 06:30:49 PST
@Antti: The way I read your comment, it seems this bug only affects testcase-like pages. So not many real world pages should be affected. Am I understanding correctly?

If that's the case, @Vadim, you may want to update your post to reflect that ?
Comment 16 Vadim Makeev 2023-02-07 06:40:59 PST
> The way I read your comment, it seems this bug
> only affects testcase-like pages. So not many
> real world pages should be affected

@Anthony Ricaud: There are at least two real-world use cases that I mentioned earlier in this issue.

1. Print styles that block rendering, although they’re not used
2. Lazy-loading technique that relies on the non-blocking behavior

Both of them are not test case-like.

> You may want to update your post to reflect that?

I already updated the post to mention the PR with the fix. Otherwise, I think it’s still correct.
Comment 17 Sam Sneddon [:gsnedders] 2023-02-07 07:58:43 PST
(In reply to Vadim Makeev from comment #16)
> > The way I read your comment, it seems this bug
> > only affects testcase-like pages. So not many
> > real world pages should be affected
> 
> @Anthony Ricaud: There are at least two real-world use cases that I
> mentioned earlier in this issue.
> 
> 1. Print styles that block rendering, although they’re not used
> 2. Lazy-loading technique that relies on the non-blocking behavior
> 
> Both of them are not test case-like.

However, very few pages on the web are HTML files with an empty body element: _that_ is what is being called test case-like. Add a paragraph or two to the body, and behaviour should change, and you'll see the body straight away.
Comment 18 Vadim Makeev 2023-02-07 08:18:37 PST
Oh, I see now, you’re right. I calculated that it takes a bit more than 200 characters to change the behavior. But with a typical modern SPAs setup, where you have empty <body> and a bunch of scripts, it might not be enough to trigger the proper behavior:

<body>
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <script src="app.js"></script>
</body>

This kind of page won’t get CSS loading benefits.
Comment 19 EWS 2023-02-07 09:34:06 PST
Committed 259963@main (9e31d4e46121): <https://commits.webkit.org/259963@main>

Reviewed commits have been landed. Closing PR #9746 and removing active labels.
Comment 20 Vadim Makeev 2023-02-08 01:00:45 PST
@Sam Sneddon: I updated the article, thank you! https://pepelsbey.dev/articles/lazy-loading-safari/#full-body