Re: [RequestAnimationFrame] Processing model does not permit handling animation callbacks for multiple documents at once

On Tue, Dec 6, 2011 at 10:04 PM, Boris Zbarsky <bzbarsky@mit.edu> wrote:

> On 12/7/11 12:56 AM, James Robinson wrote:
>
>> On Tue, Dec 6, 2011 at 8:18 PM, Boris Zbarsky <bzbarsky@mit.edu
>> <mailto:bzbarsky@mit.edu>> wrote:
>>
>>    In at least Gecko's implementation, all documents in a given tab are
>>    treated in an atomic manner for purposes of animation callbacks, in
>>    the sense that all the callbacks from all those lists are placed
>>    into a single list, then all the document-level lists are cleared,
>>    and then the callbacks are processed.
>>
>>
>> In what order are callbacks from different documents processed?
>>
>
> In Gecko?  When the first entry is added to a document's list of animation
> callbacks, that document is itself added to a list of documents that have
> animation callbacks.
>
> Then when it comes time to call the callbacks the lists of animation
> callbacks for those documents are just concatenated, with the ordering on
> lists matching the order of the "list of documents that have animation
> callbacks".
>

How is the concatenation done?  I would think the easiest thing to
implement would be to do something like:

for each document in "list of documents that have animation callbacks"
  append all callbacks within document to global list

which would mean that all entries for a given document would be adjacent in
the final, flattened list.  This agrees with what the spec currently says
and it nearly agrees with what WebKit does today, except that WebKit
assembles this list by documents in document order, not by document in
registration of initial callbacks order.  This would be an observable
different to some scripts so I think we should specify a behavior.


>
> So in other words, there is no particular attempt to impose any sort of
> ordering other than whatever was simple to implement.


I didn't put a lot of effort into this in WebKit either, to be honest.


>
>
>  Et voila.  Since the UA controls exactly when tasks are entered into the
>> "animation task source" task queue, and the UA can decide when to
>> service that task queue relative to other task queues like timers, etc,
>> UAs actually have a lot of freedom here.
>>
>
> So what happens in practice when one of the callbacks does a sync XHR or
> puts up an alert?  Both in terms of what implementations actually do and in
> terms of what the spec currently requires, if anything?


So here it mattes what exactly the synchronous action is.  For
window.alert(), confirm(), prompt() and print() the spec says that the
"pause" algorithm is invoked:
http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#pausewhich
invokes the same "If necessary, update the rendering..." language.
 So a conforming UA could either choose to go ahead and paint at that point
or not.  I believe the WebKit implementation today will not paint in that
instance except for window.print() which is currently implemented in
different ways in different WebKit embedders and probably has bugs in both.

Synchronous XHR simply runs all of the steps of the loading sequence as
part of the callback, so it just looks like a really long-running script.
 In particular there's no opportunity spec-wise to process tasks or to
update the rendering before the sync XHR returns.  I believe that WebKit
matches the spec behavior here and just sits there not painting until the
XHR send() algorithm returns, at least in Chromium.

window.showModalDialog() is full of crazy because it spins the event loop
inside of the task execution. It also marks all browsing contexts except
for the showModalDialog's document's context as background, which has some
implications for task scheduling as I understand it.  I believe a
confirming UA could decide to stop servicing the "animation task source"
task queue completely while a showModalDialog() up, but it will typically
have to paint while the dialog is up so some intermediate state is
necessary.

I haven't put too much thought into this particular case in the spec or in
the WebKit implementation because it's just to full of stupid.  I'd be
happy saying that calling showModalDialog() during a rAF callback throws an
exception and having that be the end of it.  The HTML spec for
showModalDialog lists "running animations" as an example of something that
should continue to occur in backgrounded browsing contexts but I personally
think that's a bad idea.  Implementation wise, we generally have bugs with
showModalDialog() across the board and don't really care all that much
relative to other things.  If we *were* to spend any time on
showModalDialog() I think it should be designing and evangelizing its
replacement.

That's all of the synchronous actions I'm aware of that need consideration.
 Let me know if I've missed any.  Based on these I don't think any spec
text is needed, unless we want to add something about showModalDialog()'s
behavior.


>
>  In practice what WebKit does is prior to painting it iterates through
>> documents in DOM tree order and processes all the callbacks for that
>> document.
>>
>
> OK, so this would correspond, in Gecko's case, to processing all the
> callbacks for one document before moving to the next one in the list
> (instead of concatenating all the lists then processing callbacks).
>

I think it only makes a difference if a document changes its 'fully active'
state, which should prevent any tasks for that document from happening and
thus any callbacks for that document from firing.  I haven't experimented
with this scenario much - I guess you could navigate a document with
pending callbacks synchronously to about:blank from a callback on an
earlier document?


> What does IE do here?


That's a great question.  Could someone from the IE team chime in here?


>
>
>  One consequence of this model is that all callbacks for a given document
>> must be processed as an atomic block - they cannot be interspersed with
>> callbacks from a different document.
>>
>
> This is the case in Gecko too.
>
>
>  This behavior somwhat falls out of the WebKit
>> implementation details "for free" so I didn't put a ton of thought into
>> making this clear in the spec text, although I believe it is there.
>>
>
> Yeah, I believe the spec pretty clearly requires atomic processing for a
> single document (modulo the "blocking call" weirdness, of course).


Do you think this is good or bad?

I see a few possibilities here:

1.) Instead of queuing a task for each document, queue a task that runs
over all documents and does the following:
 a) Create an empty list of <handle, callback> tuples called L
 b) For each document in DOM order
  i) Copy all entries in that document's animation frame request list into L
  ii) Clear that document's animation frame request list
 c) For each entry in L
  i) If that entry's callback's cancelled flag is not set, call the sample
operation of that entry's callback and swallow any exception

2.) Same as (1) except that in step (b) iterate over documents in the order
that the first animation callback was registered in that document instead
of in DOM order

3.) Callbacks are run in the order they are registered, regardless of which
document they are registered in.


I believe that (1) is what I've attempted to implement in WebKit and (2) is
what you are describing in Gecko.  (3) has some advantage of consistency,
but I dislike it as it intermingles callbacks from different documents and
potentially different domains.  I can't convince myself of any great reason
to favor 1 or 2 over each other, and I suspect it won't be meaningful to
too many authors, so let's just pick one.

- James

>
>
> -Boris
>

Received on Wednesday, 7 December 2011 06:57:52 UTC