Open Bug 629366 Opened 13 years ago Updated 2 years ago

web page using TypeKit fonts is displayed before the @font-face rules are processed

Categories

(Core :: Layout: Text and Fonts, defect)

x86
All
defect

Tracking

()

People

(Reporter: jfkthame, Unassigned)

References

(Depends on 1 open bug, )

Details

(See bug 499292 comment 26, 29, 30.)

When a site uses TypeKit to provide downloadable fonts, we reflow and display the page before the @font-face rules (which TypeKit injects via JavaScript, AAUI) are seen and added to the userFontSet. As a result, we display the text using fallbacks before there's been any chance to get the desired fonts, and a @font-face fallback delay as per bug 499292 doesn't help. If anything, it makes the user experience worse because the text initially gets displayed using system fonts; then we see the @font-face rules and so we hide the fallback text; and then the text reappears once the fonts are downloaded (or once the fallback timeout expires).

It's not yet clear to me whether this is something we should/can address in Gecko, or if it is really a flaw in how TypeKit serves the resources - in which case it should go over to Evangelism.
The way we handle FOUC is that parser-inserted stylesheets prevent layout start until after the sheet is loaded.

Typekit inserts its sheets using createElement and DOM operations, from what I can tell from their minified code.  As a result, we don't block layout start on those sheets, and as soon as we see <body> we start layout.

If typekit used document.write(), things would work better, but then there would be limitations on when the relevant typekit api can be called.
Is it known if TypeKit is deliberately using this technique in order to show the fallback font while the TypeKit fonts are downloading?

Note that while in the JavaScriptless case Gecko shows the fallback font relatively soon, WebKit doesn't paint text until the fonts have downloaded. See https://bugs.webkit.org/show_bug.cgi?id=25207 I think this is a pretty serious usability issue in WebKit and one I think we shouldn't clone. It could be that TypeKit is trying to work around the WebKit problem.

I think we shouldn't do anything in particular here, except maybe standardize a way to listen for font readiness events. In the absence of such events, TypeKit could use the same technique the Google Font API uses for polling if the fonts have downloaded. (I gather the Google Font API measures the dimensions of a piece of text repeatedly and when the dimensions have changed, it concludes the font has downloaded.)
(In reply to comment #2)
> Is it known if TypeKit is deliberately using this technique in order to show
> the fallback font while the TypeKit fonts are downloading?

Not specifically known, but I doubt it - especially as it does _not_ achieve that in webkit, which would be the place it's currently relevant.

> Note that while in the JavaScriptless case Gecko shows the fallback font
> relatively soon, WebKit doesn't paint text until the fonts have downloaded. See
> https://bugs.webkit.org/show_bug.cgi?id=25207 I think this is a pretty serious
> usability issue in WebKit and one I think we shouldn't clone.

Agreed, and there's no intention to do so. Bug 499292 provides a (configurable) timer so that we go ahead and show a fallback if the downloaded font does not become available quickly enough.
Hi Mozilla folks. I'm one of the Typekit developers. Paul Irish pointed me to this thread, so I thought I'd chime in.

First, a little background:
We do indeed use createElement and DOM operations today to insert the link element for the CSS (with @font-face) into the head. We do this as soon as the Typekit.load function is executed. We recommend that people put this in the head of their page so the font downloads are kicked off as quickly as possible.

If I understand correctly, what happens in Firefox is that the browser downloads and executes the Typekit JS, then continues to parse and render the page while the Typekit CSS is downloaded. However, before the CSS is downloaded, Firefox has already done an initial layout/paint (before it even had a chance to notice the @font-face declarations in the Typekit CSS), so you still see a flash of unstyled text. Am I understanding that correctly?

It's possible that we could modify the Typekit JS to use document.write when Typekit.load is called from the head, and use the existing DOM operations otherwise. We could differentiate between the two cases by checking document.getElementsByTagName('body').length.

I'm curious, though, why this isn't a problem with Typekit's JS today in Webkit-based browsers. They've implemented similar functionality that hides @font-face content until the fonts have been downloaded, and there's no flash of unstyled content, even though we're adding the CSS with DOM operations. Does that mean they are blocking initial layout/paint on CSS added with DOM operations as well as document.write?
> Am I understanding that correctly?

Yes.

> why this isn't a problem with Typekit's JS today in Webkit-based browsers

They might kick off their font loads differently.  They might implement their avoidance of a flash of unstyled content (which is what you're really getting here) differently.  FOUC avoidance is handled pretty differently in different browsers, in general.... and how it's handled has been changing, iirc, both in Gecko and Webkit.

On out end, we could try to extend the layout-blocking (but not parse blocking, which was the sticking point before which led us to only consider parser-inserted scripts, nor script-execution blocking) to all loading non-alternate sheets.  Henri, have you looked at this section of HTML5 recently?  Does it say anything useful?
It seems reasonable that if CSS is in the head before the browser finishes parsing the head, then it should block layout (but not parsing), regardless of how it got there. It doesn't seem like it should matter whether document.write or DOM operations were used to get it there, as long as it's there before the browser begins parsing the body.

Code like this (which is what we would probably end up doing to work around this) seems a little hacky:

if (document.getElementsByTagName('body').length < 1) {
  document.write('<link ...');
} else {
  document.getElementsByTagName('head').appendChild(linkTag);
}
FYI, test builds with the delayed-font-fallback patch from bug 499292 are available at http://ftp.mozilla.org/pub/mozilla.org/firefox/tryserver-builds/jkew@mozilla.com-fa266067ca79 (Windows and Linux) and http://ftp.mozilla.org/pub/mozilla.org/firefox/tryserver-builds/jkew@mozilla.com-426204f85250/try-osx64/ (Mac).

To adjust the length of delay after we kick off the @font-face load and before we show fallback text anyway (because the font is taking too long to arrive), go to about:config and modify the setting gfx.downloadable_fonts.fallback_delay.
I filed bug 631547.
Depends on: 631547
Boris, this is great. Thanks so much for looking into this. I really feel that it's a better way to solve this issue in a general way.

I subscribed to the other bug too so I'll get notified of updates.
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.