W3C home > Mailing lists > Public > www-style@w3.org > September 2012

Re: [css3-fonts] rethinking font load events

From: John Daggett <jdaggett@mozilla.com>
Date: Thu, 6 Sep 2012 19:45:55 -0700 (PDT)
To: "Tab Atkins Jr." <jackalmage@gmail.com>
Cc: www-style list <www-style@w3.org>
Message-ID: <846482069.5572521.1346985955377.JavaMail.root@mozilla.com>
Tab Atkins wrote:

> > Rethinking the font load event that Tab proposed, I think it would
> > be better to create a separate event target for font loads. 
> > Typical patterns for load events in other Web API's rely on there
> > being a single target for one resource, typically an element.
> 
> I'm fine with this.  In particular, it prevents me from having to
> rename the events to something more unique, since it turns out that
> all the events that can hit a particular target must have unique
> names across all specs.
> 
> (Btw, sorry for not posting up my feedback on my own proposal, like I
> said I would. ;_;)

No worries.

> >   // -- fires when readyState changes
> >   [TreatNonCallableAsNull] attribute Function? onreadystatechange;
> 
> I don't think this is necessary.  There's only two states.  Changing
> from "loading" to "idle" is handled by the below functions, and
> changing from "idle" to "loading" could probably use a better name.

I was mimicing what other webapps API's do.  But simply having a pair of events
for the two state changes would be fine too.

> Note that we have a 'callback' type in WebIDL now, which all of
> these should use.  That way you can define the arguments passed to
> the callback in IDL rather than prose.

I haven't seen this used.  Can you point at examples of this in a spec?

> >   // -- fires when all font loads have completed or failed
> >   [TreatNonCallableAsNull] attribute Function? onallcomplete;
> 
> In discussion, my coworkers and I thought the "all" name would be
> somewhat confusing.  It's all the fonts *that the page has so far
> requested*, which means that the "allcomplete" event can fire
> multiple times in a page's lifecycle.  I'm not sure what a better
> name is, though.
> 
> How about just onidle?  You can complement that with onloading for
> the opposite case.

Maybe "loading" and "doneloading" would be better?  Other instances
use "loadend" but that's for a single resource and I think we need to
distinguish this from that case, it's only firing after the last load
completes.

> >   // -- fires when a single font load completes
> >   [TreatNonCallableAsNull] attribute Function? onload;
> >
> >   // -- fires when a single font load fails
> >   [TreatNonCallableAsNull] attribute Function? onerror;
> 
> I don't think I like errors to trigger a separate event.  I used a
> "usedSrc" attribute in the load event to indicate which source was
> chosen, which was nulled in case of error.

Every other event target definition I looked has some way of
explicitly identifying errors, either with an explicit readyState
state for errors or an explicit "error" event (e.g. XMLHttpRequest
[1], FileAPI [2]).

Reflecting errors via a 'userSrc' parameter seems like a really bad design.

> >   // async load
> >   void loadFont(DOMString font, optional DOMString text);
> 
> What do the arguments mean here?  It looks like, from your example,
> the 'font' argument is meant to be a 'font' property declaration.  I
> presume the 'text' argument, if supplied, specifies characters that
> must be covered by the loaded fonts (determined through
> unicode-range).

Exactly, using 'text' gives finer grained control over which font
faces are loaded if unicode-range descriptors are present

> >   // notify completion, even if no fonts load
> >   void notifyAfterCompletion();
> 
> I'm fine with things like this (simplifies interaction/API usually),
> but this is the reason promises are often better than events - you
> could just hinge some code on the allloaded promise and it'll either
> run immediately (well, in the next event loop cycle) or be held until
> the promise actually fulfills.  I wish tc39 would hurry up and decide
> which flavor of promises they want to bless. :/

Sorry, not clear what you're talking about here, some sort of
reference would help.  

If there's a simpler way to do this without explicit events, I'm all
for it. The basic problem is that operations that can't occur until
font loads (if any) complete need to by synched with layout
operations, specifically the layout operations that determine whether
fonts need to be downloaded or not.  That's why your original proposal
for just a simple load event doesn't work, because you can't identify
that point.

> > The 'readyState' attribute switches between "idle" and "loading"
> > depending upon whether one or more fonts were loading at the time.
> 
> When is readyState changed, relative to the other events?  If you
> look at it in a load or allcomplete event, which state should it
> reflect?

Right, we'd need to clarify this explicitly.  If a page loads three
fonts, I think you should get this sequence:

  "readystatechange" ==> idle -> loading
  "load" ==> font1 completes
  "load" ==> font2 completes
  "load" ==> font3 completes
  "readystatechange" ==> loading -> idle
  "allcomplete"

> > The "load" and "error" events would fire the event below:
> >
> > [Constructor]
> > interface CSSFontFaceLoadEvent : EventTarget {
> >   readonly attribute CSSFontFaceRule fontface;
> >   readonly attribute DOMString error;
> > }
> >
> > Bubbles: only in my champagne darling
> > Cancelable: no
> 
> Inheriting from Event, presumably. ^_^

Er, right, oops.

> Is that supposed to be the CSSFontFaceRule that actually kicked off
> the load?  Note that you can't expose that cross-origin.  This also
> means that to identify the font, you have to write
> "event.fontface.style.foo".  This is why I flattened the @font-face
> descriptors onto the event object - much shorter, and no
> cross-origin issues.

I think it would be easier to use a readonly copy of the
CSSFontFaceRule, that way you don't have the problem of having to keep
two structs in sync. The cross-origin issues seem larger than just the
contents of the struct, you're effectively leaking information about
those via the event.  Somebody should care, not sure I do...

> This also lacks the ability to tell which source was chosen in the
> load, which seemed useful in discussion with coworkers.  My proposal
> had a 'usedSrc' attribute for that.

I don't see a real use case for 'usedSrc' at all.  It's not going to
tell you which piece of text uses which font, so what information does
it provide?  The debugger use case seems out of scope to me, a
debugger needs other ways of inspecting content at a deeper level
beyond what the OM can provide, just lower-level ways of accessing
font matching information is needed to determine which font was used
for a given text run.

In general, I'm still very skeptical that the per-font load events are
needed at all.  I don't quite see what they give you unless there are
other API's around that provide lower-level access to font matching
and text shaping info.  I think it's a useful exercise to sketch out how
per-font load events would work but by themselves I think it would be
better to omit them from the first round version.  Web apps that want
to do full-on text layout are going to need a much richer set of API's
to do this sort of thing but I think the number of teams capable of
doing this one can probably count on your hands (with maybe a toe or
two).  Let's do the mainstream use case first and focus on the high-end
use cases later.

Regards,

John

[1] XMLHttpRequest
http://www.w3.org/TR/XMLHttpRequest/#interface-xmlhttprequest

[2] FileReader
http://www.w3.org/TR/FileAPI/#FileReader-interface
Received on Friday, 7 September 2012 02:46:23 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:59 GMT