- From: Adam Klein <adamk@chromium.org>
- Date: Mon, 26 Sep 2011 13:58:18 -0700
- To: Olli@pettay.fi
- Cc: public-webapps@w3.org, Ojan Vafai <ojan@chromium.org>, rafaelw@chromium.org, rniwa@chromium.org, Jonas Sicking <jonas@sicking.cc>, annevk@opera.com, arv@chromium.org
On Mon, Sep 26, 2011 at 12:08 PM, Olli Pettay <Olli.Pettay@helsinki.fi> wrote: > On 09/26/2011 09:09 PM, Adam Klein wrote: >> >> On Mon, Sep 26, 2011 at 11:05 AM, Olli Pettay<Olli.Pettay@helsinki.fi> >> wrote: >>> >>> On 09/24/2011 12:16 AM, Adam Klein wrote: >>>> >>>> Chromium (myself, Rafael Weinstein, Erik Arvidsson, Ryosuke Niwa) and >>>> Mozilla (Olli Pettay, Jonas Sicking) have worked together on a >>>> proposal for a replacement for Mutation Events. >>>> >>>> This proposal represents our best attempt to date at making a set of >>>> sensible trade offs which allows for a new mutation observation >>>> mechanism that: >>>> >>>> - Is free of the faults of the existing Mutation Events mechanism >>>> (enumerated in detail here: >>>> http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html) >>>> >>>> - Meets the goal of having the main “questions” that use-cases will >>>> need answered about the net-effect of changes, be computable in linear >>>> time complexity roughly proportional to the number of changes that >>>> occurred. >>>> >>>> Significant aspects of this design: >>>> >>>> - Delivery of MutationRecords happens asynchronously, at the end of >>>> the current “microtask”. This is between Options 2 and 3 from this >>>> discussion >>>> http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0780.html. >>>> Instead of calling listeners at the end of outermost DOM operation or >>>> at the end of a Task, listeners are called at the end of outermost >>>> script invocation. If there are no script invocations, listeners are >>>> called at the end of Task. >>>> >>>> - Information about mutations is delivered to observers as an ordered >>>> sequence of MutationRecords, representing an observed sequence of >>>> changes that have occurred. >>>> >>>> - Subtree observation properly handles the case where nodes are >>>> transiently removed from, mutated outside of and then returned to the >>>> subtree. >>>> >>>> - Observers specify the types of changes they are interested in and >>>> (in some cases) level of detail they require. >>>> >>>> Sample usage: >>>> >>>> var observer = new MutationObserver(function(mutationRecords) { >>>> // Handle mutations >>>> }); >>>> >>>> observer.observe(myNode, >>>> { // options: >>>> subtree: true; // observe the subtree rooted at myNode >>>> childList: true; // include information childNode insertion/removals >>>> attribute: true; // include information about changes to attributes >>>> within the subtree >>>> }); >>>> >>>> … >>>> >>>> observer.disconnect(); // Cease observation >>>> >>>> Details: >>>> >>>> We introduce a new interface MutationObserver with a constructor on >>>> DOMWindow: >>>> >>>> [Constructor(in MutationCallback callback)] >>>> interface MutationObserver { >>>> void observe(in Node target, in MutationObserverOptions options); >>>> void disconnect(); >>>> }; >>>> >>>> where MutationCallback is >>>> >>>> [Callback, NoInterfaceObject] >>>> interface MutationCallback { >>>> void handleEvent(in MutationRecord[] mutations, in >>>> MutationObserver observer); >>>> }; >>>> >>>> Registration& Observation >>>> - A call to observe creates a registration for the observer to be >>>> delivered mutations made to |target|, and optionally, its descendants. >>>> >>>> - Subsequent calls to the same MutationObserver made with the same >>>> |target| have the effect of resetting the options associated with the >>>> registration. >>>> >>>> - Subsequent calls to the same MutationObserver made with different >>>> |targets| have the effect of expanding the set of nodes which are >>>> being observed by the observer. All mutations made to all observed >>>> nodes in all registrations for a given observer are delivered, in >>>> time-ordered sequence, via a single invocation of the >>>> MutationCallback’s handleEvent method. >>>> >>>> - disconnect ceases observation over the observer’s set of observed >>>> nodes. >>>> >>>> Registration Options >>>> The |options| argument provided in observe is defined by the >>>> MutationObserverOptions interface: >>>> >>>> interface MutationObserverOptions { >>>> // Mutation types >>>> boolean childList; // If true, mutations affecting node’s >>>> childNodes are included. >>>> boolean attribute; // If true, mutations affecting element’s >>>> attributes are included. >>> >>> We need to change this name, since per WebIDL 'attribute' can't be used >>> here. >>> >>> I propose we use attr everywhere in the API. MutationRecord has already >>> attrName. >>> attributeOldValue -> attrOldValue and attributeFilter -> attrFilter >> >> Good catch (and I should've caught it when typing this up, as it's >> pseudo-idl), 'attr' works for me. If that shortening isn't >> appreciated, we could go with 'attributes' instead. > > > Apparently I was wrong. > In WebIDL one can escape names using _ prefix. > > But actually, attributes sounds more right than > attribute. One really does want to observe all the attributes, unless > names are filtered. > > so: > attributes > attributeName > attributeNamespace > attributeOldValue > attributeFilter I like 'attributes' as the type, for readability. I'm not entirely convinced we need to lengthen the rest of the names, but I'm all for consistency, so on that basis I think it's OK. - Adam > > > -Olli > > >> >> - Adam >> >>> >>> >>>> boolean characterData; // If true, mutations affecting the value >>>> of CharacterData >>>> //nodes are included. >>>> // [Note: If none of the known mutation types is specified, an >>>> Error is thrown] >>>> >>>> // Subtree observation >>>> boolean subtree; // If true, the observed set of nodes for this >>>> registration should include >>>> // descendants of MutationTarget >>>> (behavior described below). >>>> >>>> // Old values >>>> boolean attributeOldValue; >>>> // If true, MutationRecords describing changes to attributes should >>>> // contain the value of the attribute before the change. If true >>>> // without attribute: true specified, an Error is thrown. >>>> >>>> boolean characterDataOldValue; >>>> // If true, MutationRecords describing changes to >>>> // CharacterData nodes should contain the value >>>> // of the node before the change. If true without >>>> // characterData: true, an Error is thrown. >>>> >>>> // Filtering >>>> DOMString[] attributeFilter; >>>> // If provided, only changes to attributes with localName equaling >>>> // one of the provided strings will be delivered. If provided >>>> without >>>> // attribute: true, an Error is thrown. >>>> }; >>>> >>>> Subtree Observation >>>> If the subtree option is requested during registration, the observer >>>> is delivered mutations which occur to a set of observed nodes which is >>>> computed as follows: >>>> >>>> - At the time of registration, the set includes |target| and all >>>> descendant nodes >>>> - At any point, if a node becomes a descendant of |target|, it is >>>> synchronously added to the observed set >>>> - Immediately before delivering all pending MutationRecords to the >>>> observer, all nodes which are no longer descendants of |target| are >>>> removed from the observed set. >>>> >>>> Processing Model: >>>> >>>> Pending Mutation Queues >>>> For each MutationObserver with at least one active registration, the >>>> UA maintains a queue of pending MutationRecords which are pending >>>> delivery. >>>> >>>> Record Creation& Enqueuing >>>> MutationRecords are created at the following times: >>>> >>>> -When one or more nodes are added to and/or removed from a node’s >>>> childNodes collection, a 'childList' record is created with target set >>>> to that node. >>>> >>>> Each childList MutationRecord represents that, at a given position in >>>> the childNodes collection, a contiguous sequence of nodes was removed >>>> and a contiguous sequence of nodes was inserted. In this way, the >>>> effect on the target node of many (but not all, e.g. execCommand) DOM >>>> operations may be represented by a single child list change record >>>> (e.g. innerHTML, insertBefore, appendChild). The position at which the >>>> nodes were inserted and/or removed is recorded via the previousSibling >>>> and nextSibling attributes of MutationRecord. >>>> >>>> - When an attribute is added, removed or changed, an 'attribute' >>>> record is created with target set to the element owning the attribute. >>>> >>>> - When the data attribute of a CharacterData (Text or Comment node) is >>>> changed a 'characterData' record is created with the target set to >>>> that CharacterData node. >>>> >>>> For each observer, if a registration exists which requests the >>>> matching mutation type and whose observed node set contains the target >>>> node of the mutation, a MutationRecord is appended to the observer's >>>> pending mutation queue. If multiple such registrations exist for a >>>> given observer, a single MutationRecord is delivered having the union >>>> of the information requested by all registrations (e.g. >>>> attributeOldValue). >>>> >>>> Delivery of Pending Mutations >>>> If mutations occur during script invocation, delivery must take place >>>> at the end of the current “microtask”. If mutations are made directly >>>> by the UA and not by script (for example due to user input), delivery >>>> must take place by the end of the current Task. >>>> >>>> The steps for delivery are: >>>> >>>> 1) Visit each observer, in registration order >>>> >>>> For each observer, >>>> >>>> 2) Remove any non-descendants from the observed set of any subtree >>>> observation associated with this observer >>>> >>>> 3) If the observer’s pending mutation queue is non-empty, clear the >>>> queue, putting the contents into a MutationRecord[] array and call the >>>> observer’s handleEvent method, passing the newly created array of >>>> records. If the observer’s associated callback is a bare function >>>> (and not an object with a handleEvent method), it is called with >>>> |this| set to the observer. >>>> >>>> 4) If any observer’s pending queue has become non-empty, goto step 1. >>>> >>>> Mutation Records >>>> >>>> interface MutationRecord { >>>> // Mutation type: one of 'childList', 'attribute', or 'characterData' >>>> readonly attribute DOMString type; >>>> >>>> // For childList and attributes, target is the owner node affected. >>>> // For CharacterData, target is the node affected. >>>> readonly attribute Node target; >>>> >>>> // For type == 'childList’, Sequence of added and removed nodes in >>>> this operation. >>>> readonly attribute NodeList addedNodes; >>>> readonly attribute NodeList removedNodes; >>>> >>>> // For type == 'childList’, The siblings in childNodes immediately >>>> preceding following the first >>>> // and last nodes added and/or removed. >>>> readonly attribute Node previousSibling; >>>> readonly attribute Node nextSibling; >>>> >>>> // For type == 'attribute', the name and namespaceURI of the >>>> attribute affected >>>> readonly attribute DOMString attrName; >>>> readonly attribute DOMString namespaceURI; >>>> >>>> // For type == ‘attribute’ or ‘characterData’, if requested, the >>>> value immediately >>>> // preceding the mutation. >>>> readonly attribute DOMString oldValue; >>>> }; >>>> >>>> MutationRecords are immutable. All the WebIDL attributes are >>>> non-writable and non-configurable. The UA may deliver the same >>>> MutationRecord to multiple observers just like Events, MutationRecords >>>> are still extensible so they could be used as side channels for >>>> communication. >>>> >>>> However, observers must only be delivered MutationRecords with the >>>> exact level of detail that they requested (e.g. If an observer >>>> requested attribute mutations, but not attributeOldValue it should >>>> never be delivered an ‘attribute’ type MutationRecord’s containing >>>> oldValue). >>>> >>>> My next plans are to begin a vendor-prefixed implementation of this >>>> API in WebKit and to begin work on formalizing the above into a proper >>>> spec. >>>> >>>> - Adam >>>> >>>> >>> >>> >> > >
Received on Monday, 26 September 2011 20:58:54 UTC