Re: Mutation events replacement

2011-07-05 Thread Adam Klein
On Mon, Jul 4, 2011 at 9:57 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 07/04/2011 07:28 PM, Ojan Vafai wrote:

 Apologies in advance if my comment makes no sense. This is a long
 thread, I tried to digest it all. :)

 On Sat, Jul 2, 2011 at 7:07 AM, Boris Zbarsky bzbar...@mit.edu
 mailto:bzbar...@mit.edu wrote:

    That may be ok, if the use cases that incur this cost are rare and
    the common case can be better served by a different approach.

    Or put another way, if 1% of consumers want the full list because it
    makes them 4x faster and the other 99% don't want the full list, and
    the full list is 3x slower for the browser to build than just
    providing the information the 99% want, what's the right tradeoff?


 I'm not sure there really is a performance tradeoff. I believe that the
 proposal Rafael put forward should almost always be faster. Storing the
 list of changes and doing a JS callback once, for nearly all use-cases,
 should be faster than frequent, semi-synchronous callbacks.

 The only bit that might be slower is what data you include in the
 mutation list. I believe that all the data you'd need is cheap except
 for possibly the following two:
 -The index of the child that changed for ChildListChanged (is this
 actually expensive?)

 You may need more than just an index. element.innerHTML = null removes
 all the child nodes.
 And element.inserBefore(some_document_fragment, element.lastChild)
 may insert several child nodes.
 Depending on whether we want to get notified for each mutation
 or batch the mutations, simple index may or may not be enough.

Would a node reference be better (nextSibling)?  Assuming the
listeners have access to all inserted/removed nodes along the way,
using another as an anchor seems like it would work properly (though
the innerHTML case may need something special).


 -The old value of an attribute/text node. I know this is expensive in
 Gecko's engine at least.

 Shouldn't be that slow.

 Mutation listener could easily
 implement old/new value handling itself, especially if it knows which
 attributes it is interested in.

This only works if listeners don't care about intermediate values,
since all they'll have access to is the last value they saw and the
current value in the DOM. If it was set several times during a single
mutation event (whether that be your or Rafael's definition of a
transaction), they'll miss those in-between values.  Also, while
this would be acceptable for some use cases, the editing/undo use case
would need to keep values of all attributes at all nodes, which seems
likely to be worse than having the UA take care of this.

 I'd be fine with excluding that information by default, but having a
 flag you pass at some point saying to include those. That way, only
 sites that need it take the performance hit.

Given that different use cases seem to have wildly different
requirements (some probably only care about one or two attributes
while others care about the entire document), this approach to
handling the availability of oldValue/newValue is appealing.

- Adam




Re: Mutation events replacement

2011-07-05 Thread Adam Klein
On Mon, Jul 4, 2011 at 1:45 PM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 07/04/2011 08:16 PM, Adam Klein wrote:
 On Mon, Jul 4, 2011 at 9:57 AM, Olli Pettayolli.pet...@helsinki.fi
  wrote:
 On 07/04/2011 07:28 PM, Ojan Vafai wrote:
 The only bit that might be slower is what data you include in the
 mutation list. I believe that all the data you'd need is cheap except
 for possibly the following two:
 -The index of the child that changed for ChildListChanged (is this
 actually expensive?)

 You may need more than just an index. element.innerHTML = null removes
 all the child nodes.
 And element.inserBefore(some_document_fragment, element.lastChild)
 may insert several child nodes.
 Depending on whether we want to get notified for each mutation
 or batch the mutations, simple index may or may not be enough.

 Would a node reference be better (nextSibling)?  Assuming the
 listeners have access to all inserted/removed nodes along the way,
 using another as an anchor seems like it would work properly (though
 the innerHTML case may need something special).

 Where would the node reference be?
 What would the API look like?

In Rafael's API, each mutation is represented by an object, so I'd
simply put it there with its own key, something like:

interface MutationRecord {
  // e.g., 'ChildListChanged', 'AttributeChanged'
  readonly attribute DOMString type;
  // element whose childNodes changed, or whose attribute was changed
  readonly attribute Node target;
  readonly attribute NodeListinsertedNodes;
  readonly attribute NodeListremovedNodes;
  // reference to the node before which the above nodes were removed/inserted
  readonly attribute Node nextSibling;
};

With your API, I think you'd want to change from passing nodes to the
callback to using something like the above, analogous to the
information hanging off of MutationEvent in the current spec.

- Adam




Re: Mutation events replacement

2011-07-21 Thread Adam Klein
On Thu, Jul 21, 2011 at 4:30 PM, Jonas Sicking jo...@sicking.cc wrote:
 On Thu, Jul 21, 2011 at 8:19 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 07/21/2011 06:01 PM, Boris Zbarsky wrote:

 On 7/21/11 5:08 AM, Dave Raggett wrote:

 Thanks for the explanation. Apps would need a way to disable
 notifications during such animation sequences, and would be able to find
 another means to serialize the animation (at a higher level).

 I'm not sure I trust apps to do that, which is why I think the default
 behavior should be that they just don't get the information.

 This raises the question is unregistering and re-regtistering a mutation
 notification handler cheap or do we need an alternative mechanism for
 temporarily suspending notifications?

 Olli is better able than I to answer this for Gecko.

 In the current WIP patch for mutation event replacement registering and
 unregistering listeners is cheap, and if there are no listeners,
 performance isn't affected at all.
 This is with the sync approach.

 If async approach is taken, listener handling becomes significantly
 more complicated. What if the listener is added after the mutation has
 happened, should it be called? If not, then we need to keep a list of
 changes for all the listeners separately.

 Hmm.. the most trivial implementation is to keep different lists for
 different listeners no matter what.

 However maybe it's worth allowing listeners to be able to share
 mutation objects? Is that what you were thinking? That might be good
 for performance since it'll create fewer objects, but it'll also add
 more complexity since it requires more bookkeeping.

 Though note that the list of mutation objects will have to be
 per-listener no matter what (i.e. both in the mostly-sync and the
 almost-async suggestions) since different listeners will be observing
 different parts of the tree and thus will see different set of
 mutations.

 The simplest solution that I can think of is to say that the
 addMutationObserver function doesn't take effect until at the end of
 the task. So any listener registered during a task won't get
 notifications until at the end of the following task.

 There are other solutions that we could use, but they seem much more
 complex and so I'd rather avoid them unless there's a good reason to.

This is only complex because you're coalescing the mutations, right?
In Rafael's original proposal, each mutation would result in a single
immutable mutation record, so the semantics would be to deliver (by
appending to a queue associated with each observer) a mutation record
to any currently-registered observers.

Or is there some other concern with beginning notifications partway
through a task?

- Adam



Mutation Observers: a replacement for DOM Mutation Events

2011-09-23 Thread Adam Klein
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.
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 

Re: Mutation Observers: a replacement for DOM Mutation Events

2011-09-23 Thread Adam Klein
On Fri, Sep 23, 2011 at 4:44 PM, Tab Atkins Jr. jackalm...@gmail.com wrote:
 On Fri, Sep 23, 2011 at 2:16 PM, Adam Klein ad...@chromium.org wrote:
 [Constructor(in MutationCallback callback)]
 interface MutationObserver {
   void observe(in Node target, in MutationObserverOptions options);
   void disconnect();
 };

 It would be nice to have both of these return the MutationObserver
 rather than void, so you can chain calls.

I don't think that makes sense for disconnect() (at least the version
specced here), since it stops observation of all nodes so chaining
wouldn't make sense.  But I definitely see that chaining observe could
be convenient.

- Adam



Re: Mutation Observers: a replacement for DOM Mutation Events

2011-09-26 Thread Adam Klein
On Mon, Sep 26, 2011 at 11:05 AM, Olli Pettay olli.pet...@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.

- 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

Re: Mutation Observers: a replacement for DOM Mutation Events

2011-09-26 Thread Adam Klein
On Mon, Sep 26, 2011 at 1:47 AM, Olli Pettay olli.pet...@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();
 };

 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/

I've yet to see a WebIDL callback that uses a method other than
handleEvent (it's not just EventListeners).  Any reason why
MutationCallbacks should be different?



 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

Re: Mutation Observers: a replacement for DOM Mutation Events

2011-09-26 Thread Adam Klein
On Mon, Sep 26, 2011 at 12:08 PM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 09/26/2011 09:09 PM, Adam Klein wrote:

 On Mon, Sep 26, 2011 at 11:05 AM, Olli Pettayolli.pet...@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

Re: Mutation Observers: a replacement for DOM Mutation Events

2011-09-28 Thread Adam Klein
On Tue, Sep 27, 2011 at 1:12 PM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 09/24/2011 12:16 AM, Adam Klein wrote:

 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).


 This is actually still ambiguous.
 What if attributeOldValue is first set to true for document.documentElement,
 and then false (implicitly or explicitly)
 for document, should attribute values be reported?

 Another problematic case is that if we have subtree A and B and both
 are being observed by same observer but different options. Then some
 node from A is moved to B. Which options should be used?

 So, should we define that union means that 'true' values override
 'false' values, and attributeFilter values are union'ed the usual way?

Yes, I think this is what I actually had in mind: true values override
false values.

 To make API somewhat more consistent, should we change
 Subsequent calls to the same MutationObserver made with the same |target|
 have the effect of resetting the options associated with the registration. 
 to follow the union-ing.

If we're going to do that, I think we need to add the ability to stop
listening to a single target (rather than disconnecting from all).
But I'm not convinced we need this consistency, as I suspect the above
case (covering multiple nodes with different registrations and
different options) is quite unusual.

- Adam



Re: [DOM4] NodeList should be deprecated

2012-03-12 Thread Adam Klein
On Thu, Mar 8, 2012 at 11:57 PM, Anne van Kesteren ann...@opera.com wrote:

 On Thu, 08 Mar 2012 23:24:45 +0100, Ojan Vafai o...@chromium.org wrote:

 Dynamic NodeLists have a significant memory and performance cost. Static
 NodeLists are basically just under-powered arrays. We should just return
 Node arrays from any new APIs that return a list of Nodes. I'd like to
 see NodeList get similar treatment to hasFeature, i.e. a note that it not be
 used for new APIs and possibly even the explicitly list the APIs allowed
 to return them.

 I don't see the Dynamic/Static distinction in DOM4 or the HTML spec. Is
 this specified anywhere?


 We use the words live and static.



  For reference, this came up in WebKit with some new Regions APIs
 https://bugs.webkit.org/show_**bug.cgi?id=80638https://bugs.webkit.org/show_bug.cgi?id=80638
 .


 Mutation observers uses NodeList too.


Though there's no reason they couldn't use arrays instead (they're not
live). In fact, that would make the API more self-consistent, since arrays
(of MutationRecords) are already part of the interface.

- Adam


Re: Should MutationObservers be able to observe work done by the HTML parser?

2012-06-19 Thread Adam Klein
On Sun, Jun 17, 2012 at 12:17 PM, Ryosuke Niwa rn...@webkit.org wrote:

 On Sun, Jun 17, 2012 at 5:03 AM, Jonas Sicking jo...@sicking.cc wrote:

 On Sat, Jun 16, 2012 at 7:04 AM, Rafael Weinstein rafa...@google.com
 wrote:
  I too thought we had intentionally spec'd them to not fire during load.
 
  The HTML spec is clear about this WRT Mutation Events:
 
  http://www.whatwg.org/specs/web-apps/current-work/#tree-construction:
 
  DOM mutation events must not fire for changes caused by the UA
  parsing the document. (Conceptually, the parser is not mutating the
  DOM, it is constructing it.) This includes the parsing of any content
  inserted using document.write() and document.writeln() calls.
 
  It seems like this should also apply to Mutation Observers, unless we
  have compelling reasons to do otherwise.

 This was something that we got people complaining about with mutation
 events over the years. Our answer used to be that mutation events
 generally suck and you can't depend on them anyway. Obviously not an
 argument we'd want to use for MutationObservers.

 I can't think of any cases where you would *not* want these to fire
 for parser mutations.


 Agreed. I'm in favor of observers being notified for parser-initiated DOM
 mutations. The primary reason we don't fire mutation events for parser
 insertion  removal is because they're synchronous and introduces all sorts
 of problems including security vulnerabilities but that isn't the case with
 mutation observers.

 One question. Should we also notify mutation observers immediately before
 executing synchronous scripts (i.e. script elements without differ or async
 content attributes) to address Mihai's use case?


This is one part of a more general question (raised by Ojan on
webkit.org/b/89351): what should the timing be for delivery of these
parser-initiated mutations? Mihai's use case is one example where we might
want something other than end-of-task delivery. Ojan points out that simply
using end-of-task could expose low-level implementation detail of the
parser to script (such as how much parsing is done in a single task before
the parser yields).

Does Firefox do anything special here? Or does it simply use the same
end-of-task delivery as everywhere else?

- Adam


Re: Should MutationObservers be able to observe work done by the HTML parser?

2012-06-20 Thread Adam Klein
On Wed, Jun 20, 2012 at 12:36 AM, Ryosuke Niwa rn...@webkit.org wrote:

 On Tue, Jun 19, 2012 at 1:52 PM, Olli Pettay olli.pet...@helsinki.fiwrote:

   Ojan points out

 that simply using end-of-task could expose low-level implementation
 detail of the parser to script (such as how much parsing is done in a
 single task
 before the parser yields).

 Does Firefox do anything special here? Or does it simply use the same
 end-of-task delivery as everywhere else?


 end-of-microtask or end-of-task everywhere. And yes, some parsing /
 networking details may unfortunately be exposed, but in a way which should
 be quite random. Web devs just can't really rely on network packages to be
 delivered to parser in some exact way.


 That randomness seems undesirable. Can we delay the delivery until
 DOMContentLoaded is fired so that we can have more consisnte behavior here?


This doesn't seem very useful to me. Observing during page load will
clearly have a cost associated with it, and if you aren't notified until
DOMContentLoaded, you could have just listened for DOMContentLoaded
yourself, looked through the document for the stuff you care about, and
then attach an observer.

I'm not entirely opposed to notifying of parse-time mutations, but if we
did it seems like we'd want to deliver them during page load in some way.

- Adam


Re: Should MutationObservers be able to observe work done by the HTML parser?

2012-06-26 Thread Adam Klein
On Wed, Jun 20, 2012 at 12:29 AM, Anne van Kesteren ann...@annevk.nlwrote:

 On Tue, Jun 19, 2012 at 10:52 PM, Olli Pettay olli.pet...@helsinki.fi
 wrote:
  end-of-microtask or end-of-task everywhere. And yes, some parsing /
  networking details may unfortunately be exposed,
  but in a way which should be quite random. Web devs just can't really
 rely
  on network packages to be delivered to parser in
  some exact way.

 I think the original solution we had to not expose parser mutations
 was better. Exposing this can lead to all kinds of subtle bugs that
 are hard to detect for developers.


I take it from your reply that you and I had the same view of what's
specced in DOM4. That is, that MutationObservers are not specified to be
notified of actions taken by the parser. Given that fact, it seems that
either the spec should be changed (and by spec here I think the required
changes are in HTML, not DOM), or Firefox's implementation ought to be
changed.

Anne, Ian, Olli, Jonas, your thoughts?

- Adam


Re: Should MutationObservers be able to observe work done by the HTML parser?

2012-06-29 Thread Adam Klein
On Fri, Jun 29, 2012 at 2:44 AM, Jonas Sicking jo...@sicking.cc wrote:

 All in all I think that as soon as we introduce exceptions to when
 MutationObserver callbacks fire we change the API from being a
 reliable way to track DOM mutations to a unreliable way where all
 callers have to be aware of exceptions and deal with them in other
 ways. I.e. it feels like it significantly reduces the value of
 MutationObservers.

 And so far I don't see any good arguments made for making that
 reduction in value. Did I miss any arguments other then the randomness
 argument?


Performance was one concern that's come up in discussing this with Ojan and
Rafael. Imagine a MutationObserver attached to the document near the top of
the page. Now we need to create basically one MutationRecord per node
inserted (because the parser operates in a depth-first sort of order). I'm
not at all sure this is a show-stopper (we might be able to introduce some
new MutationRecord type that could compactly represent parser-style
operation), but it definitely seems worrisome, especially given that one of
the common uses for MutationObservers is extensions which might run on many
(all?) pages.

- Adam


Re: Making template play nice with XML and tags-and-text

2012-07-18 Thread Adam Klein
On Wed, Jul 18, 2012 at 9:19 AM, Adam Barth w...@adambarth.com wrote:

 Inspired by a conversation with hsivonen in #whatwg, I spend some time
 thinking about how we would design template for an XML world.  One idea I
 had was to put the elements inside the template into a namespace other than
 http://www.w3.org/1999/xhtml.


Interesting idea! We considered something like this back before Rafael's
initial email to WebApps but discarded it for reasons discussed below.

One question about your proposal: do the contents of template in an HTML
document parse as HTML or XHTML (I'm not as familiar as I should be with
how the contents of svg are parsed in HTML)? For example, can I omit
closing /p tags?


 Unlike the existing wormhole template semantics, in this approach the
 tags-and-text inside template would translate into DOM as usual for XML.
  We'd get the inert behavior for free because we'd avoid defining any
 behavior for elements in the http://www.w3.org/2012/xhtml-templatenamespace 
 (just as no behavior is defined today).


This does get you inertness, but doesn't avoid querySelector matching
elements inside template.

Also, the elements inside template, though they appear to be HTML,
wouldn't have any of the IDL attributes one might expect, e.g., a
href=foo/a would have no href property in JS (nor would img have
src, etc). They are, perhaps, too inert.

When combined with the querySelector problem, this seems especially bad,
since jQuery-style invocations would expect everything matching, say,
$('img') to behave the same way (I guess we already have this problem with
SVG a tags, but it seems especially weird in a document that one might
think is all-HTML).

- Adam


Re: Why can't we just use constructor instead of createdCallback?

2014-02-18 Thread Adam Klein
On Tue, Feb 18, 2014 at 3:26 PM, Dimitri Glazkov dglaz...@google.com wrote:
 On Tue, Feb 18, 2014 at 2:59 PM, Jonas Sicking jo...@sicking.cc wrote:
  On Tue, Feb 18, 2014 at 10:35 AM, Dimitri Glazkov dglaz...@google.com 
  wrote:
   The thing that really bothers me is that this approach is contradicting
   itself. We go to into pretty elaborate lengths to enable running
   constructors during parsing, but the key performance lesson developers 
   will
   immediately learn is to avoid constructors on custom elements, because 
   they
   will trigger the two-phase code path during parsing. Here's a thing that 
   you
   can use, but you probably don't want to ever use it.
 
  The above paragraph appears to assume that creating this list is slow.
  Do you have data to back that up?

 No, of course not :) It's a first intuitive reaction.

One example that seems intuitively problematic about this approach (to
me, anyway) are those parts of the parser that inspect the tree during
parsing: foster parenting. I'm not sure it's a performance issue so
much as a complexity issue. Consider:

div
  my-element
table
  div

When following the spec
(http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#appropriate-place-for-inserting-a-node),
any text in that algorithm which says parent node need to be patched
to say something about this new list of to-be-attached nodes.

Patching all such references seems likely to be difficult and
error-prone, but I admit that I can't quantify those worries.

- Adam



Re: Indexed DB Transactions vs. Microtasks

2014-06-18 Thread Adam Klein
On Fri, Jun 6, 2014 at 5:51 PM, Jonas Sicking jo...@sicking.cc wrote:

 On Thu, Jun 5, 2014 at 12:59 PM, Joshua Bell jsb...@google.com wrote:
  case 1:
 
var tx;
Promise.resolve().then(function() {
  tx = db.transaction(storeName);
  // tx should be active here...
}).then(function() {
  // is tx active here?
});
 
  For case 1, ISTM that yes matches the IDB spec, since control has not
  returned to the event loop while the microtasks are running.
 Implementations
  appear to agree.

 Yes. I believe that this is what the spec currently calls for.

 However I think it would actually be good to change the spec to say
 that the transaction is closed at the end of the current microtask.

 When we originally designed microtasks for Mutation Observers, one of
 the goals was to ensure that there were not accidental
 interdependencies between different event handlers to the same event.
 By flushing end-of-microtask work after each event handler you ensure
 that each event handler gets a clean slate.

 I realize that this is a backwards-incompatible change. However it
 seems pretty unlikely to break any existing content. So it'd be nice
 to give it a try.


While I agree that the original microtask intent would suggest we change
this, and I concur that it seems unlikely to break content, I worry about
the spec and implementation complexity that would be incurred by having to
support the notion of at the end of the current microtask. It suggests
one of:

1. A new task queue, which runs after microtasks (nanotasks?)
2. The ability to put tasks at the start of the microtask queue rather than
at the end

(1) seems like a bad idea for a variety of reasons, not least of which that
it would soon lead us to need picotasks once an actor other than IDB saw a
need for it.

(2) strikes me as just plain hard to reason about. And again, if anyone
other than IDB tried to use that capability, it would become problematic.

Code running at end-of-microtask already has to deal with being in a
slightly different environment than normal event-handling code. I think
it might be worth avoiding additional complexity to make this quirk one of
those caveats.

- Adam

(resending from correct address, a dup may show up at some point)


Re: Indexed DB Transactions vs. Microtasks

2014-06-18 Thread Adam Klein
On Wed, Jun 18, 2014 at 9:13 PM, Domenic Denicola 
dome...@domenicdenicola.com wrote:

 [+Yehuda, +Raf]

 From: Jonas Sicking [mailto:jo...@sicking.cc]

  On Thu, Jun 19, 2014 at 8:44 AM, Adam Klein ad...@google.com wrote:
  While I agree that the original microtask intent would suggest we
  change this, and I concur that it seems unlikely to break content, I
  worry about the spec and implementation complexity that would be
  incurred by having to support the notion of at the end of the current
  microtask. It suggests one
  of:
 
  1. A new task queue, which runs after microtasks (nanotasks?) 2. The
  ability to put tasks at the start of the microtask queue rather than
  at the end
 
  I was just thinking to hardcode this into the algorithm that's run at
 the end of the microtask. Note that closing the transaction never runs
 code, which means that very little implementation complexity is needed.

  I definitely agree that both of the above options are pretty
 unattractive.

 This recalls Yehuda's proposal for a bucketed microtask queue. It seems
 like this is a very strong argument for it.


This seems orthogonal to bucketing. The IDB transaction deactivation step
isn't a sort of work that we'd want to bucket (as I argued in my previous
message, treating this IDB work as a task leads down some bad roads); as
Jonas says, we would actually want to hardcode it as a thing that just
happens after each microtask is run (an idea that would have unappealing
consequences for the Chromium implementation of microtasks, but one which
I'll mull over).

- Adam


Re: Indexed DB Transactions vs. Microtasks

2014-06-19 Thread Adam Klein
On Fri, Jun 6, 2014 at 5:51 PM, Jonas Sicking jo...@sicking.cc wrote:

 On Thu, Jun 5, 2014 at 12:59 PM, Joshua Bell jsb...@google.com wrote:
  case 1:
 
var tx;
Promise.resolve().then(function() {
  tx = db.transaction(storeName);
  // tx should be active here...
}).then(function() {
  // is tx active here?
});
 
  For case 1, ISTM that yes matches the IDB spec, since control has not
  returned to the event loop while the microtasks are running.
 Implementations
  appear to agree.

 Yes. I believe that this is what the spec currently calls for.

 However I think it would actually be good to change the spec to say
 that the transaction is closed at the end of the current microtask.

 When we originally designed microtasks for Mutation Observers, one of
 the goals was to ensure that there were not accidental
 interdependencies between different event handlers to the same event.
 By flushing end-of-microtask work after each event handler you ensure
 that each event handler gets a clean slate.

 I realize that this is a backwards-incompatible change. However it
 seems pretty unlikely to break any existing content. So it'd be nice
 to give it a try.


While I agree that the original microtask intent would suggest we change
this, and I concur that it seems unlikely to break content, I worry about
the spec and implementation complexity that would be incurred by having to
support the notion of at the end of the current microtask. It suggests
one of:

1. A new task queue, which runs after microtasks (nanotasks?)
2. The ability to put tasks at the start of the microtask queue rather than
at the end

(1) seems like a bad idea for a variety of reasons, not least of which that
it would soon lead us to need picotasks once an actor other than IDB saw a
need for it.

(2) strikes me as just plain hard to reason about. And again, if anyone
other than IDB tried to use that capability, it would become problematic.

Code running at end-of-microtask already has to deal with being in a
slightly different environment than normal event-handling code. I think
it might be worth avoiding additional complexity to make this quirk one of
those caveats.

- Adam


Re: Custom element lifecycle callbacks

2015-01-08 Thread Adam Klein
On Thu, Jan 8, 2015 at 7:56 AM, Anne van Kesteren ann...@annevk.nl wrote:

 1) Can we use symbols to identify these instead? That gives us a
 guarantee they won't be used for other things and makes it somewhat
 safer to put them where they are located now.


As Dimitri noted, I've expressed mild concerns about this in the past, but
I may be coming around to the idea. The main advantage of using Symbols is
that it would guarantee us the ability to add new callbacks in the future
without fear of breaking existing elements, though I don't know how
realistic a concern that is given the Callback suffix all these names
have.

Do you have a proposal for where these symbols would be vended? In ES,
builtin symbols are available as properties on the Symbol object, but
clearly WebIDL shouldn't be adding things there. This might be a good
question for public-script-coord.

- Adam


Re: template namespace attribute proposal

2015-03-12 Thread Adam Klein
On Thu, Mar 12, 2015 at 11:22 AM, Anne van Kesteren ann...@annevk.nl
wrote:

 On Thu, Mar 12, 2015 at 7:16 PM, Adam Klein ad...@chromium.org wrote:
  For clarity, is this significantly different from the below (which works
  today)?
 
  template id=tmpl
svg
  circle .../
/svg
  /template
 
  Clearly there's an extra step here, in that accessing the SVG elements
  requires hopping into firstElementChild, but adding new namespace-related
  features seems unfortunate.

 That was going to be my argument, but then I remembered that we went
 out of our way to make tbody, tr, td, etc. work as direct
 children of template.


Is your thinking that adding special-casing for SVG-looking (as in, tag
names appearing in the list of SVG tags but not in the list of HTML tags)
inside template has fewer compat risks than a wholesale change of the
HTML parser to recognize SVG-looking tags anywhere?


Re: template namespace attribute proposal

2015-03-12 Thread Adam Klein
On Wed, Mar 11, 2015 at 8:32 PM, Benjamin Lesh bl...@netflix.com wrote:

 I'd like to propose that the template tag have a namespace= attribute
 that allows the user to specify namespaces such as 
 http://www.w3.org/2000/svg;, so that the document fragment that comes
 from `.content` is created properly.

 e.g.:

 template id=my-svg-template namespace=http://www.w3.org/2000/svg
   circle cx=10 cy=10 cr=10/
 /template


For clarity, is this significantly different from the below (which works
today)?

template id=tmpl
  svg
circle .../
  /svg
/template

Clearly there's an extra step here, in that accessing the SVG elements
requires hopping into firstElementChild, but adding new namespace-related
features seems unfortunate.

- Adam


Re: Custom Elements: insert/remove callbacks

2015-05-08 Thread Adam Klein
On Thu, May 7, 2015 at 10:56 PM, Elliott Sprehn espr...@chromium.org
wrote:


 On Thu, May 7, 2015 at 10:44 PM, Anne van Kesteren ann...@annevk.nl
 wrote:

 On Fri, May 8, 2015 at 7:42 AM, Elliott Sprehn espr...@chromium.org
 wrote:
  That actually seems pretty similar to what we have, ours is in the form
 of:
 
  Node#insertedInto(Node insertionPoint)
  Node#removedFrom(Node insertionPoint)
 
  where insertionPoint is the ancestor in the tree where a connection was
  added or removed which may be arbitrarily far up the ancestor chain.
 From
  that you can figure out all the cases Boris is describing.

 Cool. So maybe the DOM specification needs to be updated to have that
 model and we should expose that as low-level hook to web developers.


 We'd consider adding a new MutationObserver type, we'd prefer not to add
 any more tree mutation callbacks. Anything you can do with those you can do
 with an ancestorChanged record type and takeRecords().


We should think hard before adding ancestor information to
MutationObservers; like other DOM-tree-related APIs, they are very much
based around the model of information coming up the tree from below (you
can imagine how odd it would be to add event dispatch that flowed down the
tree towards the leaves).

- Adam


Re: Privileged context features and JavaScript

2015-04-17 Thread Adam Klein
On Fri, Apr 17, 2015 at 7:06 AM, Boris Zbarsky bzbar...@mit.edu wrote:

 On 4/17/15 3:38 AM, Elliott Sprehn wrote:

 It's preferable not to do that for us because you can then create a
 static heap snapshot at compile time and memcpy to start JS contexts
 faster.


 For this specific case, where there are only two possibilities (privileged
 or not) it seems like you can just have two different snapshots and pick
 the right one.  I agree this uses more memory; there are all sorts of
 tradeoffs here.

 But yes, this is an argument against having any sort of dynamic behavior.
 In the case of Gecko, we have to have _something_ somewhat dynamic anyway,
 since we expose APIs to extensions that we don't expose to web pages...
 which I'm sure you do too.


In Blink, we don't include host objects in our snapshot anyway, so this
point is probably moot for web APIs. Or at least it's moot until more
things are self-hosted.

- Adam


Re: Writing spec algorithms in ES6?

2015-06-11 Thread Adam Klein
On Thu, Jun 11, 2015 at 1:32 PM, Dimitri Glazkov dglaz...@google.com
wrote:

 Folks,

 Many specs nowadays opt for a more imperative method of expressing
 normative requirements, and using algorithms. For example, both HTML and
 DOM spec do the run following steps list that looks a lot like
 pseudocode, and the Web components specs use their own flavor of
 prose-pseudo-code.

 I wonder if it would be good the pseudo-code would actually be ES6, with
 comments where needed?

 I noticed that the CSS Color Module Level 4 actually does this, and it
 seems pretty nice:
 http://dev.w3.org/csswg/css-color/#dom-rgbcolor-rgbcolorcolor

 WDYT?


I love the idea of specifying algorithms in something other than English.
But I'm afraid that ECMAScript is not a good language for this purpose, for
the same reasons Boris cites in his response (which arrived as I was typing
this).

- Adam


Re: TPAC Topic: Using ES2015 Modules in HTML

2015-10-16 Thread Adam Klein
Consider this a second vote for a California-compatible time for such a
discussion. V8 also has a partial ES2015 module implementation that's
blocked on the loader, and I'm very interested in pushing forward support
for modules in HTML.

On Fri, Oct 16, 2015 at 5:39 AM, Ryosuke Niwa  wrote:

> Hi all,
>
> Can we discuss how we can integrate ES2015 modules into HTML on Tuesday,
> October 27th at TPAC?
>
> Both Gecko and WebKit are basically done implementing ES6 module supports
> in their respective JavaScript engines but blocked on
> http://whatwg.github.io/loader/ to support in web contents.
>
> Since my colleague who is interested in this topic cannot attend TPAC in
> person, it would be great if we could have it in Tuesday Morning (Monday
> evening in California).
>
> - R. Niwa
>
>