whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
7.98k stars 2.6k forks source link

Define parser behavior for in-body external stylesheets #1349

Open pmeenan opened 8 years ago

pmeenan commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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."

zcorpan commented 8 years ago

Chromium is considering changing to be like Edge, though: https://groups.google.com/a/chromium.org/forum/#!searchin/blink-dev/css$20block/blink-dev/ZAPP8aTnyn0/JURBX5uFCAAJ

foolip commented 8 years ago

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

foolip commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 4 years ago

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 https://github.com/whatwg/html/issues/1349#issuecomment-391475650 ?

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 commented 4 years ago

@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 commented 4 years ago

I also updated this page with the latest status.

zcorpan commented 4 years ago

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 commented 1 year ago

@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 commented 11 months ago

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

zcorpan commented 11 months ago

So maybe we can close this issue?

pmeenan commented 11 months ago

@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 commented 11 months ago

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

chrishtr commented 11 months ago

@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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

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.