- From: Olli Pettay <Olli.Pettay@helsinki.fi>
- Date: Mon, 26 Sep 2011 19:37:52 +0300
- CC: Adam Klein <adamk@chromium.org>, 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 09/26/2011 11:47 AM, Olli Pettay 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(); >> }; > > Yeah, these methods could return mutationobserver. > (Although I don't like the o.observe(foo).observe(bar) kind of coding > style, since it doesn't make it clear which instance's method you're > calling. But one doesn't need to use that.) > > >> >> where MutationCallback is >> >> [Callback, NoInterfaceObject] >> interface MutationCallback { >> void handleEvent(in MutationRecord[] mutations, in >> MutationObserver observer); >> }; > > s/handleEvent/handleMutations/ > > >> >> 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. >> 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; This should be attrNamespace, so that it is clear that it is the namespace of the attribute. >> >> // 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 > > I'm hoping to have a patch (vendor-prefixed) for this API for Gecko in > next few days. > > > -Olli > > >> and to begin work on formalizing the above into a proper >> spec. >> >> - Adam >> >> > > >
Received on Monday, 26 September 2011 16:38:57 UTC