Surfin' Safari

The FOUC Problem

Posted by Dave Hyatt on Friday, September 1st, 2006 at 10:59 pm

FOUC stands for Flash of Unstyled Content. This situation occurs whenever a Web browser ends up showing your Web page’s content without having any style information yet. It’s an interesting technical problem, because when/how a browser ends up committing the crime of FOUCing depends heavily on how the browser’s engine is architected and on interesting assumptions made by Web site authors when designing their sites.

When a browser loads a Web page, it first fetches an HTML file from the Web site. As that HTML file is downloaded from the Web site, the browser engine begins to feed the file’s contents to its HTML parser. The browser engine builds up a tree that represents the structure of the Web page (the DOM tree) as it encounters elements and nodes in the file. At some point while parsing, the engine is going to hit a stylesheet loading directive, either inside a <style> block or in a <link> element.

It is at this point that things get interesting. The browser engine now has a choice to make. It can either stall the parsing process while it waits for the remote stylesheet to load, or it can plunge bravely ahead. Let’s examine each option.

Stall

If the browser engine stalls parsing then the processing of the entire page is now held up. Although the network layer (typically on another thread) can still provide data to the engine, this data basically queues up unprocessed. In effect the entire engine is doing nothing while it waits for this stylesheet to finish. Gecko has this behavior.

One major disadvantage of this approach is that stylesheets and scripts cannot load in parallel. If a Web page has a link element followed by a script element, the engine won’t even begin loading the script element until it has completely loaded the stylesheet. The situation gets even worse if the stylesheet itself wants to import other stylesheets, since the engine will continue to stall until the entire stylesheet tree represented by the directive in the HTML file has loaded.

This choice effectively makes stylesheets behave the same as scripts and effectively guarantees that no load parallelization will occur on anything in the head of the Web page. This sounds bad, so why do it? Well, the fundamental reason is that Web authors have essentially already written code that makes the assumption that style information accessed via scripts will be current.

For example, consider a script following a link element. If that script asks for the offsetHeight of an element, and the height was specified in the CSS file, Web page authors expect to get the right height. Even though this behavior is not part of any standard, Web designers have implicitly built the serial loading assumption into their scripts.

How did this happen? Well, primarily because existing engines behaved this way, but also because of the synchronous nature of script elements. Because scripts already stall parsing, authors just made the natural assumption that stylesheets would stall parsing too.

Verdict: Thumbs down.

Don’t Stall

WebKit has the opposite behavior and will continue parsing a page even after a stylesheet directive has been encountered so that stylesheet and script loads can be parallelized. That way everything can be ready for display much earlier.

The problem with this behavior is what to do when a script attempts to access a property that involves having accurate layout/style information to answer. Shipping Safari’s current behavior when this happens is as follows: it will go ahead and lay out what it’s got even though it doesn’t have the stylesheet yet. It will also display it. This means you see FOUC whenever a script tries to access properties like scrollHeight or offsetWidth before the stylesheet has loaded.

We’ve improved things in the WebKit nightlies a bit so that at least the rendering of unstyled content will be suppressed. However we will still give back information that the page might consider to be incorrect, since the stylesheet that supplies the up-to-date information hasn’t loaded yet.

This approach ends up being superior in speed to the stalling approach in the case where no style/layout properties are accessed. However it ends up possibly being inferior in speed to the stalling approach if such a property is accessed, depending on how severe the contortions the engine has to perform between the unstyled rendering and the final styled rendering are.

Verdict: Thumbs down.

On-Demand Stall

The ideal technical solution to this problem involves a mixture of the two approaches, and that is on-demand stalling. In this approach the engine still parallelizes loads and doesn’t stall parsing. However if a layout or style property is accessed, the JavaScript engine suspends itself and awaits the load of any pending stylesheets. Once all the stylesheets have loaded, the script picks up where it left off.

The hurdle with this approach is that you have to be able to halt scripts mid-execution and resume them later. A technically inferior solution might be to just stall the execution of *all* scripts, but given the preponderance of script elements on most Web sites, that approach is really not much better than stalling all the time.

Verdict: Thumbs up.

Why does this matter?

The FOUC problem would normally be a minor occurrence. However with the advent of Google AdSense, FOUC has become a Safari epidemic. Because these Google ads not only execute inline script but access layout information that they often don’t even end up using in the page, the problem of FOUC is much more severe than it should be.

It is poor coding to access style/layout information repeatedly during the loading cycle of a page, because in order for the engine to supply you with an answer, it has to ensure that its layout and style are up to date. Think of every use of a property like offsetWidth as a bottleneck that stalls Web page display while the engine computes an accurate snapshot of the entire page just to give the script a correct answer. If a script accesses such properties repeatedly while making numerous other layout/style changes between accesses, it can cripple the load speed of a Web site in every modern browser.

31 Responses to “The FOUC Problem”

  1. Pingback from qooxdoo » Blog » FOUC - Other jobs are complicated, too:

    [...] d the well known "Flash of Unstyled Content" (FOUC) problem. Thank you David for this insight. No Comments No comme [...]

  2. gazhay Says:

    Developers can play their part in improving this situation.
    Though not solving the problem, creating an opaque white div which is fixed to the page size, you can alowed content to load behind, and upon completion, hide the div and reveal page content.

    While not ideal, it prevents the unsightly part of this problem, and you can always add a progress indicator.

  3. Pingback from FOUC Problem at Sumeet Singh:

    [...] y your browser sometimes pumps out the webpage without the css or any style applied to it? Safari Blog has an explanation on FOUC (Flash of Unstyled Content). This situation occurs whenever a Web browser end [...]

  4. Pingback from FOUC « g.wp.com:

    [...] d. FOUC David Hyatt has written a far better description of the FOUC problem than I ever could… I suck at explaining things… About this entry Y [...]

  5. pgib Says:

    I prefer to do any Javascript inside the onload handler for the window. This way, I know everything is complete before my script executes. I have found that not doing this results in frequent errors when manipulating the DOM tree, particularly in Internet Exploder.

  6. Pingback from Michael Tsai - Blog - Flash of Unstyled Content:

    [...] tent Saturday, September 2, 2006 Flash of Unstyled Content
    Dave Hyatt:

    The ideal technical solution to this problem involves a mixture of the two approaches, and that i [...]

  7. heckenpenner_rot Says:

    Can you go into more detail what’s complicated about implementing the “on-demand stall” approach? Do you guys have to explicitly save the state of the script execution, cooperatively transfer control to the style loading, before afterwards restore the script state and resume with the script? Isn’t it just a matter of using the right concurrency data structures that block the JavaScript thread when accessing style information until the loading thread delivers the information?

  8. macnoid Says:

    Communicate the problem to the user.
    For a long time there has been no feedback that scripts are running (Javascript, Jscript, ActiveX or whatever). Users have sat through an app that feels like it’s not responding all because someone’s Javascript is problematic, running amok, or hogging resources. We have a “thermometer” that shows lack of server responsiveness (slow page loading) but no one has ever implemented any sort of indicator about browser side script execution. It’s a hard problem, but that may just be because no one’s ever tried it before. My eyes are now trained to check a slow page to see if it’s loaded, why does script performance have to remain hidden from the user? It might also help security.

    Stop the Jumps
    One thing that makes people nervous isn’t that the page isn’t rendering properly, it’s that it jumps from an ugly but somewhat comprehensible state to a beutiful but unexpected state. CSS isn’t the problem. Beautiful page design isn’t the problem. It’s the jump. Start the page in some sort of default, low contrast look (small dark grey text on light grey background?) then when style sheets arrive quickly animate the transition to grow, move, or tweak the start template into the designed page.

    While We’re At It
    Let users have a peek at the stage manager’s job. The browser knows it has to load all of these pieces and parts (images, style sheets, external objects, scripts, and so on). Have a categorized list that let’s people know when the actors have actually shown up to go onstage. Did some drunk web designer forget the name of their own style sheet so it won’t ever be called to go on stage? The list might show that it’s really the web browser’s problem. The “drawer” of content would only show during the rendering of the page but slide closed when all of the parts showed up (or when the user manually closed it).

  9. hyatt Says:

    “Do you guys have to explicitly save the state of the script execution, cooperatively transfer control to the style loading, before afterwards restore the script state and resume with the script?”

    Yeah, that’s it. You just have to be able to save off enough of the current script’s execution state such that it can be resumed again later. That involves having a JS engine structured in such a way that it can pause like this.

  10. anonymouscontrib Says:

    Fourth option:

    Aggressively cache externally-referenced style sheets — even beyond
    their indicated expiry.

    Speculatively proceed to render HTML with the cached style data. MD5 sum
    new style data as it arrives. When it has fully arrived, compare its
    MD5 sum against that of the cached data the page was / is being
    rendered with. If the two differ, rerender the page as required.

    The performace of this method, amortized over many page loads,
    increases as the probability that a style sheet has changed decreases.
    Since I suspect style sheets very rarely change, the performance of
    this method ought to be quite good..

    I’m not really familiar with the HTML rendering space; my apologies
    if I’m way off the mark here..

  11. hyatt Says:

    Hey, new term. :) That approach would cause FOOC (flash of obsolete content) if the stylesheets didn’t match, and it has the same “wrong info” problem of approach #2 (e.g., scripts would get the wrong style information).

  12. Chris Says:

    hyatt: I’m not sure you’ve fully answered heckenpenner_rot’s question, or at least I don’t understand the fullness. What’s the difference between this and simply blocking the current thread in any other language? Surely the JS engine must be able to block, because there are cases where it must do so or waste CPU time.

    I mean, isn’t it just a matter of modifying the hooks that read the properties in question to block until the appropriate CSS is deemed available, or is there some deeper issue? (Or is that itself a deeper issue than it appears on the surface, presumably due to some complexity of interaction between the rendering bits and the JS engine?) I mean, on the surface, it sounds like we’re talking about a single mutex. But you guys aren’t dumb.

    I won’t be surprised if the answer is ‘yes’ and ‘go look at the JS engine source code to see for yourself’, of course. :)

  13. heckenpenner_rot Says:

    “Yeah, that’s it. You just have to be able to save off enough of the current script’s execution state such that it can be resumed again later. That involves having a JS engine structured in such a way that it can pause like this.”

    How is the whole process of loading a page handled internally? Is there just a single thread that does both the parsing and also the execution of the JS scripts tags? And in this thread, you then manually alternate between the parsing and the JS engine?

    It seems to me that, if you just use a single thread, you will now have to manually implement some sort of ‘lightweight cooperative multischeduling scheme’ to handle the new approach. And that indeed seems complicated. So why not just have two threads—one for the parsing and another one for the JS engine—that synchronize accordingly?

    Or do I oversimplify too much? Or are you guys already planning for a future integration of first-class continuations into JavaScript? :)

  14. hyatt Says:

    “Surely the JS engine must be able to block, because there are cases where it must do so or waste CPU time.”

    There’s a difference between blocking by stalling an entire thread and saving off state, yielding and resuming later. If we lived in a world where every page had its own thread that was also independent of the UI thread, you could imagine that it might be ok to just block a “page thread”. We don’t though. (The reality is that pages are typically network-bound enough that there isn’t much benefit to dramatically complicating the code just for a bit of parallelization that would only be useful in very limited circumstances.)

    In WebKit (and Gecko) all pages parse and execute script on the main thread (the UI of the browser renders and responds to events on this thread as well). Thus blocking the thread that JS is running on blocks everything except network loading.

    “Is there just a single thread that does both the parsing and also the execution of the JS scripts tags? And in this thread, you then manually alternate between the parsing and the JS engine?”

    Correct, one thread. There isn’t really “alternation” though. You just run scripts as they are encountered (and loaded) in the page. Scripts block parsing. Even if you did suspend JS, you couldn’t keep parsing that particular page. Therefore there is no benefit to separating into multiple threads based on script execution vs. page parsing.

  15. Pingback from links for 2006-09-03 [MacStansbury]:

    [...] links for 2006-09-03

    The FOUC Problem I’ve noticed this getting worse with all the widgets on webpages now. It seriously FOUCs AdSe [...]

  16. gazhay Says:

    @macnold.

    A developer can easily display a progress bar of his own, if he so wishes, and I always try to have some kind of progress indicator on tasks that are going to take a while.

    I agree this is not a catch-all solution, but this problem in general is down to lazy web developers, there is so much you can do to stop this problem, but people just seem to code for ‘fairly good’ results in IE nowadays.

  17. Pingback from The Infotainment Telesector / Archive / Four on the Floor:

    [...] 3 September 2006 Here’s a fascinating article on the Flash Of Unstyled Content problem by Dave Hyatt: It is poor coding to access style/layout information repeatedly d [...]

  18. Chris Says:

    ‘If we lived in a world where every page had its own thread that was also independent of the UI thread, you could imagine that it might be ok to just block a “page thread”.’

    Got it; this is not BeOS; gotta remember that. :) Makes sense.

  19. boult Says:

    Hyatt,
    this is a off topic… just wanting to let you know that this nightly webkit app spawn a new page when a link is set to open new page but it load in same window at the same time spawn a new blank page. hmm..

    is it a bug or feature… are you guys aware of this? (I am not programmer but I am just trying this webkit and it seems to run better than the stable Safari that came out in latest 10.4.x release… which I am glad it was able to load all animations live.)

  20. jburke Says:

    Does the FOUC problem show up if we check for document.readyState to be loaded/completed before accessing the style properties in JavaScript? This check is used as part of a window.onload alternative that does not need to wait for things like images and iframes to load. But would it cause FOUC? For more info, see Dean Edward’s post: http://dean.edwards.name/weblog/2006/06/again/

  21. Pingback from » Dave Hyatt on the FOUC Problem Nate Koechley’s Blog:

    [...] pts, or bottleneck everything until correct values are illuminated? Here’s the post: The FOUC Problem. Here’s another classic from him: Testing Page Load Speed. Tags: browsers, Engineering, [...]

  22. heckenpenner_rot Says:

    “In WebKit (and Gecko) all pages parse and execute script on the main thread (the UI of the browser renders and responds to events on this thread as well). Thus blocking the thread that JS is running on blocks everything except network loading.”

    Oh right, I wasn’t really aware of that; I mean, of the fact, that everything just runs in a single thread, namely the UI thread.

    In this case, I am curious on your opinion whether this model is still viable for the long run? In particular, if we expect even more scripting stuff incorporated into future web pages. People are already doing quite complicated computations on the client side. Stuff like parsing JSON strings, calculating spreadsheets, etc is already
    there and takes some time. And we can certainly expect a further shift of computations to the client side, I’d guess. I already see my rainbow spinning … :)

    Are there any long term plans to change that model in WebKit? If so, what’s the way to go? Native “page threads”? A rewritten JS engine with explicit control state? Or both?

  23. Pingback from Red Sweater Links » Blog Archive » FOUC Off:

    [...] rigued by the conclusion that the problem is exacerbated by the prevalence of Google ads! Link. This link was tagged Geeky, Web, Apple. [...]

  24. Pingback from alexking.org: Blog > Around the web:

    [...] e same way about T-Mobile: but if their service is bad, what use are they, even if cheap? The FOUC Problem – I avoid this by doing these kind of script calls “onload”. Who Writes [...]

  25. xenon Says:

    @boult, that is a recent regression that has now been fixed in the latest nightlies.

  26. Pingback from Emma Snowdon - Jewellery Website, Please review.:

    [...] tyled Content or FOUC and there are workarounds for it. Flash of Unstyled Content (FOUC) Surfin’ Safari – Blog Archive The FOUC Problem

    [...]

  27. Trackback from humorlessbitch:

    Dave. Dave. What Are You Doing, Dave

    Uh, Dave? Whatever you wanna call it, and however interesting, the question of what to render first and why—I was wondering if we could just fucking skip it and let Safari be the Bitch Queen she used to be?

    ‘k?

  28. hyatt Says:

    humorless, not sure why you can’t upload files. Do you have any Safari extensions installed? You might try uninstalling them if so.

  29. cian Says:

    The Nokia E61 (which runs WebKit) does a lot of FOUCing actually, I wonder if this will be fixed with a nokia upgrade…

  30. Pingback from humorlessbitch.com » Dave. Dave. What Are You Doing, Dave:

    [...] ptember 14, 2006 at 11:47 pm
    · Filed under the handbasket

    Uh, Dave? Whatever you wanna call it, and however interesting, the question of what t [...]

  31. JonathanAquino Says:

    I think I’ve found a simple workaround: first CSS file just hides the body; last CSS file just shows the body. More info at http://jonaquino.blogspot.com/2007/02/workaround-for-safari-fouc-bug.html