- From: Simon Pieters <simonp@opera.com>
- Date: Thu, 30 Jun 2011 10:44:23 +0200
- To: "Aryeh Gregor" <Simetrical+w3c@gmail.com>, "Adam Klein" <adamk@google.com>, "Rafael Weinstein" <rafaelw@google.com>
- Cc: "Jonas Sicking" <jonas@sicking.cc>, Olli@pettay.fi, "Anne van Kesteren" <annevk@opera.com>, "Webapps WG" <public-webapps@w3.org>
On Wed, 29 Jun 2011 23:54:47 +0200, Rafael Weinstein <rafaelw@google.com> wrote: > On Wed, Jun 29, 2011 at 7:13 AM, Aryeh Gregor <Simetrical+w3c@gmail.com> > wrote: >> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking <jonas@sicking.cc> wrote: >>> This new proposal solves both these by making all the modifications >>> first, then firing all the events. Hence the implementation can >>> separate implementing the mutating function from the code that sends >>> out notifications. >>> >>> Conceptually, you simply queue all notifications in a queue as you're >>> making modifications to the DOM, then right before returning from the >>> function you insert a call like "flushAllPendingNotifications()". This >>> way you don't have to care at all about what happens when those >>> notifications fire. >> >> So when exactly are these notifications going to be fired? In >> particular, I hope non-DOM Core specifications are going to have >> precise control over when they're fired. For instance, execCommand() >> will ideally want to do all its mutations at once and only then fire >> the notifications (which I'm told is how WebKit currently works). How >> will this work spec-wise? Will we have hooks to say things like >> "remove a node but don't fire the notifications yet", and then have to >> add an extra line someplace saying to fire all the notifications? >> This could be awkward in some cases. At least personally, I often say >> things like "call insertNode(foo) on the range" in the middle of a >> long algorithm, and I don't want magic happening at that point just >> because DOM Range fires notifications before returning from >> insertNode. >> >> Also, even if specs have precise control, I take it the idea is >> authors won't, right? If a library wants to implement some fancy >> feature and be compatible with users of the library firing these >> notifications, they'd really want to be able to control when >> notifications are fired, just like specs want to. In practice, the >> only reason this isn't an issue with DOM mutation events is because >> they can say "don't use them", and in fact people rarely do use them, >> but that doesn't seem ideal -- it's just saying library authors >> shouldn't bother to be robust. > > In working on Model Driven Views (http://code.google.com/p/mdv), we've > run into exactly this problem, and have developed an approach we think > is promising. > > The idea is to more or less take Jonas's proposal, but instead of > firing callbacks immediately before the outer-most mutation returns, > mutations are recorded for a given observer and handed to it as an > in-order sequence at the "end" of the event. > > var observer = window.createMutationObserver(callback); > document.body.addSubtreeChangedObserver(observer); > document.body.addSubtreeAttributeChangedObserver(observer); > ... > var div = document.createElement('div'); > document.body.appendChild(div); > div.setAttribute('data-foo', 'bar'); > div.innerHTML = '<b>something</b> <i>something else</i>'; > div.removeChild(div.childNodes[1]); > ... > > // mutationList is an array, all the entries added to > // |observer| during the preceding script event > function callback(mutationList) { > // mutationList === [ > // { type: 'ChildlistChanged', target: document.body, inserted: [div] }, > // { type: 'AttributeChanged', target: div, attrName: 'data-foo' }, > // { type: 'ChildlistChanged', target: div, inserted: [b, i] }, > // { type: 'ChildlistChanged', target: div, removed: [i] } > // ]; > } > >> >> Maybe this is a stupid question, since I'm not familiar at all with >> the use-cases involved, but why can't we delay firing the >> notifications until the event loop spins? If we're already delaying >> them such that there are no guarantees about what the DOM will look >> like by the time they fire, it seems like delaying them further >> shouldn't hurt the use-cases too much more. And then we don't have to >> put further effort into saying exactly when they fire for each method. > > Agreed. > > For context, after considering this issue, we've tentatively concluded > a few things that don't seem to be widely agreed upon: > > 1) In terms of when to notify observers: Sync is too soon. Async (add > a Task) is too late. > > - The same reasoning for why firing sync callbacks in the middle of > DOM operations is problematic for C++ also applies to application > script. Calling mutation observers synchronously can invalidate the > assumptions of the code which is making the modifications. It's better > to allow one bit of code to finish doing what it needs to and let > mutation observers operate "later" over the changes. > > - Many uses of mutation events would actually *prefer* to not run sync > because the "originating" code may be making multiple changes which > more or less comprise a "transaction". For consistency and > performance, the abstraction which is watching changes would like to > operate on the final state. > > - However, typical uses of mutation events do want to operate more or > less "in the same event" because they are trying to create a new > consistent state. They'd like to run after the "application code" is > finished, but before paint occurs or the next scheduled event runs. If I understand you correctly, the HTML spec has support for this. It's called "await a stable state". For instance, the media resource selection algorithm uses it to let the script finish before trying to find a <source> to load. http://www.whatwg.org/specs/web-apps/current-work/complete/webappapis.html#await-a-stable-state http://www.whatwg.org/specs/web-apps/current-work/complete/the-iframe-element.html#concept-media-load-algorithm > 2) Because the system must allow multiple "observers" and allow > observers to make further modifications, it's possible for an > arbitrary number of mutations to have occurred before any given > observer is called. Thus is it preferable to simply record what > happened, and provide the list to the observer. > > - An observer can be naive and assume that only "simple" mutations > have occurred. However, it's more likely that an observer is an > abstraction (like MDV) which only wants to do work WRT to the net of > what has happened. This can be done by creating a projection which > takes the sequence of mutations as input. -- Simon Pieters Opera Software
Received on Thursday, 30 June 2011 08:44:27 UTC