W3C home > Mailing lists > Public > public-webapps@w3.org > July to September 2011

Re: DOM Mutation Events Replacement: When to deliver mutations

From: Rafael Weinstein <rafaelw@google.com>
Date: Thu, 11 Aug 2011 08:13:47 -0700
Message-ID: <CABMdHiT=A_zJKK8PLcG1fF=ACdrpTvGvADbAHbzC36AfNu57FA@mail.gmail.com>
To: olli@pettay.fi
Cc: Webapps WG <public-webapps@w3.org>
Thanks Olli. I think this is now a fairly complete summary of the
issues identified thus far.

It'd be great to get some additional views -- in particular from folks
representing UAs that haven't yet registered any observations or
opinons.

Note: I think what Olli has listed is fair, but I'm concerned that
because of terminology ("consistent" vs "inconsistent" semantics),
others may be confused. I'm going to clarify a bit. I believe my
comments should be uncontroversial. Olli (or anyone else), please
correct me if this isn't so.

On Thu, Aug 11, 2011 at 2:02 AM, Olli Pettay <Olli.Pettay@helsinki.fi> wrote:
> On 08/11/2011 03:44 AM, Rafael Weinstein wrote:
>>
>> Although everyone seems to agree that mutations should be delivered
>> after the DOM operations which generated them complete, the question
>> remains:
>>
>>   When, exactly, should mutations be delivered?
>>
>> The four options I'm aware of are:
>>
>> 1) Immediately - i.e. while the operation is underway. [Note: This is
>> how current DOM Mutation events work].
>>
>> 2) Upon completion of the "outer-most" DOM operation. i.e. Immediately
>> before a the lowest-on-the-stack DOM operation returns, but after it
>> has done all of its work.
>>
>> 3) At the end of the current Task. i.e. immediately before the UA is
>> about to fetch a new Task to run.
>>
>> 4) Scheduled as a future Task. i.e. fully async.
>>
>> -------
>>
>> Discussion:
>>
>> Options 1&  4 are don't seem to have any proponents that I know of, so
>> briefly:
>>
>> Option 1, Immediately:
>>
>> Pro:
>> -It's conceptually the easiest thing to understand. The following *always*
>> hold:
>>   -For calling code: When any DOM operation I make completes, all
>> observers will have run.
>>   -For notified code: If I'm being called, the operation which caused
>> this is below me on the stack.
>>
>> Con:
>> -Because mutations must be delivered for some DOM operations before
>> the operation is complete, UAs must tolerate all ways in which script
>> may invalidate their assumptions before they do further work.
>>
>>
>> Option 4, Scheduled as a future Task:
>>
>> Pro:
>> -Conceptually easy to understand
>> -Easy to implement.
>>
>> Con:
>> -It's too late. Most use cases for mutation observation require that
>> observers run before a paint occurs. E.g. a widget library which
>> watches for special attributes. Script may create a<div
>> class="FooButton">  and an observer will react to this by decorating
>> the div as a FooButton. It is unacceptable (creates visual
>> artifacts/flickering) to have the div be painted before the widget
>> library has decorated it as a FooButton.
>>
>> Both of these options appear to be non-starters. Option 1 has been
>> shown by experience to be an unreasonable implementation burden for
>> UAs. Option 4 clearly doesn't handle properly important use cases.
>>
>> -------
>>
>> Options 2&  3 have proponents. Since I'm one of them (a proponent),
>> I'll just summarize the main *pro* arguments for each and invite those
>> who wish (including myself), to weigh in with further support or
>> criticism in follow-on emails.
>>
>>
>> Option 2: Upon completion of the "outer-most" DOM operation.
>>
>> Pro:
>> -It's conceptually close to fully synchronous. For simple uses
>> (specifically, setting aside the case of making DOM operations within
>> a mutation callback), it has the advantages of Option 1, without its
>> disadvantages. Because of this, it's similar to the behavior of
>> current Mutation Events.
>
> Pro:
> Semantics are consistent: delivery happens right before the
> outermost DOM operation returns.

This statement is true. When I described Option 2 (perhaps too
harshly) as having "inconsistent semantics", I was referrer only to
the expectations of Callers and Observers. To be totally clear:

Parties:

Caller = any code which performers a DOM operation which triggers a mutation.
Observer = any code to whom the mutation is delivered.

Expectations for synchrony:

Caller: When any DOM operation I make completes, all observers will
have been notified.
Observer: If I'm being notified, the Caller which triggered the
mutation is below me on the stack.

Parties:   Caller              Observer
Options
1:            Always           Always
2:            Sometimes(a) Sometimes(a)
3:            Never             Never
4:            Never             Never

(a) True when Caller is run outside of a mutation observer callback.
False when Caller is run inside a mutation observer callback.

>
> Easier transition from mutation events to the new API.
>
> Not bound to tasks. Side effects, like problems related
> to spinning event loop are per mutation callback, not
> per whole task.
>
>
>
>>
>> Option 3: At the end of the current Task.
>>
>> Pro:
>> -No code is at risk for having its assumptions invalidated while it is
>> trying to do work. All participants (main application script,
>> libraries which are implemented using DOM mutation observation) are
>> allowed to complete whatever work (DOM operations) they wish before
>> another participant starts doing work.
>>
>>
>
> Con:
> Since the approach is bound to tasks, it is not clear what should happen
> if event loop spins while handling the task. What if some other task
> modifies the DOM[1], when should the mutation callbacks fire?
> Because of this issue, tasks, which may spin event loop, should not
> also modify DOM since that may cause some unexpected result.

I think the *pro* side of this you listed is more fair. Both Options 2
& 3 must answer this question. It's true that because Option 3 is
later, it sort of has this issue "more".

However, "what should happen" has been defined. In both cases, if
there are any mutations which are queued for delivery when an inner
event loop spins up, they are *not* delivered inside the inner event
loop. In both Options, they are always delivered in the loop which
queued them.

>
> Callback handling is moved far away from the actual mutation.
>
>
> Pro:
> Can batch more, since the callbacks are called later than in
> option 2.
>
>
> -Olli
>
>
> [1] showModalDialog("javascript:opener.document.body.textContent = '';", "",
> "");
>
Received on Thursday, 11 August 2011 15:14:14 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:47 GMT