Re: [css-fonts] Proposal for standardizing font timeout behavior

Ilya Grigorik wrote:

> > As defined, the 'optional' keyword would always be effectively
> > determined by whether a font had been loaded by a previous page,
> > since layout initiates font loads and paint follows shortly after
> > layout. So 'optional' in practice would be equivalent to "use the
> > the font if it's already been cached".
> >
> 
> The fact that a resource may be in a local cache does not mean it is
> instantly available: you need to hit your spinning rust / flash, and you
> (likely) need to hop between I/O and render threads. We have telemetry data
> showing that even with flash, some disk accesses are in high double and
> even triple digits of milliseconds, and on top of that you have thread
> hops, which can also add non trivial amounts of overhead (although, often
> less due to hops and instead due to contention between main thread being
> busy and events accumulating in the queue).
> 
> Cache lookups are not free or instant, and from first hand experience in
> Chrome, we can't count on font being available in time just because its
> "cached"... There are cases where network fetches are faster than cache
> lookups -- crazy, I know.

With 'optional', the font generally won't be available the first
time it's referenced, it'll only be *possibly* available on
subsequent pages, subject to all the lovely browser internals you
describe.

> > The Font Loading API gives authors complete control over when fonts
> > are loaded, precisely when they are used, and how the "swap" occurs.
> 
> Loads are not instant. Yes, we can kick off a .load() with Font Events API,
> but that doesn't mean it'll complete in time for layout+paint.

The Font ****Loading**** API allows you to kick off the load. Fonts
can be loaded without being added to the FontFaceSet, so they will
be unavailable to layout. Authors can hide elements until a font
is available. On a timer, script can determine whether the font is
loaded or not and choose to use the fallback font at that point. Or
it can wait until the font is loaded and decide the most effective
way to handle the transition to the newly loaded font.

Fonts are needed for layout, so there aren't any separate decisions
to be made at paint time.

> However, even with the JS event, defining this behavior would still require
> quite a bit of JS work on behalf of developer. Personally, I'd still like
> to see this as a CSS property. Text rendering (aka, communicating actual
> information on the page) is the bread and butter of the web... It would
> sure be nice to make delivering a performant text rendering experience
> simple.

Just to be clear, what's been proposed doesn't actually affect text
rendering *performance* in any way. It's simply trying to ameliorate
the effects of poor load performance for fonts on the dynamic layout
of the page.

> > That said, I think trying to spec behavior on "first layout" is
> > really tricky since the precise timing of this depends on the
> > incremental layout algorithm used by a browser and that's going to
> > vary across user agents/devices.
> 
> I don't think variability is the problem... I'd kick off my font request as
> soon as possible, and then run a "is font ready" check in each element
> layout callback to see if I can/should apply it or not.

The variability of incremental layout algorithms will certainly be a
problem for authors using this property, since user agents that
start laying out a page earlier will be much more likely to hit the
key "first layout" point before a font is loaded.

> The behavior we're proposing here is not tweaking fetch parameters
> in any way. To deliver the optimal experience you'd use this
> alongside Font Events API to kick off an early fetch (from network
> or cache), and then use these CSS properties to control behavior
> of text rendering at first layout+paint of each element based on
> state of the font request.
> 
> - Font Events API allows me to start the font request sooner
> - Browser is now racing font request and layout+paint work
> - Browser gets to layout+paint and uses font-rendering to determine which
> text rendering strategy it should apply
>  -- if request has finished, it applies the font and moves on
>  -- if request is still in flight, it applies specified strategy

I should point out here that the ability to set timeout-sensitive
behavior on elements is fraught with problems. Consider the example
of a large document (think HTML5 spec) that uses a downloadable font
for headings:

  @font-face { font-family: myfont; src: url(myfont.woff); }

  h4 { font-family: myfont, fallback-serif; font-rendering: swap 3s; }

The user agent starts the font load and lays out the first section
of the document. Headings appear in the fallback font. Because it's
a large document the font load takes more than 3 seconds, so
headings in the first section remain in the fallback font. But as
later sections are laid out, the font *is* available so in those
sections headings appear in the downloadable font.  But scroll back
up and the headings remain in the fallback font.

>From an implementation viewpoint, you'd need to track this "timed
out" state for those elements to prevent them switching to the
fallback font. So when running font matching on reflows you'd need
to keep track of font availability from the first layout. Ick. This
doesn't sound like a way of providing performant text rendering.

> > Ilya, you need to define what the default behavior is. If you don't
> > want a single, standard behavior, then an 'auto' value is needed
> > here, which would allow user agents to vary.
> 
> Personally, I'm ok with allowing UAs to customize own defaults. As Tab
> noted:
> 
>   FF and Chrome/Opera currently have a default of "mandatory 3s".  IE
>   has a default of "mandatory 0s".  Safari has a default of "mandatory
>   <infinite-time-value>".

CSS properties have an initial value which is the same across
browsers. The descriptors of the @font-face value have default
values which are the same across browsers. So you need to define an
initial value of the 'font-rendering' property and a default value
for the 'font-rendering' descriptor within @font-face rules. In
fact, to implement what you've described you need slightly different
values for the property, since I think you want the default value
for the descriptor to be "whatever the user agent timeout policy is"
and for the *property* you want an initial value of 'auto' to mean
"decide based on the 'font-rendering' descriptor value of the font".

But this is just CSS mechanics, the important thing to decide here
is whether you're proposing a single browser default timeout
behavior or not. I actually think requiring some form of timeout
would be a good idea. I'm guessing the 'mandatory <infinite>' for
Safari is just a bug not really a feature.

I think the additions proposed don't really address the underlying
problem (font loads happening late or taking too long) and have some
unfortunate side effects. I think the Font Loading API provides a
lot of ways authors can address problems associated with slow font
loading. I'm not really convinced that twiddling timeout behavior
via CSS is the right solution here.

Cheers,

John Daggett
Mozilla Japan

Received on Thursday, 30 October 2014 08:44:22 UTC