- From: Olli Pettay <Olli.Pettay@helsinki.fi>
- Date: Wed, 20 Jul 2011 15:20:16 +0300
- To: Jonas Sicking <jonas@sicking.cc>
- CC: Rafael Weinstein <rafaelw@google.com>, Ryosuke Niwa <rniwa@webkit.org>, Aryeh Gregor <Simetrical+w3c@gmail.com>, Adam Klein <adamk@google.com>, Anne van Kesteren <annevk@opera.com>, Webapps WG <public-webapps@w3.org>
On 07/20/2011 02:01 AM, Jonas Sicking wrote: > On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sicking<jonas@sicking.cc> wrote: >> On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein<rafaelw@google.com> wrote: >>>> So yes, my proposal only solves the usecase outside mutation handlers. >>>> However this is arguably better than never solving the use case as in >>>> your proposal. I'm sure people will end up writing buggy code, but >>>> ideally this will be found and fixed fairly easily as the behavior is >>>> consistent. We are at least giving people the tools needed to >>>> implement the synchronous behavior. >>> >>> Ok. Thanks for clarifying. It's helpful to understand this. >>> >>> I'm glad there's mostly common ground on the larger issue. The point >>> of contention is clearly whether accommodating some form of sync >>> mutation actions is a goal or non-goal. >> >> Yup, that seems to be the case. >> >> I think the main reason I'm arguing for allowing synchronous callbacks >> is that I'm concerned that without them people are going to stick to >> mutation events. If I was designing this feature from scratch, I'd be >> much happier to use some sort of async callback. However given that we >> need something that people can migrate to, and we don't really know >> what they're using mutation events for, I'm more conservative. > > Ok, here is my updated proposal. > > There are two issues at stake here: When to send notifications, and > what they contain. I'll get to when to send them second as that is a > more controversial. > > As for what the notification contain, lets first start at how to > register for notifications. Since we want a single callback to contain > information about all mutations that has happened, we need the ability > to choose, for a single callback, which mutations we should tell it > about. Something like this would work: > > node.addMutationListener(listener, { childlist: true, attributes: > true, characterdata: true }); > node.removeMutationListener(listener); > > 'listener' above would be a function which receives a single argument > when notifications fire. The value of this argument would be an Array > which could look something like this: > > [ { target: node1, type: "childlist", added: [a, b, c, d], removed: [x, y] }, > { target: node1, type: "attributes", changed: ["class", "bgcolor", "href"] }, > { target: node2, type: "characterdata" }, > { target: node3, type: "childlist", added: [r, s, t, x], removed: [z] } ] > > A few things to note here: > > * There is only ever one entry in the array for a given target+type > pair. If, for example, multiple changes are made to the classlist of a > given node, these changes are added to the added/removed lists. > * For "childlist" changes, you get the full list of which nodes were > added and removed. > * For "attributes" changes you get a full list of which attributes > were changed. However you do not get the new and old value of the > attributes as this could result in significant overhead for attributes > like "style" for example. > * For "characterdata" you don't get the old or new value of the node. > We could also simply add the before/after values here as there > shouldn't be as much serialization overhead involved. > > > A nice thing with the above approach is that it is very expandable if > we want to introduce more types of notifications in the future. Some > examples that have been mentioned are the ability to be notified about > class changes, text-content changes and changes to individual > attributes. We could do that using: > > node.addMutationListener(listener, { class: ["myclass1", "warning"], > textcontent: true, attributes: ["type", "title", "data-foo"] }); > > We could also add the ability to get notified about microdata changes > or data-prefix-* changes. But for now I think we should start with a > minimal set and see if people use it. But it's good to know that we > have a path forward. > > There are of course a few more things that needs to be defined. Here > some of them: > > * The notification-objects are added to the list in the order they > happen. With the exception that if there is a notification-object for > the specific target+type then a new object isn't created, but rather > added to the existing one. > * If you call addMutationListener with the same listener multiple > times any new "flags" are added to the existing registration. So > node.addMutationListener(listener, { attributes: true }); > node.addMutationListener(listener, { childlist: true }); > is equivalent to > node.addMutationListener(listener, { childlist: true, attributes: true }); > * For the "childlist" notifications, nodes are added to the > added/removed lists in document order when a whole list of them are > added or removed. For example for .appendChild(docfragment) or > .textContent = "". > * If a node is first added and then removed from a childlist, it > doesn't appear in neither the "added" nor the "removed" lists for the > childlist notification. > * If a node is removed and then readded to a childlist, it appears in > both the "added" and the "removed" lists. This is needed to indicate > that it might have a different location now. > > > So, this leaves the issue of when to fire these notifications. I had a > very interesting talk with Rafael Weinstein about this last week and > he presented some very strong points. > > The two proposals that we have on the table are: > > 1. Mostly-synchronous. The notifications are dispatched at the end of > any mutating DOM call. The notifications are not fully synchronous > when the mutation happens inside another mutation notification > callback. > 2. Almost-asynchronous. The notifications are dispatched at the end of > the task which mutated the the DOM. So before any other scheduled > tasks get a chance to run. The distinction to fully asynchronous is > important since it means that all tasks still see a consistent state, > including tasks that paint. I don't understand this. There are cases when event loop may spin while handling a task. How should the almost-async listeners be handled in that case - at which point would they be called? > > The advantage of synchronous are fairly obvious. Indeed. So I'm quite surprised that you are leaning towards async. > Strictly speaking it > provides a superset of functionality compared to the asynchronous > method. It also has the advantage that it is more similar to the API > we're trying to replace, mutation events, which might make migration > easier. > > However, the asynchronous version also has advantages. First of all > it's more consistent in that for a given mutating DOM operation, the > callbacks have never been called by the time the operation returns. > I.e. code that runs inside a mutation handler doesn't see different > behavior from code that runs outside it. > > An even bigger advantage however, and this was the convincing argument > for me, is that it's a simpler API to develop against for web > developers. How so? Mostly-sync is in general close to how event handling works, so web devs should be familiar with it. > As browser implementors, synchronous events (and other > callbacks) are always a pain in the ass because the world can change > under us by the time the event is done firing. With the > mostly-synchronous option, we create the same situation for web > developers. By the time that they get control again from the > notifications, all sorts of things might have changed under them. > > This is especially the case when you have several independent > libraries running on a page, which is exactly the target audience for > mutation notifications. > > Hence I'm leaning towards using the almost-asynchronous proposal for > now. If we end up getting the feedback from people that use mutation > events today that they won't be able to solve the same use cases, then > we can consider using the synchronous notifications. However I think > that it would be beneficial to try to go almost-async for now. I disagree. > > / Jonas > >
Received on Wednesday, 20 July 2011 12:21:22 UTC