Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define parser behavior for in-body external stylesheets #1349

Open
pmeenan opened this issue May 30, 2016 · 31 comments
Open

Define parser behavior for in-body external stylesheets #1349

pmeenan opened this issue May 30, 2016 · 31 comments
Labels
compat Standard is not web compatible or proprietary feature needs standardizing topic: parser topic: style

Comments

@pmeenan
Copy link
Contributor

pmeenan commented May 30, 2016

In-body stylesheets are treated differently by most of the browser engines.

Edge/IE: Parser blocks until stylesheeet is loaded (painting is not blocked)
Firefox: Parser continues, loading stylesheets asynchronously (painting is not blocked)
Chrome/Blink and Safari/WebKit: Parser continues, loading stylesheets asynchronously (painting is blocked until discovered sheets load)

The Edge behavior can be emulated in Firefox by placing a non-empty script tag after the in-body stylesheets since the parser needs to block at a script tag until previous stylesheets have finished. Technically, Blink and Webkit can also emulate the same blocking behavior but it is less useful for developers since all painting is disabled until the sheets have loaded.

@bzbarsky
Copy link
Contributor

The Firefox behavior is the one currently defined in the spec, except for the painting aspect (which is not defined anywhere). So I'm not sure what this issue is asking to define, exactly.

@domenic
Copy link
Member

domenic commented May 31, 2016

Right, so, it sounds like we should be filing bugs on Edge (with test cases) showing how their parser-blocking behavior is not interoperable with the rest of the web (and is against the spec).

For painting, I'm not sure what to do, or whether to specify it at all. Maybe we could continue adding notes to https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model step 7.3. This one would be something like "Another reason browsers might want to block the rendering is if they have discovered stylesheets that they have not yet loaded, and want to avoid a flash of unstyled content."

@domenic domenic added the compat Standard is not web compatible or proprietary feature needs standardizing label May 31, 2016
@zcorpan
Copy link
Member

zcorpan commented May 31, 2016

@foolip
Copy link
Member

foolip commented May 31, 2016

@DigiTec, could you help us find a good contact for this issue in Edge?

@foolip
Copy link
Member

foolip commented May 31, 2016

Does anyone have a high level view of when browsers block painting and for what reasons? I assume it's a non-interoperable pile of heuristics, but it's part of the reality the web developers need to understand and deal with, so specifying it in some way shouldn't be off the table even if it's not observable to the page itself.

@pmeenan
Copy link
Contributor Author

pmeenan commented May 31, 2016

FWIW, Chrome is open to either the Edge or Mozilla implementations.

The main thing I'm hoping to be able to push forward is native support for the performance optimization where developers inline the critical css in the head (or push with HTTP/2) and load the remaining css later in the body. Right now for that to work they have to use something like loadcss because Blink and Webkit browsers block painting as soon as they discover the in-body css (usually before the viewport has rendered anything).

The Edge behavior has less risk of introducing Flashes of Unstyled Content (FOUC) for any sites that haven't designed for the Mozilla behavior (which is a strong possibility on mobile where Webkit and Blink get most of the attention). It also seems easier for developers to rationalize that "content after an external stylesheet won't paint until the stylesheet has loaded" but the main goal is to just get consistency across browsers so we have something developers can rely on.

@domenic
Copy link
Member

domenic commented May 31, 2016

If we're looking for consistency across browsers, then the Gecko behavior has the advantage of having the same observable-from-web-code consequences as the current Blink and WebKit behavior. So going with that makes more sense to me.

I guess it's worth finding out if Edge has encountered any compat issues because of their behavior. If so, then probably the Gecko behavior is the better choice. If not, then I guess browsers have some flexibility, and we could try to change everyone to the Edge behavior (despite that being a longer path to interop).

@bzbarsky
Copy link
Contributor

As I pointed out in https://groups.google.com/a/chromium.org/d/msg/blink-dev/QC5iefctcag/dZJZyOz-AwAJ, I think the Edge behavior requires UAs to do a bunch of unstandardized heuristics to get even close to the loading parallelism that Just Happens with the Gecko behavior.... If we wanted to standardize on the Edge behavior, I strongly feel we should standardize those heuristics too, just so new UA implementors have some idea of what they actually need to do to be web-compatible in practice.

@DigiTec
Copy link

DigiTec commented May 31, 2016

I'm pretty sure we don't block for in body external stylesheets. At least not that I recall. I could be completely wrong. Let me circle around with some of our older parser folks.

I do know that we increase the progress sink counter. This is used to determine when the load event fires only, but allows everything else to continue. And there were some heuristics added to avoid painting before stylesheets were ready in order to help align us with other browsers. That was done in the original IE code before we forked so it is code we would share.

@pmeenan
Copy link
Contributor Author

pmeenan commented May 31, 2016

@DigiTec I just stood up a quick test page and at least from the outside it looks like the parser is being blocked.

The test page sets a js timer in the head to fire after 1 second then has a 5-second external css in the body (that turns the background black) and is followed by an empty div. When the timer fires the code looks for the div in the DOM to determine if the parser blocked or not.

Chrome/Safari: Page stays completely blank until the CSS finishes loading then displays that the parser was not blocked (while it didn't paint, the div was present in the DOM)

Firefox: Page displays the progress message immediately, one second later displays the message that the parser did not block and 4 seconds later (when the css loads) changes the background to black.

Edge: Page displays the progress message immediately, one second later displays the message that the parser did block and 4 seconds later changes the background to black.

@chrishtr
Copy link
Contributor

Hi, reviving this thread.

The intent to ship referred to above was approved to ship in Chrome 69 (https://groups.google.com/a/chromium.org/d/msg/blink-dev/QC5iefctcag/HZIo-dIZBAAJ).

I have researched the HTML spec w/@pmeenan and identified a proposed set of patches to it to implement the desired behavior indicated in that Intent:

https://github.com/chrishtr/rendering/blob/master/stylesheet-loading-proposal.md

Note that this proposal goes a bit further than the actual intent, which is for the moment limited to only style sheets in the body.

I have also written up documentation for the motivation of this intent, and results of research into
what the various browsers do here:

https://github.com/chrishtr/rendering/blob/master/stylesheet-loading-behavior.md

I think it would be great to standardize the behavior described in the proposal above. I think it is a clear simplification of behavior, enables progressive rendering without flash-of-unstyled-content via some simple best practices, and cleanly integrates with the existing rendering phase of the event loop processing model.

Regarding performance, @pmeenan and I believe it will be performance-neutral because of the presence of a preload scanner, but will be tracking performance metrics carefully during rollout.

@bzbarsky
Copy link
Contributor

believe it will be performance-neutral because of the presence of a preload scanner

Note that there is no preload scanner in the standard, behavior of the preload scanner is not standardized, and may differ wildly between browsers (and does). So unfortunately, extrapolating any performance results that rely on the preload scanner from one browser to others is complicated.

Put another way, you could have a performance regression in Chrome but not other browsers, or vice versa.

@pmeenan
Copy link
Contributor Author

pmeenan commented May 23, 2018

In theory, yes, but any browser that has a preload scanner that is competitive when it comes to blocking script tags should be just as effective for blocking stylesheets. I'm not aware of any modern browser that doesn't have a preload scanner that would mitigate any impacts of blocking the parser for stylesheets.

Maybe it is worth layoung out the specifications for a preload scanner as a mirror of the HTML parser flow though I'm not sure any browser wants the heuristics they use in the scanners to be restricted too much.

@chrishtr
Copy link
Contributor

chrishtr commented May 23, 2018

Note that there is no preload scanner in the standard, behavior of the preload scanner is not standardized,

Right. That is why I am proposing spec edits to explicitly specify a preload scanner.

@bzbarsky
Copy link
Contributor

though I'm not sure any browser wants the heuristics they use in the scanners to be restricted too much

That is true. There's a fine line to walk here to create specs that are useful for new market entrants but don't overconstrain existing browsers.

That is why I am proposing spec edits to explicitly specify a preload scanner.

Great.

@chrishtr
Copy link
Contributor

though I'm not sure any browser wants the heuristics they use in the scanners to be restricted too much

That is true. There's a fine line to walk here to create specs that are useful for new market entrants but don't overconstrain existing browsers.

Agreed. Though we have to think through the possible side-effects of preload scanners, to make sure we preserve enough interop. The Chrome "prerender" system which was recently removed went a lot further than a preload scanner, but was an example of a system that had too many side-effects to be effective.

@zcorpan
Copy link
Member

zcorpan commented Jun 8, 2020

That is why I am proposing spec edits to explicitly specify a preload scanner.

Great.

I can work on such a specification. Is there interest for that for Gecko/WebKit? @hsivonen @othermaciej

@chrishtr can you give an update of what has happened on this front for Chromium since #1349 (comment) ?

I'm aware of https://bugs.chromium.org/p/chromium/issues/detail?id=901056 but I haven't yet reviewed this to be able to summarize how it relates to this issue. cc @richard-townsend-arm

@chrishtr
Copy link
Contributor

chrishtr commented Jun 8, 2020

@chrishtr can you give an update of what has happened on this front for Chromium since #1349 (comment) ?

Here is what has happened since then:

  1. The behavior identified here as "Blink (after launch)" has shipped in Chromium 69.

  2. The behavior identified as "Blink (longer term) (!)" has been implemented and is tracked at crbug.com/891767. We plan to ship it in the near future. @lilles

  3. Various entanglements between the rendering event loop and parsing have been investigated and clarified, in particular relating to the DOMContentLoaded event. Examples include relationship to lazy-loaded images, plugin loading time, and the rendering event loop itself. We plan to ship improvements in this area in the near future.

  4. The code to try to remove the background parser is mostly about cleaning up the code, improving yielding heuristics, and making it faster, but there is a relation to the preload scanner because maybe the architecture would change a bit @mfreed7

@chrishtr
Copy link
Contributor

chrishtr commented Jun 8, 2020

I also updated this page with the latest status.

@zcorpan
Copy link
Member

zcorpan commented Jun 9, 2020

Thank you @chrishtr! Since specifying the speculative parser is only one part of the overall proposal for this issue, I think it's clearer to separate it out to a new issue. I'll file one and reference this issue.

@chrishtr
Copy link
Contributor

@zcorpan can you remind me of the latest status? Does the spec align with the Blink behavior to not start rendering updates until head stylesheets and scripts finish loading, and the <body> open tag has been parsed?

@zcorpan
Copy link
Member

zcorpan commented Sep 26, 2023

@chrishtr yes, it's specified as part of https://html.spec.whatwg.org/#render-blocking-mechanism which was formalized in #7474 (cc @xiaochengh)

@zcorpan
Copy link
Member

zcorpan commented Sep 26, 2023

So maybe we can close this issue?

@pmeenan
Copy link
Contributor Author

pmeenan commented Sep 26, 2023

@chrishtr yes, it's specified as part of https://html.spec.whatwg.org/#render-blocking-mechanism which was formalized in #7474 (cc @xiaochengh)

I'm not sure that covers the in-body case:

A Document document allows adding render-blocking elements if document's content type is "text/html" and the body element of document is null.

I read that as only covering elements in the head of the document (when the body is null) and the overall blocking of all render. It does imply that stylesheets added in the body would not block render.

@pmeenan
Copy link
Contributor Author

pmeenan commented Sep 26, 2023

Or, I guess, the Firefox behavior is what is spec'd and the other browsers are not following the spec.

@chrishtr
Copy link
Contributor

@chrishtr yes, it's specified as part of https://html.spec.whatwg.org/#render-blocking-mechanism which was formalized in #7474 (cc @xiaochengh)

Great thanks! That's just what I needed.

Or, I guess, the Firefox behavior is what is spec'd and the other browsers are not following the spec.

Chromium browsers don't block render on in-body style sheets. They just block the parser. @zcorpan is that in the spec?

@zcorpan
Copy link
Member

zcorpan commented Sep 27, 2023

No, <link> doesn't block the parser in the spec. https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead

I had missed (or maybe forgotten) that Chromium now blocks the parser for in-body stylesheets. @hsivonen @annevk thoughts about this for Gecko and WebKit?

@annevk
Copy link
Member

annevk commented Sep 27, 2023

From what I understand from the above what's "blocked" are the tree mutations following the insertion of the stylesheet. You could still tokenize and prepare changesets. You just can't apply the changesets until after the stylesheet has been fetched and applied?

This seems like something @rniwa and @cdumez need to weigh in on.

@chrishtr
Copy link
Contributor

From what I understand from the above what's "blocked" are the tree mutations following the insertion of the stylesheet. You could still tokenize and prepare changesets. You just can't apply the changesets until after the stylesheet has been fetched and applied?

That's correct. In other words, as far as the web developer or user can tell in terms of visible or API effects, the parser paused. The browser can of course still continue its preload scanner or other pre-work.

@annevk
Copy link
Member

annevk commented Oct 9, 2023

I checked with colleagues and we're okay with experimenting with this, provided it doesn't result in a noticeable performance regression. Perhaps that does mean we need to proceed with the HTML Standard change more slowly, until we have that experience.

@chrishtr
Copy link
Contributor

chrishtr commented Oct 9, 2023

I checked with colleagues and we're okay with experimenting with this, provided it doesn't result in a noticeable performance regression.

Great! Hope your experiment goes well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compat Standard is not web compatible or proprietary feature needs standardizing topic: parser topic: style
Development

No branches or pull requests

8 participants