Re: DOM Mutation Events Replacement: When to deliver mutations

2011-09-08 Thread Sean Hogan

On 8/09/11 8:57 AM, Travis Leithead wrote:

When I proposed watchSelector [2], the idea was clearly to propose an option 
that provided semantics similar to Option 4 as described here.
The primary benefits I sought were:
Pros:
* batching - allow a script to operate on the DOM's cumulative changes, vs. 
incremental changes.
* filtering - provide a well-known mechanism for quickly and precisely 
identifying the nodes in the document that should be observed.
* performance - fully async has the potential to very fast to implement


Hi Travis,

I like the watchSelector proposal and think it would combine well with 
the independent shadow DOM proposal to facilitate Javascript 
implementations of (something like) XBL2.


The *batching* described in the proposal seems to allow out-of-order and 
even post-relayout mutation notifications. I don't think this feature 
would be used, nor that it makes much of a performance difference 
relative to the *filtering* implicit in your proposal.


Could you give more details on why fully async would be very fast 
relative to other solutions?



I think the filtering benefit could be extended to either Option 2 or 3.
I prefer Option 3 because if offer a larger opportunity for batching.

Cons:
* See previously stated use case as argument against this approach
* The approach didn't account for node movement within the document 
(reparenting of elements).


I would like to have watchSelector AND mutation events (or their 
replacement). Maybe you do too?



* The approach (using Selectors) was deemed too risky because web developers 
can provide complex selectors that make running the mutation detection algorithm 
expensive for the UA.


Could you explain how this could be more expensive than what the browser 
already does with handling CSS?



regards,
Sean




Re: DOM Mutation Events Replacement: When to deliver mutations

2011-09-07 Thread Travis Leithead
On 08/11/2011 03:44 AM, Rafael Weinstein wrote:
 Although everyone seems to agree that mutations should be delivered
 after the DOM operations which generated them complete, the question
 remains:

When, exactly, should mutations be delivered?

 The four options I'm aware of are:

 1) Immediately - i.e. while the operation is underway. [Note: This is
 how current DOM Mutation events work].

 2) Upon completion of the outer-most DOM operation. i.e. Immediately
 before a the lowest-on-the-stack DOM operation returns, but after it
 has done all of its work.

 3) At the end of the current Task. i.e. immediately before the UA is
 about to fetch a new Task to run.

 4) Scheduled as a future Task. i.e. fully async.

 ---

 Discussion:

 Options 1  4 are don't seem to have any proponents that I know of, so 
 briefly:

 Option 1, Immediately:

 Pro:
 -It's conceptually the easiest thing to understand. The following *always* 
 hold:
-For calling code: When any DOM operation I make completes, all
 observers will have run.
-For notified code: If I'm being called, the operation which caused
 this is below me on the stack.

 Con:
 -Because mutations must be delivered for some DOM operations before
 the operation is complete, UAs must tolerate all ways in which script
 may invalidate their assumptions before they do further work.


 Option 4, Scheduled as a future Task:

 Pro:
 -Conceptually easy to understand
 -Easy to implement.

 Con:
 -It's too late. Most use cases for mutation observation require that
 observers run before a paint occurs. E.g. a widget library which
 watches for special attributes. Script may create adiv
 class=FooButton  and an observer will react to this by decorating
 the div as a FooButton. It is unacceptable (creates visual
 artifacts/flickering) to have the div be painted before the widget
 library has decorated it as a FooButton.

 Both of these options appear to be non-starters. Option 1 has been
 shown by experience to be an unreasonable implementation burden for
 UAs. Option 4 clearly doesn't handle properly important use cases.



When I proposed watchSelector [2], the idea was clearly to propose an option 
that provided semantics similar to Option 4 as described here.
The primary benefits I sought were:
Pros:
* batching - allow a script to operate on the DOM's cumulative changes, vs. 
incremental changes.
* filtering - provide a well-known mechanism for quickly and precisely 
identifying the nodes in the document that should be observed.
* performance - fully async has the potential to very fast to implement

I think the filtering benefit could be extended to either Option 2 or 3.
I prefer Option 3 because if offer a larger opportunity for batching.

Cons:
* See previously stated use case as argument against this approach
* The approach didn't account for node movement within the document 
(reparenting of elements).
* The approach (using Selectors) was deemed too risky because web developers 
can provide complex selectors that make running the mutation detection 
algorithm expensive for the UA.

I still like the idea of using selectors, and after a conversation with Jonas, 
I realized that the perf concern can be eliminated yet preserve the filtering 
benefit by only allowing what CSS calls simple selectors [3], which are 
basically attribute/class/id selectors w/out combinators. Text mutations are 
generally in an entirely different category (primarily editing scenarios), and 
obviously won't work with Selectors. Additionally, the context information for 
text nodes is different than for elements, which leads me to believe that for 
text mutations, some different API may be necessary.



 ---

 Options 2  3 have proponents. Since I'm one of them (a proponent),
 I'll just summarize the main *pro* arguments for each and invite those
 who wish (including myself), to weigh in with further support or
 criticism in follow-on emails.


 Option 2: Upon completion of the outer-most DOM operation.

 Pro:
 -It's conceptually close to fully synchronous. For simple uses
 (specifically, setting aside the case of making DOM operations within
 a mutation callback), it has the advantages of Option 1, without its
 disadvantages. Because of this, it's similar to the behavior of
 current Mutation Events.


Pros:
* May be very easy to migrate existing sites/apps from current mutation events 
code to this option. If the message of the new mutation events model we create 
is we finally fixed mutation events, please use this new code instead then 
the easier we make the transition to the new model the better.

However, in my experience websites and apps don't just migrate because the new 
model is available. Web sites generally follow the pattern: if it isn't broke, 
don't fix it. To me, this means that classic Mutation events aren't going 
away, and unless the new model provides significant advantages over the old 
model, legacy code just won't update. It's really the new code that we're 

Re: DOM Mutation Events Replacement: Findings from implementing net-effect projections

2011-08-22 Thread Ryosuke Niwa
On Wed, Aug 17, 2011 at 3:17 AM, Olli Pettay olli.pet...@helsinki.fiwrote:

 On 08/17/2011 04:54 AM, Rafael Weinstein wrote:

 TL;DR;

 1) ObserveSubtree semantics doesn't provide a robust mechanism for
 observing a tree/fragment, and if we don't provide something more
 complete, libraries will likely register observers at every node in
 the document.


 ModificationBatch approach should provide as much information as
 current mutation events.


No, the problem is that the list of mutations is given asynchronously in
some cases.

Say you have the following DOM tree:
body
div
span class=special
#text('hello')

and div is removed.  At this point, the list of mutations we have is:
(ChildlistChanged, body, div)
where div has the subtree:
span class=special
#text('hello')

If observers (e.g. of widget library) wanted to detect whether any span with
the special classname has been removed from the document, it can do so
when it receives this list of mutations in this case.

But now suppose that another observer of this mutation list decides to
remove span from div's child list before my observer sees the list of
mutations.  Oops!  I don't have any idea whether span was removed or not
because all I get to see is (ChildlistChanged, body, div) and div doesn't
have any children.

Sure, this issue is prevalent in the existing mutation events but it's a
good use case to address.  In fact, providing a way to work-around this
issue (by providing means to watch all nodes with the same owner document)
might provide a strong incentive for authors to start using our new API.

But is that a common enough case which the API needs to handle.
 I would think that a script library which wants to handle such case,
 can just use the API to observe everything.


Recall the distributed editing use cases pointed by Dave Raggett for
example:
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0416.html
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0381.html

- Ryosuke


Re: DOM Mutation Events Replacement: Findings from implementing net-effect projections

2011-08-17 Thread Olli Pettay

On 08/17/2011 04:54 AM, Rafael Weinstein wrote:

TL;DR;

1) ObserveSubtree semantics doesn't provide a robust mechanism for
observing a tree/fragment, and if we don't provide something more
complete, libraries will likely register observers at every node in
the document.


ModificationBatch approach should provide as much information as
current mutation events.





2) Not providing position information (e.g childlistIndex) in
ChildlistChanged mutations means that the algorithmic complexity of
computing whether/where nodes have moved to doesn't improve vs. having
to bruce-force compute.


Yeah, I think we really to provide some position information.
Adding index is easy (although it can be slow in some implementations,
but that is implementation specific thing)
I'll add this to ModificationBatch.



3) Not providing lastValue for text changes (attribute, textNode,
etc...) may adversely impact some use cases.


I agree.






---
Following up on points 6  7 from
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html
...

I've prototyped the main net-effect projections that I believe
various use cases will need. I've published to code to this public
github repository:

https://github.com/rafaelw/MutationProjections

I encourage anyone interested to check my work and make corrections
and/or suggestions, or create a branch that experiments in any
direction they see fit.

---
Per point (3) in the email above, we've concluded that mutations
should be delivered in batches, since any number of mutations can have
taken place since an observer was notified.

Also, though it varies, there's generally a hierarchy of expense for
types of operations done from JS:

   reads/writes on pure JS data  DOM Reads  DOM Writes  External I/O

Often, an application is willing to do a some amount of work of a less
expensive operation to avoid doing any work of a more expensive
operation.

Thus, a mutation observer is highly motivated, for both correctness
and performance, to answer the question:

   What is the net-effect of some set of DOM changes that have taken
place since I last got to run?

---
Depending on the use case, what happened likely means some
combination of the following:

1) Reachability: What nodes were added to or removed from the document?

2) Matching: What nodes stopped or started matching a given pattern?
This could be arbitrarily complex, but in the use cases discussed,
this usually means a simple selector.

3) Parent changes: What nodes are now children of a new parent node?

4) Movement: What nodes moved to a new position in the document?

5) Value change: What character/text or attribute nodes changed value
(for attributes, also, whether the attribute was added or removed)?

Note that none of the above requires any mechanism of mutation
observation. All can be computed given the opportunity to inspect the
DOM at two points in time.

However, without any information about what happened, the expense of
answering each of the above questions is roughly proportional to the
number of nodes in the document. All are roughly linear, except (4)
Movement, which is roughly quadratic.

It seems to me that a good goal for any mutation event replacement
should be that the expense of answering the above projections be
proportional the number of mutations which actually took place.

I've implemented the null case (lacking any mutation information)
for 1, 3  4 here:

https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L39

And the same projections, but taking mutation records to improve
performance here:

https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L240

---
Observing sets of nodes

In order to implement the above projections, I prototyped:

   Document.observeOwnedNodes

I'm not proposing this (yet), but I think it is conceptually the most
general case (btw, to give credit to Jonas, he pointed out that this
might be solution to the problem I'm about to describe, though I'm
pretty sure he meant sometime down the road).


I'm not quite sure what all the Document.observeOwnedNodes approach
tries to observe. I assume it should be observing everything, all the 
changes to child nodes and also changes to attributes.

(currently it handles only elements)
Is that really needed?




The problem is that observeSubtree doesn't report anything about what
happened to a node that was removed from the document, modified, then
returned to the document. Consider the following case:

-Register a subtreeObserver at the root of the document
-Some other code removes an element, appends a new element to it,
modifies an attribute, then returns the node to the document.

Now, in order to properly compute the above projections, we are nearly
back to the null case (matching becomes proportional the number of
descendants of added/removed nodes that we heard about, but everything
else returns to being proportional to the number of nodes in the

Re: DOM Mutation Events Replacement: Findings from implementing net-effect projections

2011-08-17 Thread Rafael Weinstein
On Wed, Aug 17, 2011 at 3:17 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 08/17/2011 04:54 AM, Rafael Weinstein wrote:

 TL;DR;

 1) ObserveSubtree semantics doesn't provide a robust mechanism for
 observing a tree/fragment, and if we don't provide something more
 complete, libraries will likely register observers at every node in
 the document.

 ModificationBatch approach should provide as much information as
 current mutation events.




 2) Not providing position information (e.g childlistIndex) in
 ChildlistChanged mutations means that the algorithmic complexity of
 computing whether/where nodes have moved to doesn't improve vs. having
 to bruce-force compute.

 Yeah, I think we really to provide some position information.
 Adding index is easy (although it can be slow in some implementations,
 but that is implementation specific thing)
 I'll add this to ModificationBatch.


 3) Not providing lastValue for text changes (attribute, textNode,
 etc...) may adversely impact some use cases.

 I agree.





 ---
 Following up on points 6  7 from
 http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html
 ...

 I've prototyped the main net-effect projections that I believe
 various use cases will need. I've published to code to this public
 github repository:

 https://github.com/rafaelw/MutationProjections

 I encourage anyone interested to check my work and make corrections
 and/or suggestions, or create a branch that experiments in any
 direction they see fit.

 ---
 Per point (3) in the email above, we've concluded that mutations
 should be delivered in batches, since any number of mutations can have
 taken place since an observer was notified.

 Also, though it varies, there's generally a hierarchy of expense for
 types of operations done from JS:

   reads/writes on pure JS data  DOM Reads  DOM Writes  External I/O

 Often, an application is willing to do a some amount of work of a less
 expensive operation to avoid doing any work of a more expensive
 operation.

 Thus, a mutation observer is highly motivated, for both correctness
 and performance, to answer the question:

   What is the net-effect of some set of DOM changes that have taken
 place since I last got to run?

 ---
 Depending on the use case, what happened likely means some
 combination of the following:

 1) Reachability: What nodes were added to or removed from the document?

 2) Matching: What nodes stopped or started matching a given pattern?
 This could be arbitrarily complex, but in the use cases discussed,
 this usually means a simple selector.

 3) Parent changes: What nodes are now children of a new parent node?

 4) Movement: What nodes moved to a new position in the document?

 5) Value change: What character/text or attribute nodes changed value
 (for attributes, also, whether the attribute was added or removed)?

 Note that none of the above requires any mechanism of mutation
 observation. All can be computed given the opportunity to inspect the
 DOM at two points in time.

 However, without any information about what happened, the expense of
 answering each of the above questions is roughly proportional to the
 number of nodes in the document. All are roughly linear, except (4)
 Movement, which is roughly quadratic.

 It seems to me that a good goal for any mutation event replacement
 should be that the expense of answering the above projections be
 proportional the number of mutations which actually took place.

 I've implemented the null case (lacking any mutation information)
 for 1, 3  4 here:


 https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L39

 And the same projections, but taking mutation records to improve
 performance here:


 https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L240

 ---
 Observing sets of nodes

 In order to implement the above projections, I prototyped:

   Document.observeOwnedNodes

 I'm not proposing this (yet), but I think it is conceptually the most
 general case (btw, to give credit to Jonas, he pointed out that this
 might be solution to the problem I'm about to describe, though I'm
 pretty sure he meant sometime down the road).

 I'm not quite sure what all the Document.observeOwnedNodes approach
 tries to observe. I assume it should be observing everything, all the
 changes to child nodes and also changes to attributes.
 (currently it handles only elements)
 Is that really needed?

It's not fully implemented in the shim I created, in that it only
reports ChildlistChanged mutations.

It's purpose is to provide a single registration that allows you to be
notified of all changes that happen to all nodes owned by this
document, regardless of whether they are current in the document.




 The problem is that observeSubtree doesn't report anything about what
 happened to a node that was removed from the document, modified, then
 returned to the document. Consider the following case:

 

Re: Mutation events replacement

2011-08-17 Thread Olli Pettay

Here are some tryserver builds.
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/opet...@mozilla.com-e57a1a317f25/

The default is mostly-sync approach,
but if one sets dom.AlmostAsyncModificationBatch to true
(load about:config, right click, add new boolean),
and restarts the browser, callbacks are called at the end of the task.

API is 
http://hg.mozilla.org/try/file/23ac4760d571/dom/interfaces/core/nsIDOMModificationBatch.idl

and a very simple test page
http://mozilla.pettay.fi/modificationbatchtest.html
The API doesn't have the index of added/removed child node,
but that could be added easily.


-Olli




On 08/10/2011 10:49 PM, Olli Pettay wrote:

FYI, I'm planning to implement the proposal (using vendor prefixed API)
so that I can create tryserver builds.
I'll post the links to builds here later, hopefully in a few days, when
I find the time to do the actual implementation.


-Olli


On 08/04/2011 04:38 PM, Olli Pettay wrote:

Hi all,

here is a bit different proposal for mutation events replacement.
This is using the mostly-sync approach, and batching can make it easier
to use with several simultaneous script libraries; each one would use
their own batch.
For performance reasons it might be useful to have an attribute name
filter for AttrChange, but such thing could be added later.
One advantage this approach has is that it doesn't pollute Node API.


-Olli




interface Modification {
/**
* type is either TextChange, ChildListChange or AttrChange.
* (More types can be added later if needed.)
*/
readonly attribute DOMString type;

/**
* Target of the change.
* If an attribute is changed, target is the element,
* if an element is added or removed, target is the node
* which was added or removed.
* If text is changed, target is the CharacterData node which was
* changed.
*/
readonly attribute Node target;
/**
* parent node of the target right before the change happened,
* or null.
*/
readonly attribute Node targetParent;
/**
* The node which is batching the change.
*/
readonly attribute Node currentTarget;

/**
* The name of the attribute which was changed, or null.
*/
readonly attribute DOMString attrName;

/*
* The previous value of the attribute or CharacterData node, or null.
* If a new attribute is added, prevValue is null.
*/
readonly attribute DOMString prevValue;

/*
* The new value of the attribute or CharacterData node, or null.
* If an attribute is removed, newValue is null.
*/
readonly attribute DOMString newValue;
};

[Callback, NoInterfaceObject]
interface ModificationCallback {
void handleBatch(in ModificationBatch aBatch);
};

[Constructor(in ModificationCallback aDoneCallback)]
interface ModificationBatch {
/**
* Modifications is non-empty array only while aDoneCallback
* is called. And while that happens, modifications list doesn't
* change.
*/
readonly attribute Modification[] modifications;

void batchTextChanges(in Node aNode);
void unbatchTextChanges(in Node aNode);

void batchChildListChanges(in Node aNode);
void unbatchChildListChanges(in Node aNode);

void batchAttrChanges(in Node aNode);
void unbatchAttrChanges(in Node aNode);

void batchAll();
void unbatchAll();
};

aDoneCallback is called right before the call which is modifying DOM
returns. If aDoneCallback modifies DOM, new modifications list will be
collected
and callbacks will be called right after the initial aDoneCallback
returns.
ModificationBatches are always handled in the order they are *created*.
Callbacks are never called if modifications list is empty.


Example 1:
// log all the attribute changes
var o = new ModificationBatch(function(b) {
for (var i = 0; i  b.modifications.length; ++i) {
var m = b.modifications[i];
if (m.prevValue == null) {
console.log(m.attrName +  added);
} else if (m.newValue == null) {
console.log(m.attrName +  removed);
} else {
console.log(m.attrName +  modified);
}
}
}
);
o.batchAttrChanges(document);













Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-16 Thread Anne van Kesteren
On Mon, 15 Aug 2011 14:23:10 +0200, Anne van Kesteren ann...@opera.com  
wrote:
Since there seems to be consensus to not do either Immediately or New  
task should I remove those from  
http://wiki.whatwg.org/wiki/Modifications now? It is fine with me if  
someone else does it too.


I instead added a new section upfront that attempts to summarize the  
debate thus far:


http://wiki.whatwg.org/wiki/Modifications#Invoking_Modification_Listeners

What we are missing is input from library authors (though I suppose we are  
getting some here from Google, that is not entirely clear to me). Is there  
a pain with mutation events for them or is that restricted to UA  
implementors?


The end of task approach has a problem with showModalDialog(). Is this  
going to be a problem with dialog modal as well? Any other constructs?  
showModalDialog() may be bad practice, but dialog modal is not.



In addition to that I think it would be great if Rafael could expand on  
point 6 and 7 made in  
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html so  
that we can hash that out as well against what Olli has proposed thus far.



--
Anne van Kesteren
http://annevankesteren.nl/



Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-16 Thread Ryosuke Niwa
On Mon, Aug 15, 2011 at 5:23 AM, Anne van Kesteren ann...@opera.com wrote:

 Since there seems to be consensus to not do either Immediately or New
 task should I remove those from http://wiki.whatwg.org/wiki/**
 Modifications http://wiki.whatwg.org/wiki/Modifications now? It is fine
 with me if someone else does it too.


That sounds great!  Thanks for doing that.

On Tue, Aug 16, 2011 at 8:21 AM, Anne van Kesteren ann...@opera.com wrote:

 What we are missing is input from library authors (though I suppose we are
 getting some here from Google, that is not entirely clear to me). Is there a
 pain with mutation events for them or is that restricted to UA implementors?


Yeah, that's my feeling as well.  We need more input from application and
library authors.

- Ryosuke


DOM Mutation Events Replacement: Findings from implementing net-effect projections

2011-08-16 Thread Rafael Weinstein
TL;DR;

1) ObserveSubtree semantics doesn't provide a robust mechanism for
observing a tree/fragment, and if we don't provide something more
complete, libraries will likely register observers at every node in
the document.

2) Not providing position information (e.g childlistIndex) in
ChildlistChanged mutations means that the algorithmic complexity of
computing whether/where nodes have moved to doesn't improve vs. having
to bruce-force compute.

3) Not providing lastValue for text changes (attribute, textNode,
etc...) may adversely impact some use cases.

---
Following up on points 6  7 from
http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html
...

I've prototyped the main net-effect projections that I believe
various use cases will need. I've published to code to this public
github repository:

https://github.com/rafaelw/MutationProjections

I encourage anyone interested to check my work and make corrections
and/or suggestions, or create a branch that experiments in any
direction they see fit.

---
Per point (3) in the email above, we've concluded that mutations
should be delivered in batches, since any number of mutations can have
taken place since an observer was notified.

Also, though it varies, there's generally a hierarchy of expense for
types of operations done from JS:

  reads/writes on pure JS data  DOM Reads  DOM Writes  External I/O

Often, an application is willing to do a some amount of work of a less
expensive operation to avoid doing any work of a more expensive
operation.

Thus, a mutation observer is highly motivated, for both correctness
and performance, to answer the question:

  What is the net-effect of some set of DOM changes that have taken
place since I last got to run?

---
Depending on the use case, what happened likely means some
combination of the following:

1) Reachability: What nodes were added to or removed from the document?

2) Matching: What nodes stopped or started matching a given pattern?
This could be arbitrarily complex, but in the use cases discussed,
this usually means a simple selector.

3) Parent changes: What nodes are now children of a new parent node?

4) Movement: What nodes moved to a new position in the document?

5) Value change: What character/text or attribute nodes changed value
(for attributes, also, whether the attribute was added or removed)?

Note that none of the above requires any mechanism of mutation
observation. All can be computed given the opportunity to inspect the
DOM at two points in time.

However, without any information about what happened, the expense of
answering each of the above questions is roughly proportional to the
number of nodes in the document. All are roughly linear, except (4)
Movement, which is roughly quadratic.

It seems to me that a good goal for any mutation event replacement
should be that the expense of answering the above projections be
proportional the number of mutations which actually took place.

I've implemented the null case (lacking any mutation information)
for 1, 3  4 here:

https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L39

And the same projections, but taking mutation records to improve
performance here:

https://github.com/rafaelw/MutationProjections/blob/master/projections.js#L240

---
Observing sets of nodes

In order to implement the above projections, I prototyped:

  Document.observeOwnedNodes

I'm not proposing this (yet), but I think it is conceptually the most
general case (btw, to give credit to Jonas, he pointed out that this
might be solution to the problem I'm about to describe, though I'm
pretty sure he meant sometime down the road).

The problem is that observeSubtree doesn't report anything about what
happened to a node that was removed from the document, modified, then
returned to the document. Consider the following case:

-Register a subtreeObserver at the root of the document
-Some other code removes an element, appends a new element to it,
modifies an attribute, then returns the node to the document.

Now, in order to properly compute the above projections, we are nearly
back to the null case (matching becomes proportional the number of
descendants of added/removed nodes that we heard about, but everything
else returns to being proportional to the number of nodes in the
document).

You may consider this to be an esoteric case, but imagine the
situation that library authors will be in. They will have two basic
options:

a) Use observeSubtree and explain to their users that they have to be
careful never to modify something outside the tree.
b) Simply register an observer at all nodes in the document.

Picking the second option means that my library works properly and I
don't need my users to be careful. It burns plenty of memory, but
library authors aren't well known for being conservative in that
regard -- especially since its pretty hard for them to quantify the
memory impact of anything.

The danger is 

Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-15 Thread Anne van Kesteren
On Sat, 13 Aug 2011 17:41:32 +0200, Anne van Kesteren ann...@opera.com  
wrote:
I created http://wiki.whatwg.org/wiki/Modifications based on your email  
and the reply from Olli. It probably needs a bit more context.


I will try to contact the relevant people at Opera to see if we have any  
input in the matter. From the perspective of the DOM specification  
option 2 would be easiest, but that is not really a good argument either  
way.


It does not matter much to Opera.

I would personally prefer it if we could stay away from tasks so the  
definition of modification listeners can be fully contained by node and  
range modification methods.


Since there seems to be consensus to not do either Immediately or New  
task should I remove those from http://wiki.whatwg.org/wiki/Modifications  
now? It is fine with me if someone else does it too.



--
Anne van Kesteren
http://annevankesteren.nl/



Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-13 Thread Anne van Kesteren
On Thu, 11 Aug 2011 02:44:32 +0200, Rafael Weinstein rafa...@google.com  
wrote:

Although everyone seems to agree that mutations should be delivered
after the DOM operations which generated them complete, the question
remains:

  When, exactly, should mutations be delivered?


I created http://wiki.whatwg.org/wiki/Modifications based on your email  
and the reply from Olli. It probably needs a bit more context.


I will try to contact the relevant people at Opera to see if we have any  
input in the matter. From the perspective of the DOM specification option  
2 would be easiest, but that is not really a good argument either way.



--
Anne van Kesteren
http://annevankesteren.nl/



Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-11 Thread Olli Pettay

On 08/11/2011 03:44 AM, Rafael Weinstein wrote:

Although everyone seems to agree that mutations should be delivered
after the DOM operations which generated them complete, the question
remains:

   When, exactly, should mutations be delivered?

The four options I'm aware of are:

1) Immediately - i.e. while the operation is underway. [Note: This is
how current DOM Mutation events work].

2) Upon completion of the outer-most DOM operation. i.e. Immediately
before a the lowest-on-the-stack DOM operation returns, but after it
has done all of its work.

3) At the end of the current Task. i.e. immediately before the UA is
about to fetch a new Task to run.

4) Scheduled as a future Task. i.e. fully async.

---

Discussion:

Options 1  4 are don't seem to have any proponents that I know of, so briefly:

Option 1, Immediately:

Pro:
-It's conceptually the easiest thing to understand. The following *always* hold:
   -For calling code: When any DOM operation I make completes, all
observers will have run.
   -For notified code: If I'm being called, the operation which caused
this is below me on the stack.

Con:
-Because mutations must be delivered for some DOM operations before
the operation is complete, UAs must tolerate all ways in which script
may invalidate their assumptions before they do further work.


Option 4, Scheduled as a future Task:

Pro:
-Conceptually easy to understand
-Easy to implement.

Con:
-It's too late. Most use cases for mutation observation require that
observers run before a paint occurs. E.g. a widget library which
watches for special attributes. Script may create adiv
class=FooButton  and an observer will react to this by decorating
the div as a FooButton. It is unacceptable (creates visual
artifacts/flickering) to have the div be painted before the widget
library has decorated it as a FooButton.

Both of these options appear to be non-starters. Option 1 has been
shown by experience to be an unreasonable implementation burden for
UAs. Option 4 clearly doesn't handle properly important use cases.

---

Options 2  3 have proponents. Since I'm one of them (a proponent),
I'll just summarize the main *pro* arguments for each and invite those
who wish (including myself), to weigh in with further support or
criticism in follow-on emails.


Option 2: Upon completion of the outer-most DOM operation.

Pro:
-It's conceptually close to fully synchronous. For simple uses
(specifically, setting aside the case of making DOM operations within
a mutation callback), it has the advantages of Option 1, without its
disadvantages. Because of this, it's similar to the behavior of
current Mutation Events.


Pro:
Semantics are consistent: delivery happens right before the
outermost DOM operation returns.

Easier transition from mutation events to the new API.

Not bound to tasks. Side effects, like problems related
to spinning event loop are per mutation callback, not
per whole task.





Option 3: At the end of the current Task.

Pro:
-No code is at risk for having its assumptions invalidated while it is
trying to do work. All participants (main application script,
libraries which are implemented using DOM mutation observation) are
allowed to complete whatever work (DOM operations) they wish before
another participant starts doing work.




Con:
Since the approach is bound to tasks, it is not clear what should happen
if event loop spins while handling the task. What if some other task
modifies the DOM[1], when should the mutation callbacks fire?
Because of this issue, tasks, which may spin event loop, should not
also modify DOM since that may cause some unexpected result.

Callback handling is moved far away from the actual mutation.


Pro:
Can batch more, since the callbacks are called later than in
option 2.


-Olli


[1] showModalDialog(javascript:opener.document.body.textContent = '';, 
, );




Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-11 Thread Rafael Weinstein
Thanks Olli. I think this is now a fairly complete summary of the
issues identified thus far.

It'd be great to get some additional views -- in particular from folks
representing UAs that haven't yet registered any observations or
opinons.

Note: I think what Olli has listed is fair, but I'm concerned that
because of terminology (consistent vs inconsistent semantics),
others may be confused. I'm going to clarify a bit. I believe my
comments should be uncontroversial. Olli (or anyone else), please
correct me if this isn't so.

On Thu, Aug 11, 2011 at 2:02 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 08/11/2011 03:44 AM, Rafael Weinstein wrote:

 Although everyone seems to agree that mutations should be delivered
 after the DOM operations which generated them complete, the question
 remains:

   When, exactly, should mutations be delivered?

 The four options I'm aware of are:

 1) Immediately - i.e. while the operation is underway. [Note: This is
 how current DOM Mutation events work].

 2) Upon completion of the outer-most DOM operation. i.e. Immediately
 before a the lowest-on-the-stack DOM operation returns, but after it
 has done all of its work.

 3) At the end of the current Task. i.e. immediately before the UA is
 about to fetch a new Task to run.

 4) Scheduled as a future Task. i.e. fully async.

 ---

 Discussion:

 Options 1  4 are don't seem to have any proponents that I know of, so
 briefly:

 Option 1, Immediately:

 Pro:
 -It's conceptually the easiest thing to understand. The following *always*
 hold:
   -For calling code: When any DOM operation I make completes, all
 observers will have run.
   -For notified code: If I'm being called, the operation which caused
 this is below me on the stack.

 Con:
 -Because mutations must be delivered for some DOM operations before
 the operation is complete, UAs must tolerate all ways in which script
 may invalidate their assumptions before they do further work.


 Option 4, Scheduled as a future Task:

 Pro:
 -Conceptually easy to understand
 -Easy to implement.

 Con:
 -It's too late. Most use cases for mutation observation require that
 observers run before a paint occurs. E.g. a widget library which
 watches for special attributes. Script may create adiv
 class=FooButton  and an observer will react to this by decorating
 the div as a FooButton. It is unacceptable (creates visual
 artifacts/flickering) to have the div be painted before the widget
 library has decorated it as a FooButton.

 Both of these options appear to be non-starters. Option 1 has been
 shown by experience to be an unreasonable implementation burden for
 UAs. Option 4 clearly doesn't handle properly important use cases.

 ---

 Options 2  3 have proponents. Since I'm one of them (a proponent),
 I'll just summarize the main *pro* arguments for each and invite those
 who wish (including myself), to weigh in with further support or
 criticism in follow-on emails.


 Option 2: Upon completion of the outer-most DOM operation.

 Pro:
 -It's conceptually close to fully synchronous. For simple uses
 (specifically, setting aside the case of making DOM operations within
 a mutation callback), it has the advantages of Option 1, without its
 disadvantages. Because of this, it's similar to the behavior of
 current Mutation Events.

 Pro:
 Semantics are consistent: delivery happens right before the
 outermost DOM operation returns.

This statement is true. When I described Option 2 (perhaps too
harshly) as having inconsistent semantics, I was referrer only to
the expectations of Callers and Observers. To be totally clear:

Parties:

Caller = any code which performers a DOM operation which triggers a mutation.
Observer = any code to whom the mutation is delivered.

Expectations for synchrony:

Caller: When any DOM operation I make completes, all observers will
have been notified.
Observer: If I'm being notified, the Caller which triggered the
mutation is below me on the stack.

Parties:   Caller  Observer
Options
1:Always   Always
2:Sometimes(a) Sometimes(a)
3:Never Never
4:Never Never

(a) True when Caller is run outside of a mutation observer callback.
False when Caller is run inside a mutation observer callback.


 Easier transition from mutation events to the new API.

 Not bound to tasks. Side effects, like problems related
 to spinning event loop are per mutation callback, not
 per whole task.




 Option 3: At the end of the current Task.

 Pro:
 -No code is at risk for having its assumptions invalidated while it is
 trying to do work. All participants (main application script,
 libraries which are implemented using DOM mutation observation) are
 allowed to complete whatever work (DOM operations) they wish before
 another participant starts doing work.



 Con:
 Since the approach is bound to tasks, it is not clear what should happen
 if event loop spins while handling the task. What 

Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-11 Thread Olli Pettay

On 08/11/2011 06:13 PM, Rafael Weinstein wrote:

Con:
Since the approach is bound to tasks, it is not clear what should happen
if event loop spins while handling the task. What if some other task
modifies the DOM[1], when should the mutation callbacks fire?
Because of this issue, tasks, which may spin event loop, should not
also modify DOM since that may cause some unexpected result.


I think the *pro* side of this you listed is more fair. Both Options 2
  3 must answer this question. It's true that because Option 3 is
later, it sort of has this issue more.

And it has a lot more. Since for example when handling an event, all
the listeners for it are called in the same task and if one event
listener modifies DOM and some other spins event loop, it is hard to
see what is causing the somewhat unexpected behavior.




However, what should happen has been defined. In both cases, if
there are any mutations which are queued for delivery when an inner
event loop spins up, they are *not* delivered inside the inner event
loop. In both Options, they are always delivered in the loop which
queued them.

But what happens when event loop spins within a task, and some
inner task causes new mutations?
We want to notify about mutations in the order they have happened, right?
So if there are pending mutations to notify, the inner task must just
queue notifications to the queue of the outermost task.
This could effectively disable all the mutation callbacks for example 
when a modal dialog (showModalDialog) is open.



Option 2 has similar problem, but *only* while handling mutation 
callbacks, not during the whole task.




-Olli







Callback handling is moved far away from the actual mutation.


Pro:
Can batch more, since the callbacks are called later than in
option 2.


-Olli


[1] showModalDialog(javascript:opener.document.body.textContent = '';, ,
);









Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-11 Thread Rafael Weinstein
On Thu, Aug 11, 2011 at 9:12 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 08/11/2011 06:13 PM, Rafael Weinstein wrote:

 Con:
 Since the approach is bound to tasks, it is not clear what should happen
 if event loop spins while handling the task. What if some other task
 modifies the DOM[1], when should the mutation callbacks fire?
 Because of this issue, tasks, which may spin event loop, should not
 also modify DOM since that may cause some unexpected result.

 I think the *pro* side of this you listed is more fair. Both Options 2
   3 must answer this question. It's true that because Option 3 is
 later, it sort of has this issue more.

 And it has a lot more. Since for example when handling an event, all
 the listeners for it are called in the same task and if one event
 listener modifies DOM and some other spins event loop, it is hard to
 see what is causing the somewhat unexpected behavior.



 However, what should happen has been defined. In both cases, if
 there are any mutations which are queued for delivery when an inner
 event loop spins up, they are *not* delivered inside the inner event
 loop. In both Options, they are always delivered in the loop which
 queued them.

 But what happens when event loop spins within a task, and some
 inner task causes new mutations?
 We want to notify about mutations in the order they have happened, right?

In general, yes. But I believe the idea is that spinning an inner
event loop is an exception. In that case delivering mutations in the
order they were generated will be broken. To be perfectly precise:
Mutations will be delivered in the order they were generated *for and
within any given event loop*.

There's no question this is unfortunate. The case in which the bad
thing happens is you:

-Made some modifications to the main document
-Used showModalDialog
-Modified the opener document from the event loop of showModalDialog
-Got confused because mutations from within the showModalDialog were
delivered before the mutations made before calling it

I suppose this comes down to judgement. Mine is that it's acceptable
for us to not attempt to improve the outcome in this case.

 So if there are pending mutations to notify, the inner task must just
 queue notifications to the queue of the outermost task.
 This could effectively disable all the mutation callbacks for example when a
 modal dialog (showModalDialog) is open.


 Option 2 has similar problem, but *only* while handling mutation callbacks,
 not during the whole task.



 -Olli





 Callback handling is moved far away from the actual mutation.


 Pro:
 Can batch more, since the callbacks are called later than in
 option 2.


 -Olli


 [1] showModalDialog(javascript:opener.document.body.textContent = '';,
 ,
 );








Re: Mutation events replacement

2011-08-10 Thread Olli Pettay

FYI, I'm planning to implement the proposal (using vendor prefixed API)
so that I can create tryserver builds.
I'll post the links to builds here later, hopefully in a few days, when
I find the time to do the actual implementation.


-Olli


On 08/04/2011 04:38 PM, Olli Pettay wrote:

Hi all,

here is a bit different proposal for mutation events replacement.
This is using the mostly-sync approach, and batching can make it easier
to use with several simultaneous script libraries; each one would use
their own batch.
For performance reasons it might be useful to have an attribute name
filter for AttrChange, but such thing could be added later.
One advantage this approach has is that it doesn't pollute Node API.


-Olli




interface Modification {
/**
* type is either TextChange, ChildListChange or AttrChange.
* (More types can be added later if needed.)
*/
readonly attribute DOMString type;

/**
* Target of the change.
* If an attribute is changed, target is the element,
* if an element is added or removed, target is the node
* which was added or removed.
* If text is changed, target is the CharacterData node which was
* changed.
*/
readonly attribute Node target;
/**
* parent node of the target right before the change happened,
* or null.
*/
readonly attribute Node targetParent;
/**
* The node which is batching the change.
*/
readonly attribute Node currentTarget;

/**
* The name of the attribute which was changed, or null.
*/
readonly attribute DOMString attrName;

/*
* The previous value of the attribute or CharacterData node, or null.
* If a new attribute is added, prevValue is null.
*/
readonly attribute DOMString prevValue;

/*
* The new value of the attribute or CharacterData node, or null.
* If an attribute is removed, newValue is null.
*/
readonly attribute DOMString newValue;
};

[Callback, NoInterfaceObject]
interface ModificationCallback {
void handleBatch(in ModificationBatch aBatch);
};

[Constructor(in ModificationCallback aDoneCallback)]
interface ModificationBatch {
/**
* Modifications is non-empty array only while aDoneCallback
* is called. And while that happens, modifications list doesn't
* change.
*/
readonly attribute Modification[] modifications;

void batchTextChanges(in Node aNode);
void unbatchTextChanges(in Node aNode);

void batchChildListChanges(in Node aNode);
void unbatchChildListChanges(in Node aNode);

void batchAttrChanges(in Node aNode);
void unbatchAttrChanges(in Node aNode);

void batchAll();
void unbatchAll();
};

aDoneCallback is called right before the call which is modifying DOM
returns. If aDoneCallback modifies DOM, new modifications list will be
collected
and callbacks will be called right after the initial aDoneCallback returns.
ModificationBatches are always handled in the order they are *created*.
Callbacks are never called if modifications list is empty.


Example 1:
// log all the attribute changes
var o = new ModificationBatch(function(b) {
for (var i = 0; i  b.modifications.length; ++i) {
var m = b.modifications[i];
if (m.prevValue == null) {
console.log(m.attrName +  added);
} else if (m.newValue == null) {
console.log(m.attrName +  removed);
} else {
console.log(m.attrName +  modified);
}
}
}
);
o.batchAttrChanges(document);









Re: Mutation events replacement

2011-08-10 Thread Rafael Weinstein
Awesome. Status update on our side:

-Adam has been researching the implementation implications in Webkit
of a few variants of proposals made here -- with the goal of having
enough understanding to start a discussion on webkit-dev.

-I'm composing an email which attempts to summarize the discussion so
far, the points of broad agreement and the remaining points of
divergence -- with the goal of trying to drive to an initial consensus
on the main design points of a replacement.

-I've been experimenting with implementing various net effect
projections that take sequences of mutations as input -- with the
goal of reporting back on the time/space complexity implications for
variants of what information is provided in the mutation records.

Cheers
Rafael

On Wed, Aug 10, 2011 at 12:49 PM, Olli Pettay olli.pet...@helsinki.fi wrote:
 FYI, I'm planning to implement the proposal (using vendor prefixed API)
 so that I can create tryserver builds.
 I'll post the links to builds here later, hopefully in a few days, when
 I find the time to do the actual implementation.


 -Olli


 On 08/04/2011 04:38 PM, Olli Pettay wrote:

 Hi all,

 here is a bit different proposal for mutation events replacement.
 This is using the mostly-sync approach, and batching can make it easier
 to use with several simultaneous script libraries; each one would use
 their own batch.
 For performance reasons it might be useful to have an attribute name
 filter for AttrChange, but such thing could be added later.
 One advantage this approach has is that it doesn't pollute Node API.


 -Olli




 interface Modification {
 /**
 * type is either TextChange, ChildListChange or AttrChange.
 * (More types can be added later if needed.)
 */
 readonly attribute DOMString type;

 /**
 * Target of the change.
 * If an attribute is changed, target is the element,
 * if an element is added or removed, target is the node
 * which was added or removed.
 * If text is changed, target is the CharacterData node which was
 * changed.
 */
 readonly attribute Node target;
 /**
 * parent node of the target right before the change happened,
 * or null.
 */
 readonly attribute Node targetParent;
 /**
 * The node which is batching the change.
 */
 readonly attribute Node currentTarget;

 /**
 * The name of the attribute which was changed, or null.
 */
 readonly attribute DOMString attrName;

 /*
 * The previous value of the attribute or CharacterData node, or null.
 * If a new attribute is added, prevValue is null.
 */
 readonly attribute DOMString prevValue;

 /*
 * The new value of the attribute or CharacterData node, or null.
 * If an attribute is removed, newValue is null.
 */
 readonly attribute DOMString newValue;
 };

 [Callback, NoInterfaceObject]
 interface ModificationCallback {
 void handleBatch(in ModificationBatch aBatch);
 };

 [Constructor(in ModificationCallback aDoneCallback)]
 interface ModificationBatch {
 /**
 * Modifications is non-empty array only while aDoneCallback
 * is called. And while that happens, modifications list doesn't
 * change.
 */
 readonly attribute Modification[] modifications;

 void batchTextChanges(in Node aNode);
 void unbatchTextChanges(in Node aNode);

 void batchChildListChanges(in Node aNode);
 void unbatchChildListChanges(in Node aNode);

 void batchAttrChanges(in Node aNode);
 void unbatchAttrChanges(in Node aNode);

 void batchAll();
 void unbatchAll();
 };

 aDoneCallback is called right before the call which is modifying DOM
 returns. If aDoneCallback modifies DOM, new modifications list will be
 collected
 and callbacks will be called right after the initial aDoneCallback
 returns.
 ModificationBatches are always handled in the order they are *created*.
 Callbacks are never called if modifications list is empty.


 Example 1:
 // log all the attribute changes
 var o = new ModificationBatch(function(b) {
 for (var i = 0; i  b.modifications.length; ++i) {
 var m = b.modifications[i];
 if (m.prevValue == null) {
 console.log(m.attrName +  added);
 } else if (m.newValue == null) {
 console.log(m.attrName +  removed);
 } else {
 console.log(m.attrName +  modified);
 }
 }
 }
 );
 o.batchAttrChanges(document);










DOM Mutation Events Replacement: The Story So Far / Existing Points of Consensus

2011-08-10 Thread Rafael Weinstein
What follows is an attempt to summarize the (relatively recent)
discussions regarding replacing DOM Mutation Events.

My goals here are to:

-Provide a quick primer for those who haven't read all hundred or so emails.
-Reiterate the aspects of a solution which seem to have broad support.
-Identify the main points of divergence which remain.


Problem: DOM Mutation events as currently specified and implemented
are widely viewed as fatally flawed because they are:

(1) Verbose (fire too often)
(2) Slow (because of event propagation -- and because they prevent
certain UA run-time optimizations)
(3) Crashy (have been the source of many crashers for UAs because
script can tear up the world in any way it wishes while a DOM
operation is underway).


Solution:

*Agreed upon design points:

Primarily because of a proposal made by Jonas Sicking in 2009, the
group has been largely in agreement about the following:

(1) The vocabulary of mutations should be more expressive and require
fewer words to adequately describe what happened . For instance, a
single innerHTML assignment which results in removing N nodes and
inserting M nodes should be expressed as a single mutation (e.g. {
mutation: ChildlistChanged, added: [...], removed: [...] } ) -- not
a sequence of mutations, one for each node inserted or removed.

(2) Mutations should avoid the expense of event propagation (mainly
capture  bubble).

(3) Mutations should be delivered to observers after the DOM
operations which generated them complete -- removing the possibility
of having script interfere with their operation. For example, an
execCommand() operation is permitted to make any  all DOM operations
it needs *before* it has to notify any observers of what happened.

Through discussing Jonas's proposal, we observed that in a system
which allows multiple observers that can, themselves, make mutations,
observers will generally need to be tolerant of an arbitrary number of
mutations having occurred before being notified.

Further, there is strong performance motivation for observers to
respond to the net-effect of a set of mutations, rather than acting
immediately in response to each mutation.

Thus:

(4) Observers should be called with the *set* of mutations which has
occurred since they were last called (or since registration), rather
than being called once per mutation. I.e. Deliver mutations in batches
of everything that has happened since the last time I called you -
up till now.

To my understanding, the most recent proposals made by Jonas, Olli
Pettay, Adam Klein and myself all agree on the above four design
points.


*Design points lacking consensus:

(5) When are mutations delivered? There are four options here, only
two of which have proponents.

=I'm going to try to summarize the discussion on this point in a
separate email.

(6) The semantics for expressing interest in sets of nodes
(7) What information is contained in mutation records

=I've done a survey of the main use cases sited and prototyped the
main net-effect projections. I'll summarize my findings in another
email which attempts to layout the main trade-offs I see so far.



DOM Mutation Events Replacement: When to deliver mutations

2011-08-10 Thread Rafael Weinstein
Although everyone seems to agree that mutations should be delivered
after the DOM operations which generated them complete, the question
remains:

  When, exactly, should mutations be delivered?

The four options I'm aware of are:

1) Immediately - i.e. while the operation is underway. [Note: This is
how current DOM Mutation events work].

2) Upon completion of the outer-most DOM operation. i.e. Immediately
before a the lowest-on-the-stack DOM operation returns, but after it
has done all of its work.

3) At the end of the current Task. i.e. immediately before the UA is
about to fetch a new Task to run.

4) Scheduled as a future Task. i.e. fully async.

---

Discussion:

Options 1  4 are don't seem to have any proponents that I know of, so briefly:

Option 1, Immediately:

Pro:
-It's conceptually the easiest thing to understand. The following *always* hold:
  -For calling code: When any DOM operation I make completes, all
observers will have run.
  -For notified code: If I'm being called, the operation which caused
this is below me on the stack.

Con:
-Because mutations must be delivered for some DOM operations before
the operation is complete, UAs must tolerate all ways in which script
may invalidate their assumptions before they do further work.


Option 4, Scheduled as a future Task:

Pro:
-Conceptually easy to understand
-Easy to implement.

Con:
-It's too late. Most use cases for mutation observation require that
observers run before a paint occurs. E.g. a widget library which
watches for special attributes. Script may create a div
class=FooButton and an observer will react to this by decorating
the div as a FooButton. It is unacceptable (creates visual
artifacts/flickering) to have the div be painted before the widget
library has decorated it as a FooButton.

Both of these options appear to be non-starters. Option 1 has been
shown by experience to be an unreasonable implementation burden for
UAs. Option 4 clearly doesn't handle properly important use cases.

---

Options 2  3 have proponents. Since I'm one of them (a proponent),
I'll just summarize the main *pro* arguments for each and invite those
who wish (including myself), to weigh in with further support or
criticism in follow-on emails.


Option 2: Upon completion of the outer-most DOM operation.

Pro:
-It's conceptually close to fully synchronous. For simple uses
(specifically, setting aside the case of making DOM operations within
a mutation callback), it has the advantages of Option 1, without its
disadvantages. Because of this, it's similar to the behavior of
current Mutation Events.

Option 3: At the end of the current Task.

Pro:
-No code is at risk for having its assumptions invalidated while it is
trying to do work. All participants (main application script,
libraries which are implemented using DOM mutation observation) are
allowed to complete whatever work (DOM operations) they wish before
another participant starts doing work.



Re: DOM Mutation Events Replacement: When to deliver mutations

2011-08-10 Thread Rafael Weinstein
Ok. Being a proponent, here's my further (opinionated) support for
Option 3 and criticism for Option 2.

On Wed, Aug 10, 2011 at 5:44 PM, Rafael Weinstein rafa...@google.com wrote:
 Although everyone seems to agree that mutations should be delivered
 after the DOM operations which generated them complete, the question
 remains:

  When, exactly, should mutations be delivered?

 The four options I'm aware of are:

 1) Immediately - i.e. while the operation is underway. [Note: This is
 how current DOM Mutation events work].

 2) Upon completion of the outer-most DOM operation. i.e. Immediately
 before a the lowest-on-the-stack DOM operation returns, but after it
 has done all of its work.

 3) At the end of the current Task. i.e. immediately before the UA is
 about to fetch a new Task to run.

 4) Scheduled as a future Task. i.e. fully async.

 ---

 Discussion:

 Options 1  4 are don't seem to have any proponents that I know of, so 
 briefly:

 Option 1, Immediately:

 Pro:
 -It's conceptually the easiest thing to understand. The following *always* 
 hold:
  -For calling code: When any DOM operation I make completes, all
 observers will have run.
  -For notified code: If I'm being called, the operation which caused
 this is below me on the stack.

 Con:
 -Because mutations must be delivered for some DOM operations before
 the operation is complete, UAs must tolerate all ways in which script
 may invalidate their assumptions before they do further work.


 Option 4, Scheduled as a future Task:

 Pro:
 -Conceptually easy to understand
 -Easy to implement.

 Con:
 -It's too late. Most use cases for mutation observation require that
 observers run before a paint occurs. E.g. a widget library which
 watches for special attributes. Script may create a div
 class=FooButton and an observer will react to this by decorating
 the div as a FooButton. It is unacceptable (creates visual
 artifacts/flickering) to have the div be painted before the widget
 library has decorated it as a FooButton.

 Both of these options appear to be non-starters. Option 1 has been
 shown by experience to be an unreasonable implementation burden for
 UAs. Option 4 clearly doesn't handle properly important use cases.

 ---

 Options 2  3 have proponents. Since I'm one of them (a proponent),
 I'll just summarize the main *pro* arguments for each and invite those
 who wish (including myself), to weigh in with further support or
 criticism in follow-on emails.


 Option 2: Upon completion of the outer-most DOM operation.

 Pro:
 -It's conceptually close to fully synchronous. For simple uses
 (specifically, setting aside the case of making DOM operations within
 a mutation callback), it has the advantages of Option 1, without its
 disadvantages. Because of this, it's similar to the behavior of
 current Mutation Events.

Con:

-The timing delays delivery just long enough to guarantee that DOM
operations don't have to worry about having their work interfered
with, but encourages application script to leave itself exposed to
exactly the same risk.

-The semantics of delivery are inconsistent. Delivery of mutations is
synchronous if calling operation is performed outside of a mutation
callback and async if performed inside a mutation callback.


 Option 3: At the end of the current Task.

 Pro:
 -No code is at risk for having its assumptions invalidated while it is
 trying to do work. All participants (main application script,
 libraries which are implemented using DOM mutation observation) are
 allowed to complete whatever work (DOM operations) they wish before
 another participant starts doing work.



Pro:
-Creates (and requires use of -- creates a pit of success) a time to
run which is ideal in two ways:

1) Performance: The main script is finished doing its work. The
observer can minimize work by reacting to only the net-effect of what
happened. I.e. not do work in intermediate states which ultimately
become irrelevant. E.g. a widget library which needs to destruct
widgets which are removed from the document. If a widget is removed
but later added elsewhere in the same script event, the library would
prefer to avoid destructing the widget and just allow it to be moved.

2) Correctness: The observer isn't at risk for attempting to act when
the main script has put the DOM (temporarily) in an inconsistent
state. E.g. a templating library which depends upon the value of two
attributes of a single element. If script wishes to change both values
but cannot do so without creating a temporarily nonsensical state, the
library would prefer not to have to react to the nonsensical state and
simply wait for the consistent (final) state.

To be fair to Option 2, it doesn't preclude these (but it isn't
sufficient -- a separate ability to schedule work at the end of the
Task would be required).

Con:
-Behaves quite differently from the current mutation events.



Re: Mutation events replacement

2011-08-10 Thread Ryosuke Niwa
On Wed, Aug 10, 2011 at 12:49 PM, Olli Pettay olli.pet...@helsinki.fiwrote:

 FYI, I'm planning to implement the proposal (using vendor prefixed API)
 so that I can create tryserver builds.
 I'll post the links to builds here later, hopefully in a few days, when
 I find the time to do the actual implementation.


I don't mean to offend you or despise your effort (I certainly love to see
DOM mutation replacement, and am very grateful for your effort) but I don't
think we have reached a consensus yet as to what API we're implementing.  Of
course, you can always implement any vendor prefixed API you'd like.
 However, as I understand it, our goal here is to deprecate the existing
mutation events and replace it with new API.

Given that, I feel like we should spend a little more time sorting out
details so that we can be reasonably confident that all major browsers will
eventually implement the new API since there are at least 3 conflicting
proposals at the moment.

- Ryosuke


Re: Mutation events replacement

2011-08-10 Thread Boris Zbarsky

On 8/10/11 9:06 PM, Ryosuke Niwa wrote:

On Wed, Aug 10, 2011 at 12:49 PM, Olli Pettay olli.pet...@helsinki.fi
mailto:olli.pet...@helsinki.fi wrote:

FYI, I'm planning to implement the proposal (using vendor prefixed API)
so that I can create tryserver builds.
I'll post the links to builds here later, hopefully in a few days, when
I find the time to do the actual implementation.

I don't mean to offend you or despise your effort (I certainly love to
see DOM mutation replacement, and am very grateful for your effort) but
I don't think we have reached a consensus yet as to what API we're
implementing.


In case Olli's e-mail wasn't clear, he's not talking about implementing 
and shipping in Mozilla releases or even nightlies.  He's talking about 
creating some test builds so people who are interested can try actually 
using a browser with the proposal implemented and see what issues they 
run into, etc.


-Boris



Re: Mutation events replacement

2011-08-10 Thread Ryosuke Niwa
On Wed, Aug 10, 2011 at 8:32 PM, Boris Zbarsky bzbar...@mit.edu wrote:

 On 8/10/11 9:06 PM,
 Ryosuke Niwa
 Software Engineer
 Google Inc.


 wrote:

 On Wed, Aug 10, 2011 at 12:49 PM, Olli Pettay olli.pet...@helsinki.fi
 mailto:Olli.Pettay@helsinki.**fi olli.pet...@helsinki.fi wrote:

FYI, I'm planning to implement the proposal (using vendor prefixed API)
so that I can create tryserver builds.
I'll post the links to builds here later, hopefully in a few days, when
I find the time to do the actual implementation.

 I don't mean to offend you or despise your effort (I certainly love to
 see DOM mutation replacement, and am very grateful for your effort) but
 I don't think we have reached a consensus yet as to what API we're
 implementing.


 In case Olli's e-mail wasn't clear, he's not talking about implementing and
 shipping in Mozilla releases or even nightlies.  He's talking about creating
 some test builds so people who are interested can try actually using a
 browser with the proposal implemented and see what issues they run into,
 etc.


Ah, ok.  That makes sense.  Thanks for the clarification.

- Ryosuke


Re: Mutation events replacement

2011-08-04 Thread Olli Pettay

Hi all,

here is a bit different proposal for mutation events replacement.
This is using the mostly-sync approach, and batching can make it easier
to use with several simultaneous script libraries; each one would use 
their own batch.

For performance reasons it might be useful to have an attribute name
filter for AttrChange, but such thing could be added later.
One advantage this approach has is that it doesn't pollute Node API.


-Olli




interface Modification {
  /**
   * type is either TextChange, ChildListChange or AttrChange.
   * (More types can be added later if needed.)
   */
  readonly attribute DOMString   type;

  /**
   * Target of the change.
   * If an attribute is changed, target is the element,
   * if an element is added or removed, target is the node
   * which was added or removed.
   * If text is changed, target is the CharacterData node which was
   * changed.
   */
  readonly attribute Nodetarget;
  /**
   * parent node of the target right before the change happened,
   * or null.
   */
  readonly attribute NodetargetParent;
  /**
   * The node which is batching the change.
   */
  readonly attribute NodecurrentTarget;

  /**
   * The name of the attribute which was changed, or null.
   */
  readonly attribute DOMString   attrName;

  /*
   * The previous value of the attribute or CharacterData node, or null.
   * If a new attribute is added, prevValue is null.
   */
  readonly attribute DOMString   prevValue;

  /*
   * The new value of the attribute or CharacterData node, or null.
   * If an attribute is removed, newValue is null.
   */
  readonly attribute DOMString   newValue;
};

[Callback, NoInterfaceObject]
interface ModificationCallback {
  void handleBatch(in ModificationBatch aBatch);
};

[Constructor(in ModificationCallback aDoneCallback)]
interface ModificationBatch {
  /**
   * Modifications is non-empty array only while aDoneCallback
   * is called. And while that happens, modifications list doesn't
   * change.
   */
  readonly attribute Modification[] modifications;

  void batchTextChanges(in Node aNode);
  void unbatchTextChanges(in Node aNode);

  void batchChildListChanges(in Node aNode);
  void unbatchChildListChanges(in Node aNode);

  void batchAttrChanges(in Node aNode);
  void unbatchAttrChanges(in Node aNode);

  void batchAll();
  void unbatchAll();
};

aDoneCallback is called right before the call which is modifying DOM
returns. If aDoneCallback modifies DOM, new modifications list will be 
collected

and callbacks will be called right after the initial aDoneCallback returns.
ModificationBatches are always handled in the order they are *created*.
Callbacks are never called if modifications list is empty.


Example 1:
// log all the attribute changes
var o = new ModificationBatch(function(b) {
for (var i = 0; i  
b.modifications.length; ++i) {

  var m = b.modifications[i];
  if (m.prevValue == null) {
console.log(m.attrName +  added);
  } else if (m.newValue == null) {
console.log(m.attrName +  removed);
  } else {
console.log(m.attrName +  modified);
  }
}
  }
);
o.batchAttrChanges(document);





Re: Mutation events replacement

2011-08-04 Thread Olli Pettay



/**
* The name of the attribute which was changed, or null.
*/
readonly attribute DOMString attrName;

There should be probably also attribute namespace



void batchAttrChanges(in Node aNode);


A filter could be added here
-void batchAttrChanges(in Node aNode);
+void batchAttrChanges(in Node aNode, [optional] in DOMString
aReportValues);

Where aReportValues could a comma separated list of attr localNames, or
 *. Default would be to report only that the attribute has changed, but 
to not include any values.





Re: Mutation events replacement

2011-08-04 Thread Olli Pettay

On 08/04/2011 10:14 PM, David Flanagan wrote:

On 8/4/11 6:38 AM, Olli Pettay wrote:

Hi all,

here is a bit different proposal for mutation events replacement.
This is using the mostly-sync approach, and batching can make it easier
to use with several simultaneous script libraries; each one would use
their own batch.
For performance reasons it might be useful to have an attribute name
filter for AttrChange, but such thing could be added later.
One advantage this approach has is that it doesn't pollute Node API.


-Olli



I'm intrigued by the idea, but confused by the API (or at least by
naming issues in the API).

- ModificationBatch is both a batch of modifications and also a set of
methods for requesting modification batches. Should there be separate
ModificationBatch and ModificationBatcher interfaces?

Why? You have an object which can listen for some modifications and then
at some point call the callback that some modifications have happened.
But perhaps the interface could be called something else. I'd like it to 
have batch or some similar meaning in it.





- The pattern of passing a callback function to a constructor is novel
in the DOM. Will this confuse people too much?


Well, I don't think new Foo(callback); is more difficult to understand
than say
var foo = new Foo();
foo.onSomething = callback;


Also, if the callback is
passed to the constructor, there isn't a deregistration method. I assume
that you achieve this by calling unbatchAll(),

Yes that, or calling unbatchFoo if you called batchFoo before.


but that seems
non-parallel.

Registering the callback doesn't need to have anything parallel.
It is the batch*/unbatch* which are parallel.
And batch/unbatchAll are kind of catch-all.


What if the ModificationCallback was passed to the
individual batch/unbatch methods instead?
Well, the whole idea is that the same callback would handle a batch of 
modifications.






- The name batchTextChanges() implies to me that it is expressing a
preference to receive text changes in batched form, and that if you
don't call this method you'll still get text changes, just one at a
time. I don't think that is the intent, but that is what the name
implies to me. How about something (verbose) like
addTextChangeBatchListener() (and pass the ModificationCallback to this
method instead of to the constructor)
As I said, the idea is not to have separate ModificationCallbacks for 
different types, but to have a callback per ModificationBatch.

If you need different callbacks, you can create a new ModificationBatch.

But yes, the naming can be changed, if it is misleading.
Suggestions welcome :)



-Olli





David



interface Modification {
/**
* type is either TextChange, ChildListChange or AttrChange.
* (More types can be added later if needed.)
*/
readonly attribute DOMString type;

/**
* Target of the change.
* If an attribute is changed, target is the element,
* if an element is added or removed, target is the node
* which was added or removed.
* If text is changed, target is the CharacterData node which was
* changed.
*/
readonly attribute Node target;
/**
* parent node of the target right before the change happened,
* or null.
*/
readonly attribute Node targetParent;
/**
* The node which is batching the change.
*/
readonly attribute Node currentTarget;

/**
* The name of the attribute which was changed, or null.
*/
readonly attribute DOMString attrName;

/*
* The previous value of the attribute or CharacterData node, or null.
* If a new attribute is added, prevValue is null.
*/
readonly attribute DOMString prevValue;

/*
* The new value of the attribute or CharacterData node, or null.
* If an attribute is removed, newValue is null.
*/
readonly attribute DOMString newValue;
};

[Callback, NoInterfaceObject]
interface ModificationCallback {
void handleBatch(in ModificationBatch aBatch);
};

[Constructor(in ModificationCallback aDoneCallback)]
interface ModificationBatch {
/**
* Modifications is non-empty array only while aDoneCallback
* is called. And while that happens, modifications list doesn't
* change.
*/
readonly attribute Modification[] modifications;

void batchTextChanges(in Node aNode);
void unbatchTextChanges(in Node aNode);

void batchChildListChanges(in Node aNode);
void unbatchChildListChanges(in Node aNode);

void batchAttrChanges(in Node aNode);
void unbatchAttrChanges(in Node aNode);

void batchAll();
void unbatchAll();
};

aDoneCallback is called right before the call which is modifying DOM
returns. If aDoneCallback modifies DOM, new modifications list will be
collected
and callbacks will be called right after the initial aDoneCallback
returns.
ModificationBatches are always handled in the order they are *created*.
Callbacks are never called if modifications list is empty.


Example 1:
// log all the attribute changes
var o = new ModificationBatch(function(b) {
for (var i = 0; i  b.modifications.length; ++i) {
var m = b.modifications[i];
if (m.prevValue == null) {
console.log

Re: More use-cases for mutation events replacement

2011-07-26 Thread Aryeh Gregor
On Mon, Jul 25, 2011 at 11:12 PM, Sean Hogan shogu...@westnet.com.au wrote:
 I assume you are referring to the NodeWatch proposal from Microsoft.

 1st draft:
    http://www.w3.org/2008/webapps/wiki/Selector-based_Mutation_Events

 2nd draft:

  http://www.w3.org/2008/webapps/wiki/MutationReplacement#NodeWatch_.28A_Microsoft_Proposal.29

I wasn't aware of that proposal.  It seems like we came up with the
same basic idea independently.

 I think the utility of this proposal is unnecessarily limited by the
 restriction of one watcher per node.
 Also, it is not clear that handlers would be called before page reflow /
 repaint.

Yeah, those are two immediate problems I see.  Also (based on looking
at the second draft, not the first):

* I'm not sure what the use-case is for a minimum frequency.  If it's
not going to be really really common, it shouldn't be part of the API,
because authors can always fake it with setTimeout() and some globals.
* I don't think we want to return a handle -- don't other APIs let you
unwatch by just passing the same callback you originally passed?  That
makes more sense, IMO.
* It says it throws an INDEX_SIZE_ERR if the minimum frequency is
negative, but it's an unsigned long, so WebIDL already specifies
different behavior if it's negative (it wraps).



Re: Mutation events replacement

2011-07-25 Thread Dave Raggett

On 24/07/11 16:18, Aryeh Gregor wrote:

On Fri, Jul 22, 2011 at 6:58 PM, Jonas Sicking jo...@sicking.cc wrote:

We should have much richer events to aid with rich text editing. Using
mutation notifications for this is will not create a good experience
for the page author.

Agreed.  I'd be really interested in specific use-cases if people are
using mutation events for editing here.


I am interested in web-based collaboration, e.g. for distributed 
meetings, something we do a lot at W3C. We currently rely on IRC plus a 
bunch of scripts to support meeting management, such as the agenda, 
actions, resolutions, and generating an HTML version of the minutes from 
the IRC record.  My aim is to replace the current IRC system with 
something much better that runs in the browser.  An associated use case 
is to allow people to edit slide presentations as they are being shown, 
where the slides are expressed in HTML via a microformat (HTML Slidy).


I am using mutation events with content-editable plus clean up 
operations to work around variations in how different browsers treat 
Enter, Backspace and Delete.  Higher level editing actions that are 
application specific (e.g. insert slide or next agendum) can be 
serialized as such instead of serializing the constituent mutations. 
Cut, copy, and paste, and drag and drop operations are further 
challenges to ensuring interoperability.  I keep a local  undo/redo 
queue for mutations, and frequently serialize the changes for exchange 
with other clients via a lightweight websockets server module. One 
client acts as the senior editor, automatically reviewing edits proposed 
by other clients (junior editors). This role is swapped around 
automatically or manually. The senior editor serializes the official 
changes as a sequence of updates for the trunk version of the document. 
Junior editors need to be able to revert their DOM to the latest trunk 
version, and reapply their local (proposed changes) after adjusting them 
for the changes since the last common version (a 3 way merge). Ditto for 
their undo/redo queue. Likewise, the senior editor needs to adjust the 
proposed changes to an earlier trunk version to align with subsequent 
trunk versions.  This probably sounds complicated, but it works, and the 
performance is pretty good!


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: More use-cases for mutation events replacement

2011-07-25 Thread Sean Hogan

On 25/07/11 2:18 AM, Aryeh Gregor wrote:

When discussing mutation events use-cases, mostly so far people have
been talking about editors.  However, I think mutation event
replacements would have a much more general appeal if they were easily
usable in certain cases with little performance impact.  Specifically,
one use-case I've run into a lot is I want to modify some class of
node soon after it gets added to the DOM, but before it's actually
painted.  Examples of where this has come up for me in practice:

snip

What would solve all of these use-cases is a way to register a handler
that would get notified every time an element is added to the DOM that
matches a particular CSS selector, which is guaranteed to run at some
point before the element is actually painted.  Thus it could be a
special type of event that runs when the event loop spins *before*
there's any opportunity to paint, or it could be semi-synchronous, or
whatever, as long as it runs before paint.  Then I could easily solve
all the use-cases:

snip

It seems to me this dovetails pretty nicely with some of the proposed
mutation events replacement APIs.  Specifically, people have been
talking about allowing filtering of events, so this use-case should be
solved easily enough if you can use CSS selectors as filters.  In that
case, the perf hit from using such events should be negligible, right?



I assume you are referring to the NodeWatch proposal from Microsoft.

1st draft:
http://www.w3.org/2008/webapps/wiki/Selector-based_Mutation_Events

2nd draft:

http://www.w3.org/2008/webapps/wiki/MutationReplacement#NodeWatch_.28A_Microsoft_Proposal.29



I think the utility of this proposal is unnecessarily limited by the 
restriction of one watcher per node.
Also, it is not clear that handlers would be called before page reflow / 
repaint.
If these issues were resolved, then this feature plus some shadow DOM 
capabilities would facilitate a performant JS implementation of 
(something approaching) XBL2.






Re: Mutation events replacement

2011-07-24 Thread Aryeh Gregor
On Fri, Jul 22, 2011 at 11:54 AM, Boris Zbarsky bzbar...@mit.edu wrote:
 Actually, you can pretty easily do it in the other order (move the text into
 the b, and then put the b in the DOM), and may want to so as to minimize
 the number of changes the the live DOM; that's something that's often
 recommended as a performance enhancement.

Hmm.  Interesting.  So far I've been writing my draft on the theory
that my move preserving ranges operation would actually be
implemented as I've specced it, so that all Ranges (not just the
current selection) would remain in logically the same place after the
DOM operations.  The way I've designed it, you have to move stuff
around within the tree rather than removing and re-adding it.  But of
course, that design could always be changed.  Either I could just give
up on preserving anything other than the current Selection, or I could
define different primitives.

So point taken.  Editing doesn't *have* to involve moving nodes at all.

 I don't need software that uses mutation events.  I need software that
 triggers editing operations, so I can them actually measure what DOM
 mutations are performed in the course of these editing operations.

What use do you have here for software that doesn't want to use DOM
mutations to start with?  The question is what users of mutation
handlers will need, right?  If you do need such software, though, some
of the most important WYSIWYG editors out there are TinyMCE and
CKEditor, which have easy-to-use online demos:

http://tinymce.moxiecode.com/
http://ckeditor.com/

A typical workload is paste in the contents of some blog post or other
that you grab from someplace (often this would come preloaded if
you're editing or quoting an existing post), then change around some
text, type a couple of paragraphs, add an image or some smilies or
something, make some links, that sort of thing.

On Fri, Jul 22, 2011 at 6:57 PM, Jonas Sicking jo...@sicking.cc wrote:
 On Fri, Jul 22, 2011 at 2:08 AM, Dave Raggett d...@w3.org wrote:
 But if you are going to, *don't* coalesce mutations when the resulting DOM
 tree is dependent on the order in which those mutations took place.  This is
 critical to distributed editing applications.

 The DOM should have no such behavior. The only exception to this rule
 that I know of is script elements. They execute their contained
 script the first time they are inserted into a Document, but don't
 undo that action when removed (for obvious reasons), nor do they
 redo it when inserted again.

The order of mutations makes a big difference if you're recording them
as things like insert node X into node Y at offset Z.  If you append
two children to a given node, the order you append them in will affect
the resulting DOM.  Likewise if you insert two nodes at the same
index, or before the same existing child, or if you insert two nodes
at particular indices but remove some child in between.  How could you
record arbitrary DOM mutations such that the order wouldn't matter in
general?

On Fri, Jul 22, 2011 at 6:58 PM, Jonas Sicking jo...@sicking.cc wrote:
 We should have much richer events to aid with rich text editing. Using
 mutation notifications for this is will not create a good experience
 for the page author.

Agreed.  I'd be really interested in specific use-cases if people are
using mutation events for editing here.



More use-cases for mutation events replacement

2011-07-24 Thread Aryeh Gregor
When discussing mutation events use-cases, mostly so far people have
been talking about editors.  However, I think mutation event
replacements would have a much more general appeal if they were easily
usable in certain cases with little performance impact.  Specifically,
one use-case I've run into a lot is I want to modify some class of
node soon after it gets added to the DOM, but before it's actually
painted.  Examples of where this has come up for me in practice:

1) Some images in Wikipedia articles are offensive to some users, who
may want to block them by default.  However, we want to serve the same
page content to different users for caching reasons, only varying the
HTML used for the interface.  One way to solve this would be to add
classes to potentially offensive images, then have a script run that
replaces the image with a placeholder before it's visible to the user.
 Currently, as far as I can tell, the only way to do this is to put a
script immediately after the img.  This could theoretically fail
if the script winds up getting parsed too long after the img, like
if it winds up in a different TCP segment that then gets dropped and
takes a few seconds to resend while the image loads from cache.  More
practically, it's incompatible with CSP, which prohibits inline
script.  It also can't be used in situations like Wikipedia, where
administrators can add scripts to the head but cannot add inline
script.  It's also excessively verbose if there are lots of places per
page you need to do it.  (Actual writeup of requirements, albeit
abandoned: http://www.mediawiki.org/wiki/User:Simetrical/Censorship)

2) Some pages have content that should be collapsed by default with a
way for the user to un-collapse it, but they should be uncollapsed if
the user has script disabled, since otherwise the user won't be able
to access the contents.  This is true for some Wikipedia infoboxes,
for instance.  details might solve this use-case without the need
for script, but it might not (e.g., styling might not be flexible
enough).  Supposing it doesn't, the way you'd currently have to do
this is add a script right after the opening tag that collapses it
and adds the uncollapse button.  But again, inline script is
incompatible with CSP, and incompatible with setups like Wikipedia
where you might not be allowed to add inline script, and excessively
verbose if there are many occurrences per page.

3) In current HTML drafts, details auto-closes p.  I just filed a
bug asking that it be made an inline element that doesn't auto-close
p: http://www.w3.org/Bugs/Public/show_bug.cgi?id=13345.  I want
this because smaug complained that my specs didn't contain rationale,
and when I pointed out that I had detailed rationale in comments, he
said I should make it visible to the reader.  So I want to have inline
details at the end of some li's or p's.  If the change I request
is made, details will still auto-close p in current browsers, so
I'd want to work around that with a shim for browsers using the
current HTML parser.  The obvious thing to do would be to run some
script after every details is added that's the next sibling of a
p, and move it inside the p.  Again, this would require a lot of
repetitive use of script.

4) Prior to the invention of the autofocus attribute, just like in all
the cases above, the only way to reliably autofocus inputs was to add
a script immediately after the input.  This case is moot now that
autofocus exists, but it illustrates that there are more use-cases in
the same vein.

What would solve all of these use-cases is a way to register a handler
that would get notified every time an element is added to the DOM that
matches a particular CSS selector, which is guaranteed to run at some
point before the element is actually painted.  Thus it could be a
special type of event that runs when the event loop spins *before*
there's any opportunity to paint, or it could be semi-synchronous, or
whatever, as long as it runs before paint.  Then I could easily solve
all the use-cases:

1) Register a handler for img that changes the .src of the
newly-added Element.

2) Register a handler for .collapsed or whatever that sets the
appropriate part to display: none and adds the uncollapse button.

3) Register a handler for p + details that moves the details
inside the p.  (This would be trickier if I sometimes put details
in the middle of p, but still doable, and anyway I don't plan to.)

4) Register a handler for .autofocus or whatever that calls focus().

It seems to me this dovetails pretty nicely with some of the proposed
mutation events replacement APIs.  Specifically, people have been
talking about allowing filtering of events, so this use-case should be
solved easily enough if you can use CSS selectors as filters.  In that
case, the perf hit from using such events should be negligible, right?
 I think there are lots of cases like the four I gave above where this
sort of API would be handy for very general-purpose

Re: Mutation events replacement

2011-07-22 Thread Dave Raggett

On 22/07/11 02:26, Adam Klein wrote:

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?


I would suggest avoiding coalescing mutations altogether!

But if you are going to, *don't* coalesce mutations when the resulting 
DOM tree is dependent on the order in which those mutations took place.  
This is critical to distributed editing applications.


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-22 Thread Aryeh Gregor
On Thu, Jul 21, 2011 at 4:21 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 I'd really like numbers.  Having looked at the Gecko editor code in the
 past, I don't share your assurance that this is how it works

 That said, if you point to a workload, I (or anyone else; it's open source!)
 can probably generate some numbers by instrumenting the Gecko DOM.  But I
 need a workload.

Pretty much any formatting command is going to involve adding and
removing wrapper elements.  To add a wrapper element, say adding a b
around some text to make it bold, you first have to insert the wrapper
before or after the thing you want to wrap, then move all the nodes to
wrap into the wrapper.  Likewise, to remove a wrapper, you have to
first move all its contents adjacent to it, then actually remove it
from its parent.

Likewise, for instance, suppose you delete some text that spans
blocks, like: pfoo[bar/pdivbaz]quz/div.  The result will be
something like pfoo[]quz/p.  How do you do that?  First delete
bar and baz, then move quz to the p, then remove the div.
Or let's say you have pfoo[]bar/p and the user hits Enter -- you
first create an empty p after the existing one, then you move bar
into it.

Of the 37 execCommand()s I've defined, every single one will commonly
move at least one node within the DOM, except for insertHorizontalRule
and the ones that don't actually change the DOM (copy, selectAll,
styleWithCSS, useCSS).  I defined an algorithm move preserving
ranges to handle this because of the range mutation problem:

http://aryeh.name/spec/editcommands/editcommands.html#preserving-ranges

It's invoked in 17 places in my draft currently, and nearly all of
those are in general algorithms that are themselves invoked in
multiple places.

So I don't have any numbers, but anecdotally, editing things
definitely does a lot of moving.  If you want numbers, though, you
probably don't want to look at my implementation -- you want some
real-world software that actually uses mutation events.



Re: Mutation events replacement

2011-07-22 Thread Boris Zbarsky

On 7/22/11 11:44 AM, Aryeh Gregor wrote:

Pretty much any formatting command is going to involve adding and
removing wrapper elements.  To add a wrapper element, say adding ab
around some text to make it bold, you first have to insert the wrapper
before or after the thing you want to wrap, then move all the nodes to
wrap into the wrapper.


Actually, you can pretty easily do it in the other order (move the text 
into the b, and then put the b in the DOM), and may want to so as to 
minimize the number of changes the the live DOM; that's something that's 
often recommended as a performance enhancement.



Likewise, to remove a wrapper, you have to
first move all its contents adjacent to it, then actually remove it
from its parent.


Again, these can easily happen in the opposite order


So I don't have any numbers, but anecdotally, editing things
definitely does a lot of moving.  If you want numbers, though, you
probably don't want to look at my implementation -- you want some
real-world software that actually uses mutation events.


I don't need software that uses mutation events.  I need software that 
triggers editing operations, so I can them actually measure what DOM 
mutations are performed in the course of these editing operations.


-Boris



Re: Mutation events replacement

2011-07-22 Thread Dave Raggett

On 22/07/11 16:54, Boris Zbarsky wrote:
I don't need software that uses mutation events.  I need software that 
triggers editing operations, so I can them actually measure what DOM 
mutations are performed in the course of these editing operations.


How about:

 * The many blogging tools with rich text edit modes

 * Google docs is great for live communal editing as everyone can see 
or make changes at the same time.



--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-22 Thread Boris Zbarsky

On 7/22/11 12:32 PM, Dave Raggett wrote:

On 22/07/11 16:54, Boris Zbarsky wrote:

I don't need software that uses mutation events. I need software that
triggers editing operations, so I can them actually measure what DOM
mutations are performed in the course of these editing operations.


How about:

* The many blogging tools with rich text edit modes

* Google docs is great for live communal editing as everyone can see or
make changes at the same time.


Here's my issue.  I use neither of those.  So I'm not sure what a 
reasonable workload is for either one.


If someone wants to write up a reasonable workload test description, 
that would be very much appreciated...


-Boris




Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Thu, Jul 21, 2011 at 6:26 PM, Adam Klein ad...@chromium.org wrote:
 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.

I think that's the only reason yes.

/ Jonas



Re: Mutation events replacement

2011-07-22 Thread Rafael Weinstein
On Tue, Jul 19, 2011 at 4:56 PM, Jonas Sicking jo...@sicking.cc wrote:
 On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwa rn...@webkit.org wrote:
 Thanks for the new proposal, Jonas.  I'm very excited about the progress
 we're making towards a saner world!
 On Tue, Jul 19, 2011 at 4:01 PM, Jonas Sicking jo...@sicking.cc wrote:

 [ { 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 editing purposes, it's also crucial to know from/to where nodes are
 removed/inserted.  It seems like adding an offset trivially solves this
 problem without much overhead.

 I'm not really sure how you're expecting to use indexes. Not that once
 one node is removed, the index changes for all other nodes. Can you
 provide a short example code demonstrating how you'd use the index?

It's not really short, but it's more or less the analog of doing a
projection of a set of splice events if emitted by a hypothetical
observable array (startIndex, numDeleted, numAdded). The code is
implemented inside MDV here:

https://code.google.com/p/mdv/source/browse/trunk/model.js#853

The goal of the projection is to produce a new set of splice mutation
records which represent the net effect on the sequence (joining,
collapsing, canceling), such that the new set minimally describes the
changes and could be applied, in order, to a copy of the sequence's
previous state to arrive at the new state.


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

 Again, it'll be very useful to have old and new values for editing purposes.
  Although I have a reservation as to whether we should do for style or not
 because calling mutation listeners every time script modifies some style
 property will be quite expensive as it requires serializing
 CSSStyleDeclaration.

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

 For editing purposes, seeing old and new value is essential.

 As has been previously mentioned, providing the old value comes with
 significant overhead. For your usecase it seems like this is overhead
 that you'll need to live with, but for many others it might not be.

 We could solve this by making it configurable if you want the old
 values or not. For example by having separate notification types that
 contain before and after values.

 Though note that providing the after-values rarely seems useful as you
 can simply get them as needed from the DOM. As for childlists, the
 only sane solution I can think of would be to provide what the whole
 childlist looked like before modifications started.

 / Jonas




Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Fri, Jul 22, 2011 at 2:08 AM, Dave Raggett d...@w3.org wrote:
 On 22/07/11 02:26, Adam Klein wrote:

 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?

 I would suggest avoiding coalescing mutations altogether!

 But if you are going to, *don't* coalesce mutations when the resulting DOM
 tree is dependent on the order in which those mutations took place.  This is
 critical to distributed editing applications.

The DOM should have no such behavior. The only exception to this rule
that I know of is script elements. They execute their contained
script the first time they are inserted into a Document, but don't
undo that action when removed (for obvious reasons), nor do they
redo it when inserted again.

/ Jonas



Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Fri, Jul 22, 2011 at 9:32 AM, Dave Raggett d...@w3.org wrote:
 On 22/07/11 16:54, Boris Zbarsky wrote:

 I don't need software that uses mutation events.  I need software that
 triggers editing operations, so I can them actually measure what DOM
 mutations are performed in the course of these editing operations.

 How about:

  * The many blogging tools with rich text edit modes

  * Google docs is great for live communal editing as everyone can see or
 make changes at the same time.

We should have much richer events to aid with rich text editing. Using
mutation notifications for this is will not create a good experience
for the page author.

/ Jonas



Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
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.

Additionally, doesn't this problem occur equally in the mostly-sync
proposal? If someone makes a few mutations and then adds a mutation
listener it appears that the same situation appears.

/ Jonas



Re: Mutation events replacement

2011-07-22 Thread Ryosuke Niwa
On Fri, Jul 22, 2011 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote:

 We should have much richer events to aid with rich text editing. Using
 mutation notifications for this is will not create a good experience
 for the page author.


But this is a big use case of mutation events today.  If we were to replace
mutation events, then we certainly need to address this use case.

- Ryosuke


Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Fri, Jul 22, 2011 at 4:44 PM, Ryosuke Niwa rn...@webkit.org wrote:
 On Fri, Jul 22, 2011 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote:

 We should have much richer events to aid with rich text editing. Using
 mutation notifications for this is will not create a good experience
 for the page author.

 But this is a big use case of mutation events today.  If we were to replace
 mutation events, then we certainly need to address this use case.

I wouldn't want to remove mutation events from browsers before adding
better editing events I think.

/ Jonas



Re: Mutation events replacement

2011-07-21 Thread Dave Raggett

On 20/07/11 18:23, Boris Zbarsky wrote:
It's pretty common to have situations where lots (10-20) of properties 
are set in inline style, especially in cases where the inline style is 
being changed via CSS2Properties from script (think animations and the 
like, where the objects being animated tend to have width, height, 
various margin/border/padding/background properties, top, left, etc 
all set).  Those are precisely the cases that are most 
performance-sensitive and where the overhead of serializing the style 
attribute on every mutation is highest due to the large number of 
properties set.


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


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-21 Thread Olli Pettay

On 07/21/2011 01:56 AM, Jonas Sicking wrote:

On Wed, Jul 20, 2011 at 10:30 AM, Olli Pettayolli.pet...@helsinki.fi  wrote:

On 07/20/2011 06:46 PM, Jonas Sicking wrote:


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.


I had hoped for a bit more of an explanation than that ;-)

Such as why do you not think that synchronous events will be a problem
for web developers just like they have been for us?


In practice synchronous events have been a problem to us because we
are in C++, which is unsafe language. Web devs use JS.


The only C++ specific problem that we've had is dangling pointers.
However only a small set of our problems would have been solved by
making local points strong.


only a small set of our problems is a major understatement. Huge
number of problems have been fixed using either strong references or
things like nsWeakFrame (the original reason for nsWeakFrame was
to fix a class of bugs related to dangling pointers which were caused by
mutation event listeners doing something unexpected).



We'd still have problems with indexes changing under us, and nodes
that we removed from one location now being inserted elsewhere, etc.

Another way to look at it is that due to C++ specific problems, when
unexpected things happen during callbacks, we end up possibly
crashing. In Javascript, if unexpected things happen during callbacks,
you'll get buggy behavior. That's better, but still not good.


Web devs usually want something synchronous, like sync XHR
(sync XHR has other problems not related to mutation handling).
Synchronous is easier to understand.


That is a wholly different type of sync API. Those are APIs where the
return value is delivered through a callback rather than as a return
value, forcing you to create awkward code like:

doSomething(function(res1) {
   res1.callFunc(function(res2) {
 doSomethingElse(res2);
   }
}

There's a very good problem description here: http://tamejs.org/
(ignore the proposed solution, it's the problem description that's
interesting for this discussion).

Reading... will comment later.




Those problems aren't happening here as far as I can tell. There are
no return values delivered asynchronously, nor any of the problems
described in the tamejs page.

/ Jonas






Re: Mutation events replacement

2011-07-21 Thread Boris Zbarsky

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.

-Boris




Re: Mutation events replacement

2011-07-21 Thread Olli Pettay

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. If yes, then we need to
track all the changes all the time, even if there are no listeners.



-Olli



Re: Mutation events replacement

2011-07-21 Thread Aryeh Gregor
On Wed, Jul 20, 2011 at 3:11 PM, Ryosuke Niwa rn...@webkit.org wrote:
 But internally, a node movement is a removal then an insertion.  There's
 always possibility that a node gets removed then inserted again after
 mutation observers are invoked.  Also, what happens if a function removed a
 bunch of nodes and then inserted back one of them?

I'm suggesting that we change insertNode()/appendChild()/etc. so that
they're *not* internally a removal then an insertion: they're
internally atomic.  If you call foo.removeChild(bar);
foo.appendChild(bar) then that would be a remove/insert no matter
what.  But if you call foo.appendChild(bar) and bar has a parent and
bar is not the last child of foo, that would be a move.

Yes, this causes problems as long as mutation events exist.  But when
mutation event handlers modify the DOM, behavior is undefined and is
totally inconsistent between browsers in practice, so I don't think
it's a big deal.  Just do whatever's convenient and leave the behavior
inconsistent in this case like in others.  We don't need to
standardize behavior here unless we're going to standardize behavior
in all other cases where DOM mutation listeners mutate the DOM, which
we aren't.

On Wed, Jul 20, 2011 at 10:17 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 What I do have a strong opinion on is that it would be good to have some
 data on how common move operations are compared to remove and insert
 on the web.  Then we'll at least know how common or edge-case the situation
 is and hence how much effort we should spend on optimizing for it...

I can say that it's very common and critical for editors.  Tons of
what you're doing is shuffling nodes around: splitting up text nodes
and wrapping bits of them in new elements that you just inserted
before them, moving all the contents of an element next to it before
you remove it, etc.  Editors of various types seem like they're one of
the big use-cases for a mutation events replacement anyway, so my
guess is it's important.  But nobody's even made a list of use-cases
for mutation listeners, have they?

I don't think moving nodes is as common a use-case for typical sites.
But typical sites don't want mutation listeners either, so they aren't
what we should be concerned about here.



Re: Mutation events replacement

2011-07-21 Thread Boris Zbarsky

On 7/21/11 4:15 PM, Aryeh Gregor wrote:

I can say that it's very common and critical for editors.


I'd really like numbers.  Having looked at the Gecko editor code in the 
past, I don't share your assurance that this is how it works


That said, if you point to a workload, I (or anyone else; it's open 
source!) can probably generate some numbers by instrumenting the Gecko 
DOM.  But I need a workload.


-Boris



Re: Mutation events replacement

2011-07-21 Thread Jonas Sicking
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.

/ Jonas



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



Re: Mutation events replacement

2011-07-20 Thread Dave Raggett
Thanks Jonas for the proposal. For changes to attributes and changes to 
the value of text nodes, it should be possible to applications to 
request to see the before/after values.  You note that style attributes 
may be long as an argument against permitting applications to see the 
before value. But what if an editing application really wants to see 
such changes. It could create a copy of the DOM, but that is even more 
expensive when editing a sizable document. The size of text nodes may 
also be long, but a remote editing application could determine whether 
running a diff algorithm against the before/after strings is justified 
for serialization purposes.


In summary, let's allow applications to choose what data they want to 
see!  The almost asynchronous (option 2) works for me for the timing issue.


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-20 Thread Dave Raggett

On 20/07/11 00:56, Jonas Sicking wrote:

On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwarn...@webkit.org  wrote:

For editing purposes, it's also crucial to know from/to where nodes are
removed/inserted.  It seems like adding an offset trivially solves this
problem without much overhead.

I'm not really sure how you're expecting to use indexes. Not that once
one node is removed, the index changes for all other nodes. Can you
provide a short example code demonstrating how you'd use the index?


Actually, this is what I use in my editing app. The serialization of a 
change involves a tumbler naming scheme for DOM nodes based upon the 
child number, e.g.4.2.1 identifies 1st child of the 2nd child of 
the 4th child of the editing root element. With the current mutation 
events, the application script has to compute these reference strings 
and can do so because of the synchronous nature of existing mutation 
events, and the knowledge that this particular application doesn't 
mutate the DOM within such mutation event handlers.


For the notification scheme, this could be computed by the browser, and 
it would be critical for the notification array of changes to be in the 
sequence in which they occurred. This allows other editing clients to 
apply the changes correctly, despite the reference string changing as 
nodes before or above it are inserted or removed.


If the notification scheme only supplies the index number for the node 
that was inserted/removed/changed the app will need a way to find the 
index numbers for that node's ancestors at the time the mutation took 
place.  This presents challenges since the notified sequence of 
mutations may have altered the index number for the node's parent 
(perhaps even removing it from the tree) subsequent to the mutation in 
question.  A two pass algorithm may be possible that uses the state of 
the DOM tree *after* the sequence of mutations has been applied to 
compute the tumbler for each node at the time a given mutation occurred 
given the child number reported for the mutation. This index number 
needs to be the position at which a new child was added, or the position 
of the child that was removed.


I would much prefer the notification to supply the tumbler for the node 
based upon a designated root element, supplied when the notification 
observer was registered.  If you are concerned with the cost of 
computing the tumbler, this could be done only when the application 
requests it.


p.s. there should be a lightweight means to temporarily disable 
notifications when the application performs house keeping operations on 
the DOM tree.


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-20 Thread Olli Pettay

On 07/20/2011 02:01 AM, Jonas Sicking wrote:

On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sickingjo...@sicking.cc  wrote:

On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinsteinrafa...@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. 

Re: Mutation events replacement

2011-07-20 Thread Boris Zbarsky

On 7/20/11 4:26 AM, Dave Raggett wrote:

You note that style attributes may be long as an argument against permitting 
applications to see the
before value.


The problem is not the length per se.  The problem is that the value is 
not stored anywhere and has to be generated based on other data 
structures, which can be very expensive.  For example, as sane algorithm 
for generating this value will examine all the individual CSS property 
values to determine which of them can be collapsed into shorthands.



In summary, let's allow applications to choose what data they want to
see!


As long as the slow as molasses behavior is opt-in, not opt-out... ;)

-Boris



Re: Mutation events replacement

2011-07-20 Thread Dave Raggett

On 20/07/11 16:32, Boris Zbarsky wrote:

On 7/20/11 4:26 AM, Dave Raggett wrote:
You note that style attributes may be long as an argument against 
permitting applications to see the

before value.


The problem is not the length per se.  The problem is that the value 
is not stored anywhere and has to be generated based on other data 
structures, which can be very expensive.  For example, as sane 
algorithm for generating this value will examine all the individual 
CSS property values to determine which of them can be collapsed into 
shorthands.



In summary, let's allow applications to choose what data they want to
see!


As long as the slow as molasses behavior is opt-in, not opt-out... ;)

-Boris


Perhaps we need to distinguish auto generated attributes from those that 
are set by markup or scripts. Could you please clarify for me the 
difference between the html style attribute and the one you are 
referring to?  My understanding is that the html style attribute is set 
via markup or scripts and *doesn't* reflect all of the computed style 
properties for this DOM node.


Many thanks,
--

 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-20 Thread Jonas Sicking
On Wed, Jul 20, 2011 at 5:20 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 07/20/2011 02:01 AM, Jonas Sicking wrote:

 On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sickingjo...@sicking.cc  wrote:

 On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinsteinrafa...@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 

Re: Mutation events replacement

2011-07-20 Thread Ryosuke Niwa
On Wed, Jul 20, 2011 at 8:43 AM, Dave Raggett d...@w3.org wrote:

 Perhaps we need to distinguish auto generated attributes from those that
 are set by markup or scripts. Could you please clarify for me the difference
 between the html style attribute and the one you are referring to?  My
 understanding is that the html style attribute is set via markup or scripts
 and *doesn't* reflect all of the computed style properties for this DOM
 node.


In WebKit, style attribute is stored as CSSMutableStyleDeclaration instead
of as a string to allow fast style re-calculation.

- Ryosuke


Re: Mutation events replacement

2011-07-20 Thread Dave Raggett

On 20/07/11 17:05, Ryosuke Niwa wrote:
On Wed, Jul 20, 2011 at 8:43 AM, Dave Raggett d...@w3.org 
mailto:d...@w3.org wrote:


Perhaps we need to distinguish auto generated attributes from
those that are set by markup or scripts. Could you please clarify
for me the difference between the html style attribute and the
one you are referring to?  My understanding is that the html style
attribute is set via markup or scripts and *doesn't* reflect all
of the computed style properties for this DOM node.


In WebKit, style attribute is stored as CSSMutableStyleDeclaration 
instead of as a string to allow fast style re-calculation.




Isn't there a cheap way to distinguish changes to the DOM (setAttribute) 
from indirect changes to how CSSMutableStyleDeclaration is formatted to 
text?   It sounds as if you already have a setter function that knows 
how to update the CSSMutableStyleDeclaration from a string, so I would 
have thought that this is easy to deal with, right?


--
 Dave Raggettd...@w3.org  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-20 Thread Ryosuke Niwa
On Wed, Jul 20, 2011 at 9:32 AM, Dave Raggett d...@w3.org wrote:

 Isn't there a cheap way to distinguish changes to the DOM (setAttribute)
 from indirect changes to how CSSMutableStyleDeclaration is formatted to
 text?   It sounds as if you already have a setter function that knows how to
 update the CSSMutableStyleDeclaration from a string, so I would have thought
 that this is easy to deal with, right?


Yes.  The real issue isn't that modifying style attribute as string is
expensive (of course it is expensive) but the problem is that the fact our
internal representation of style attribute isn't string so that whenever
style attribute is changed, we'd have to
serialize CSSMutableStyleDeclaration.

I'm not sure how much of cost that is in practice because style attribute
tends to be short in many cases but this feature cannot be turned on by
default as it becomes a significant performance burden on UA for all other
use cases.

Maybe we can treat style attribute differently and tell which property was
added/removed/modified?

- Ryosuke


Re: Mutation events replacement

2011-07-20 Thread Bjoern Hoehrmann
* Dave Raggett wrote:
Perhaps we need to distinguish auto generated attributes from those that 
are set by markup or scripts. Could you please clarify for me the 
difference between the html style attribute and the one you are 
referring to?  My understanding is that the html style attribute is set 
via markup or scripts and *doesn't* reflect all of the computed style 
properties for this DOM node.

You can manipulate the style attribute using DOM Level 2 Style features
like the ElementCSSInlineStyle interface instead of setting the value
as a string as you would when using .setAttribute and similar features.

  p.../p
  script
  onload = function() {
var p = document.getElementsByTagName('p').item(0);
p.style.margin = '0';
alert(p.getAttribute('style'))
  }
  /script

This would alert something like `margin-top: 0px; margin-right: 0px;
margin-bottom: 0px; margin-left: 0px` or `margin: 0px;`.
-- 
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 



Re: Mutation events replacement

2011-07-20 Thread Boris Zbarsky

On 7/20/11 11:43 AM, Dave Raggett wrote:

Perhaps we need to distinguish auto generated attributes from those that
are set by markup or scripts.


I'm not sure what you mean.


Could you please clarify for me the
difference between the html style attribute and the one you are
referring to?


There isn't one.


My understanding is that the html style attribute is set
via markup or scripts and *doesn't* reflect all of the computed style
properties for this DOM node.


That's correct.

Let me give you a concrete testcase:

  !DOCTYPE html
  body style=margin: 0;
  script
document.body.style.marginLeft = 5px;
document.body.style.marginRight = 5px;
document.body.style.top = 17px;
alert(document.body.getAttribute(style));
  /script

This alerts margin: 0pt 5px; top: 17px in Gecko, margin-top: 0px; 
margin-bottom: 0px; margin-left: 5px; margin-right: 5px; top: 17px in 
WebKit and Presto.  So the value of the style attribute changes when you 
modify inline style via the CSS2Properties and CSSDeclaration 
interfaces.  Not only that, but what's stored are the longhand property 
names and values, with shorthand generation happening at serialization 
time in at least some UAs.


It's pretty common to have situations where lots (10-20) of properties 
are set in inline style, especially in cases where the inline style is 
being changed via CSS2Properties from script (think animations and the 
like, where the objects being animated tend to have width, height, 
various margin/border/padding/background properties, top, left, etc all 
set).  Those are precisely the cases that are most performance-sensitive 
and where the overhead of serializing the style attribute on every 
mutation is highest due to the large number of properties set.


-Boris



Re: Mutation events replacement

2011-07-20 Thread Olli Pettay

On 07/20/2011 06:46 PM, Jonas Sicking wrote:


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.


I had hoped for a bit more of an explanation than that ;-)

Such as why do you not think that synchronous events will be a problem
for web developers just like they have been for us?



In practice synchronous events have been a problem to us because we
are in C++, which is unsafe language. Web devs use JS.

Web devs usually want something synchronous, like sync XHR
(sync XHR has other problems not related to mutation handling).
Synchronous is easier to understand.


-Olli





/ Jonas







Re: Mutation events replacement

2011-07-20 Thread Bjoern Hoehrmann
* Boris Zbarsky wrote:
It's pretty common to have situations where lots (10-20) of properties 
are set in inline style, especially in cases where the inline style is 
being changed via CSS2Properties from script (think animations and the 
like, where the objects being animated tend to have width, height, 
various margin/border/padding/background properties, top, left, etc all 
set).  Those are precisely the cases that are most performance-sensitive 
and where the overhead of serializing the style attribute on every 
mutation is highest due to the large number of properties set.

Depending on the design of the mutation notification system and what
level of complexity people find palatable, it would naturally also be
possible to serialize lazily and additionally limit when the values
are available (further style changes made by the listener could in-
validate the information and you'd get an exception on access, for
instance). So the information being available as part of the API does
not necessarily imply performance problems.
-- 
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 



Re: Mutation events replacement

2011-07-20 Thread Aryeh Gregor
On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan dflana...@mozilla.com wrote:
 Finally, I still think it is worth thinking about trying to model the
 distinction between removing nodes from the document tree and moving them
 (atomically) within the tree.

I'll chip in that I think this is useful.  It makes things somewhat
more complicated, but remove/insert and move are conceptually very
different.  I'd really want to handle them differently for range
mutations, as I previously explained:

http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-March/031053.html

A move operation is unnecessary if the goal is to synchronize changes
between DOMs, but it's useful if the goal is to store and update data
about the nodes themselves.  In that case, moving a node could imply a
very different sort of change to the data than removing/inserting.
Specifically, you might want to throw away data when a node is
removed, but keep it when the node is moved.  Like:

* If some nodes get moved to a nearby position and are in a Range to
start with, they might conceptually belong in the Range afterward.
See the examples in the e-mail I link to before.  If they're removed
and re-inserted, you have to keep extra state somewhere to track that.
 In my edit commands spec, I had to work around this in many different
places by defining special primitives like move preserving ranges,
or in some cases by manually saying For every Range with boundary
point X, do Y.
* If you're associating spellcheck data with text nodes in an editable
region, then if a node gets moved elsewhere within the region, you
want to keep the data.  If it gets removed, you want to throw away the
data.
* Other things?

Of course, we'd have to update every method anywhere that moves nodes
to do so atomically instead of removing then inserting.

Do we have a list of use-cases for mutation events anywhere?



Re: Mutation events replacement

2011-07-20 Thread Bjoern Hoehrmann
* Boris Zbarsky wrote:
On 7/20/11 2:19 PM, Bjoern Hoehrmann wrote:
 Depending on the design of the mutation notification system and what
 level of complexity people find palatable, it would naturally also be
 possible to serialize lazily

The only way to do that is to make sure the pre-mutation data is kept 
somewhere.  Doing that is _expensive_.  We (Gecko) have been there, done 
that, and moved away from it.

Simple example: you get a notification whenever a script could observe
the .getAttribute value changes, and you get it before the change is
applied. Then you have all the data you need without expending effort on
that: you have the old state directly, and you know what change you're
about to make; the serialization code would just have to be able to pro-
duce a string assuming certain changes were made (which may be easy or
hard depending on implementation details).

Not a suggestion, but with the idea being to re-design the system from
scratch, it does seem important to understand that the cost here is not
coming from offering old and new values while notifying about changes,
but from the combination of doing that and other design decisions like
notifying after applying changes, allowing notifications to trigger new
changes, and so on. We got here from confusion about why it's expensive.
-- 
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 



Re: Mutation events replacement

2011-07-20 Thread Ryosuke Niwa
On Wed, Jul 20, 2011 at 12:53 PM, David Flanagan dflana...@mozilla.comwrote:

  On 7/20/11 12:11 PM, Ryosuke Niwa wrote:

  But internally, a node movement is a removal then an insertion.  There's
 always possibility that a node gets removed then inserted again after
 mutation observers are invoked.  Also, what happens if a function removed a
 bunch of nodes and then inserted back one of them?

   My definition of moving a node atomically is taking a node that is
 already in the tree and passing it to appendChild() or insertBefore().
 Everything else is regular node removal followed by node insertion.


But appendChild or insertBefore can happen as a part of a larger operation.
 Then we'd end up splitting the list in half whenever we have this move
entry.

Also, since the existing mutation events and new API must co-exist for some
period of time, there's a chance that mutation event listeners can modify
DOM while the node is detached from the document synchronously.

If, on the other hand, there was some way for the listener to know that the
 node was moved atomically, then it would know that it hadn't missed any
 mutation events and it could retain whatever cached state it had for node A,
 changing only the parent.


I'm not sure if we can have a concept of atomicity in DOM.  Boris might have
a strong opinion on this.

Here's one possible way that the distinction between move and remove could
 be made.  Keep the added and removed lists of nodes exactly as they are
 now.  But when a node is moved atomically, also add it to an array of moved
 nodes.  Listeners that don't care about the move/remove distinction can just
 use the added and removed properties as in the current proposal.  But
 listeners that do care can check any added or removed node against the moved
 array to see if it was an atomic move.


That sounds like a reasonable compromise since it can be an opt-in feature,
and UAs probably don't have to do anything special while making the mutation
list.

- Ryosuke


Re: Mutation events replacement

2011-07-20 Thread David Flanagan

On 7/20/11 12:11 PM, Ryosuke Niwa wrote:
On Wed, Jul 20, 2011 at 11:56 AM, Aryeh Gregor 
simetrical+...@gmail.com mailto:simetrical%2b...@gmail.com wrote:


On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan
dflana...@mozilla.com mailto:dflana...@mozilla.com wrote:
 Finally, I still think it is worth thinking about trying to
model the
 distinction between removing nodes from the document tree and
moving them
 (atomically) within the tree.

I'll chip in that I think this is useful.  It makes things somewhat
more complicated, but remove/insert and move are conceptually very
different.


But internally, a node movement is a removal then an insertion. 
 There's always possibility that a node gets removed then inserted 
again after mutation observers are invoked.  Also, what happens if a 
function removed a bunch of nodes and then inserted back one of them?


My definition of moving a node atomically is taking a node that is 
already in the tree and passing it to appendChild() or insertBefore().  
Everything else is regular node removal followed by node insertion.


If you get a mutation event that says that node A was removed from node 
B and inserted into node C, you know nothing about the state of node A, 
since it could have been mutated while it was outside of the tree and no 
mutation events would have been recorded.  Its attributes, text and 
children all could have changed, so the mutation listener has to 
basically discard everything it knows about node A and treat it as a 
brand-new node.


If, on the other hand, there was some way for the listener to know that 
the node was moved atomically, then it would know that it hadn't missed 
any mutation events and it could retain whatever cached state it had for 
node A, changing only the parent.


Here's one possible way that the distinction between move and remove 
could be made: keep the added and removed lists of nodes exactly as they 
are now.  But when a node is moved atomically, also add it to an array 
of moved nodes.  Listeners that don't care about the move/remove 
distinction can just use the added and removed properties as in the 
current proposal.  But listeners that do care can check any added or 
removed node against the moved array to see if it was an atomic move.


Another approach that might work: define a reparent or move mutation 
event type.  So when node A is moved from parent B to parent C, the 
mutations would be:


[{target:A, type:reparent},
 {target:B, type:childList, removed:[A]},
 {target:C, type:childList, added:[B]}]

To make this work, if an atomic move was followed by a removal, the 
reparent mutation would have to be removed from the list of mutations.


David


e.g. say we have divhellobrworldbrw3cbr/div

And we have a hypothetical function that does:

 1. Remove all children of div
 2. Inserts w3c back after div.

Then what should the list of mutations contain?  Should it contain
2 items one that says it removed hello, world, and 3 br's, and
then another one saying w3c moved?  But then child nodes are not
all consecutive and scripts won't be able to infer where these
nodes were even if we provided offsets or before/after node.

Should it contain 3 items, one that says hello, world and the
first 2 br's are removed, then one for moving w3c, and then
another one for removing the last br?  But then UAs have to keep
reorganizing the list as the function modifies more DOM because
there is no way to differentiate w3c until it's inserted back
into DOM.


- Ryosuke




Re: Mutation events replacement

2011-07-20 Thread Ryosuke Niwa
On Tue, Jul 19, 2011 at 8:23 PM, Boris Zbarsky bzbar...@mit.edu wrote:

 On 7/19/11 7:18 PM,
 Ryosuke Niwa
 Software Engineer
 Google Inc.


 wrote:

 For editing purposes, it's also crucial to know from/to where nodes are
 removed/inserted.  It seems like adding an offset trivially solves this
 problem without much overhead.


 I'm not convinced about without much overhead.  In general, adding an
 offset is O(N) in number of childnodes in many existing implementations
  that can be improved, but only at the cost of more memory or performance
 elsewhere.


That's a good point.  It should probably before/after node instead.

 Again, it'll be very useful to have old and new values for editing
 purposes.  Although I have a reservation as to whether we should do for
 style or not because calling mutation listeners every time script
 modifies some style property will be quite expensive as it requires
 serializing CSSStyleDeclaration.


 Yes, that is _exactly_ the problem.


Right so it should be an opt-in feature as you suggested.

- Ryosuke


Re: Mutation events replacement

2011-07-20 Thread David Flanagan

On 7/19/11 4:01 PM, Jonas Sicking wrote:

'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] } ]


Given that childlist and attribute changes are merged together into 
arrays, there is, in general, no way to reconstruct the ordering of 
mutations.  In the example above, I'd assume that the first change to 
node1 occurred before the change to node 2.  But there are 8 other 
changes to node1 and we know nothing about their ordering relative to 
the others.


So, if mutation order is not preserved, is an array the right data 
structure for this unordered set of mutations?  There is a Map type 
proposed for ES.next that allows objects as keys: 
http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets  
Would it be helpful to use that as the basis of this data structure?  Or 
could DOMCore define a NodeMap type (to go along with NodeList?)


I don't have a specific use-case in mind, but I wanted to bring this up 
since I imagine it would be nice to be able to quickly find all 
mutations for a given target node without having to do a linear search 
with Array.filter() or similar.


David



Re: Mutation events replacement

2011-07-20 Thread Ojan Vafai
On Wed, Jul 20, 2011 at 10:30 AM, Olli Pettay olli.pet...@helsinki.fiwrote:

 On 07/20/2011 06:46 PM, Jonas Sicking wrote:

  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.


 I had hoped for a bit more of an explanation than that ;-)

 Such as why do you not think that synchronous events will be a problem
 for web developers just like they have been for us?



 In practice synchronous events have been a problem to us because we
 are in C++, which is unsafe language. Web devs use JS.


In many cases, where you would have had a crash in C++, you would have a bug
and/or exception in JS. It's for exactly the same reason. Your code cannot
make assumptions about the state of the DOM because other code may have run
that changes it out from under you. A contrived example:

var firstChild = node.firstChild;
node.appendChild(randomNode); // Some mutation handler runs here that
removes firstChild from the DOM.
alert(firstChild.parentNode.innerHTML); // An exception gets thrown because
firstChild.parentNode is now null.

You can easily imagine more complicated examples that you would easily hit
in the real world if there are multiple libraries acting on the same DOM.


 Web devs usually want something synchronous, like sync XHR
 (sync XHR has other problems not related to mutation handling).
 Synchronous is easier to understand.


 -Olli




 / Jonas







Re: Mutation events replacement

2011-07-20 Thread Jonas Sicking
On Wed, Jul 20, 2011 at 10:30 AM, Olli Pettay olli.pet...@helsinki.fi wrote:
 On 07/20/2011 06:46 PM, Jonas Sicking wrote:

 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.

 I had hoped for a bit more of an explanation than that ;-)

 Such as why do you not think that synchronous events will be a problem
 for web developers just like they have been for us?

 In practice synchronous events have been a problem to us because we
 are in C++, which is unsafe language. Web devs use JS.

The only C++ specific problem that we've had is dangling pointers.
However only a small set of our problems would have been solved by
making local points strong.

We'd still have problems with indexes changing under us, and nodes
that we removed from one location now being inserted elsewhere, etc.

Another way to look at it is that due to C++ specific problems, when
unexpected things happen during callbacks, we end up possibly
crashing. In Javascript, if unexpected things happen during callbacks,
you'll get buggy behavior. That's better, but still not good.

 Web devs usually want something synchronous, like sync XHR
 (sync XHR has other problems not related to mutation handling).
 Synchronous is easier to understand.

That is a wholly different type of sync API. Those are APIs where the
return value is delivered through a callback rather than as a return
value, forcing you to create awkward code like:

doSomething(function(res1) {
  res1.callFunc(function(res2) {
doSomethingElse(res2);
  }
}

There's a very good problem description here: http://tamejs.org/
(ignore the proposed solution, it's the problem description that's
interesting for this discussion).

Those problems aren't happening here as far as I can tell. There are
no return values delivered asynchronously, nor any of the problems
described in the tamejs page.

/ Jonas



Re: Mutation events replacement

2011-07-20 Thread Sean Hogan

On 21/07/11 6:18 AM, David Flanagan wrote:

On 7/20/11 12:11 PM, Ryosuke Niwa wrote:
On Wed, Jul 20, 2011 at 11:56 AM, Aryeh Gregor 
simetrical+...@gmail.com mailto:simetrical%2b...@gmail.com wrote:


On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan
dflana...@mozilla.com mailto:dflana...@mozilla.com wrote:
 Finally, I still think it is worth thinking about trying to
model the
 distinction between removing nodes from the document tree and
moving them
 (atomically) within the tree.

I'll chip in that I think this is useful.  It makes things somewhat
more complicated, but remove/insert and move are conceptually very
different.


But internally, a node movement is a removal then an insertion. 
 There's always possibility that a node gets removed then inserted 
again after mutation observers are invoked.  Also, what happens if a 
function removed a bunch of nodes and then inserted back one of them?


My definition of moving a node atomically is taking a node that is 
already in the tree and passing it to appendChild() or 
insertBefore().  Everything else is regular node removal followed by 
node insertion.


If you get a mutation event that says that node A was removed from 
node B and inserted into node C, you know nothing about the state of 
node A, since it could have been mutated while it was outside of the 
tree and no mutation events would have been recorded.  Its attributes, 
text and children all could have changed, so the mutation listener has 
to basically discard everything it knows about node A and treat it as 
a brand-new node.




Under Jonas' original proposal, mutation listeners would be called for 
nodes that are outside the document (the API would be available on 
Document, Element and DocumentFragment interfaces).


As long as you add listeners to node A before it is removed from node B 
you can be informed of mutations on (or below) node A before it is 
inserted into node C.


Of course, the optimal place to add a listener to node A is in a 
synchronous mutation listener that is fired when node A is removed. In 
the case of asynchronous mutation listeners a better solution might be 
to have the API on the Document interface and use an approach similar to 
event delegation.






Re: Mutation events replacement

2011-07-20 Thread Boris Zbarsky

On 7/20/11 4:10 PM, Bjoern Hoehrmann wrote:

Simple example: you get a notification whenever a script could observe
the .getAttribute value changes, and you get it before the change is
applied.


Right, the synchronous will mutate notification.

Having that does simplify things.  As discussed on this list earlier, 
this can only be done if you trust your callees or can enforce that your 
callees don't mess things up during the notification.  In this case, the 
former is clearly false and the latter would basically involve changing 
the spec for every single API in the platform to behave slightly 
differently during the will mutate notification.  I agree that if this 
constraint somehow didn't exist the design space would be bigger for 
mutation events.  But it does exist, and it doesn't seem practical so 
far to remove it...


-Boris



Re: Mutation events replacement

2011-07-20 Thread Boris Zbarsky

On 7/20/11 4:14 PM, Ryosuke Niwa wrote:

I'm not sure if we can have a concept of atomicity in DOM.  Boris might
have a strong opinion on this.


I don't yet.

What I do have a strong opinion on is that it would be good to have some 
data on how common move operations are compared to remove and 
insert on the web.  Then we'll at least know how common or edge-case 
the situation is and hence how much effort we should spend on optimizing 
for it...


-Boris



Re: Mutation events replacement

2011-07-20 Thread David Flanagan

On 7/20/11 7:17 PM, Boris Zbarsky wrote:

On 7/20/11 4:14 PM, Ryosuke Niwa wrote:

I'm not sure if we can have a concept of atomicity in DOM.  Boris might
have a strong opinion on this.


I don't yet.

What I do have a strong opinion on is that it would be good to have 
some data on how common move operations are compared to remove and 
insert on the web.  Then we'll at least know how common or edge-case 
the situation is and hence how much effort we should spend on 
optimizing for it...


-Boris
I agree that it would be good to have data.  All I have is the intuition 
that moves in the form of reparenting elements is fairly common.  I 
assume that there is a lot of code out there that dynamically decorates 
static content (to add hyperlinks, animation, etc.) by reparenting that 
content into a container element using some variation on this basic code:


var container = document.createElement('div');
parent.insertBefore(container, target);
container.appendChild(target);

But you're right that this might be an edge case that is not worth 
optimizing.  If reparent events are treated as a new category of 
mutation events, then they can be added later, if needed, since Jonas's 
proposal allows for that sort of extension.


David




Re: Mutation events replacement

2011-07-19 Thread Jonas Sicking
On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sicking jo...@sicking.cc wrote:
 On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein rafa...@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 

Re: Mutation events replacement

2011-07-19 Thread Ryosuke Niwa
Thanks for the new proposal, Jonas.  I'm very excited about the progress
we're making towards a saner world!

On Tue, Jul 19, 2011 at 4:01 PM, Jonas Sicking jo...@sicking.cc wrote:

 [ { 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 editing purposes, it's also crucial to know from/to where nodes are
removed/inserted.  It seems like adding an offset trivially solves this
problem without much overhead.


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


Again, it'll be very useful to have old and new values for editing purposes.
 Although I have a reservation as to whether we should do for style or not
because calling mutation listeners every time script modifies some style
property will be quite expensive as it requires serializing
CSSStyleDeclaration.

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


For editing purposes, seeing old and new value is essential.

- Ryosuke


Re: Mutation events replacement

2011-07-19 Thread Jonas Sicking
On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwa rn...@webkit.org wrote:
 Thanks for the new proposal, Jonas.  I'm very excited about the progress
 we're making towards a saner world!
 On Tue, Jul 19, 2011 at 4:01 PM, Jonas Sicking jo...@sicking.cc wrote:

 [ { 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 editing purposes, it's also crucial to know from/to where nodes are
 removed/inserted.  It seems like adding an offset trivially solves this
 problem without much overhead.

I'm not really sure how you're expecting to use indexes. Not that once
one node is removed, the index changes for all other nodes. Can you
provide a short example code demonstrating how you'd use the index?

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

 Again, it'll be very useful to have old and new values for editing purposes.
  Although I have a reservation as to whether we should do for style or not
 because calling mutation listeners every time script modifies some style
 property will be quite expensive as it requires serializing
 CSSStyleDeclaration.

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

 For editing purposes, seeing old and new value is essential.

As has been previously mentioned, providing the old value comes with
significant overhead. For your usecase it seems like this is overhead
that you'll need to live with, but for many others it might not be.

We could solve this by making it configurable if you want the old
values or not. For example by having separate notification types that
contain before and after values.

Though note that providing the after-values rarely seems useful as you
can simply get them as needed from the DOM. As for childlists, the
only sane solution I can think of would be to provide what the whole
childlist looked like before modifications started.

/ Jonas



Re: Mutation events replacement

2011-07-19 Thread Ryosuke Niwa
On Tue, Jul 19, 2011 at 4:56 PM, Jonas Sicking jo...@sicking.cc wrote:

 On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwa rn...@webkit.org wrote:
  For editing purposes, it's also crucial to know from/to where nodes are
  removed/inserted.  It seems like adding an offset trivially solves this
  problem without much overhead.

 I'm not really sure how you're expecting to use indexes. Not that once
 one node is removed, the index changes for all other nodes. Can you
 provide a short example code demonstrating how you'd use the index?


But if you have the full list of mutations that had happened, you should be
able to infer exactly where nodes and inserted and removed.  I'm thinking of
cases where collaborative editing app and so forth needs to sync data with a
remote server.  In that case, the script wants to figure out what inserted
or removed where.

 * 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.
 
  For editing purposes, seeing old and new value is essential.

 As has been previously mentioned, providing the old value comes with
 significant overhead. For your usecase it seems like this is overhead
 that you'll need to live with, but for many others it might not be.


Agreed.

We could solve this by making it configurable if you want the old
 values or not. For example by having separate notification types that
 contain before and after values.

 Though note that providing the after-values rarely seems useful as you
 can simply get them as needed from the DOM. As for childlists, the
 only sane solution I can think of would be to provide what the whole
 childlist looked like before modifications started.


Yeah, that sounds like a reasonable approach here.  I'll think about use
cases where we want after-value.

I'm kind of guessing what editor authors want to have from my limited
experience getting feedbacks from them but I'll contact folks I know and see
if I can get more concrete use cases.

- Ryosuke


Re: Mutation events replacement

2011-07-19 Thread Boris Zbarsky

On 7/19/11 7:18 PM, Ryosuke Niwa wrote:

For editing purposes, it's also crucial to know from/to where nodes are
removed/inserted.  It seems like adding an offset trivially solves this
problem without much overhead.


I'm not convinced about without much overhead.  In general, adding an 
offset is O(N) in number of childnodes in many existing 
implementations  that can be improved, but only at the cost of more 
memory or performance elsewhere.



Again, it'll be very useful to have old and new values for editing
purposes.  Although I have a reservation as to whether we should do for
style or not because calling mutation listeners every time script
modifies some style property will be quite expensive as it requires
serializing CSSStyleDeclaration.


Yes, that is _exactly_ the problem.

I have a related question.  If the same node is inserted and removed 
over and over again (say 100 times) in the same task, what 
notification(s) will be delivered at the end of the task?


-Boris




Re: Mutation events replacement

2011-07-19 Thread David Flanagan

On 7/19/11 4:01 PM, Jonas Sicking wrote:

[ { 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] } ]
I don't see the advantage of having one array element per target/type 
combination.  Why not just one entry per modified node, and put all 
changes in the same object?  So the first two entries in the array above 
would become something like:


  { target: node1, nodesAdded: [a,b,c,d], nodesRemoved:[x,y], 
attributesChanged: [class,bgcolor,href]}



node.addMutationListener(listener, { childlist: true, attributes:
true, characterdata: true });

Is the second argument optional?  What's the default?

* 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 });
This seems awkward to me, unless removeMutationListener is modified to 
take the same second argument as well so that notification type flags 
could be individually removed.


Finally, I still think it is worth thinking about trying to model the 
distinction between removing nodes from the document tree and moving 
them (atomically) within the tree.


David



Re: Mutation events replacement

2011-07-09 Thread Sean Hogan

On 9/07/11 1:12 AM, Ryosuke Niwa wrote:
On Fri, Jul 8, 2011 at 5:21 AM, Sean Hogan shogu...@westnet.com.au 
mailto:shogu...@westnet.com.au wrote:


- MathJax (http://mathjax.org) is a JS lib that facilitates
putting math onto the web by converting LaTeX or MathML markup in
a page to HTML. By default MathJax triggers off the onload event
to run this conversion on the page. When content containing math
is dynamically added to the page, MathJax must be called manually
to convert the new content. A DOM insertion listener could
potentially be used to handle this conversion automatically.

- A similar use-case is element augmentation too complex for CSS
:before and :after

- ARIA support in JS libs currently involves updating
aria-attributes to be appropriate to behavior the lib is
implementing. Attribute mutation listeners would allow an inverse
approach - behaviors being triggered off changes to aria-attributes.

- DOM insertion and removal listeners could facilitate the
implementation of automatically updating Table-of-* (Headings /
Images / etc).


It seems like all 3 use cases here can be implemented by observers 
that are called AFTER the fact, and do not requiere any events or 
callbacks before mutation.





I agree, but it's just a list of the top of my head - I was merely 
trying to assist with the request for use-cases.


An obvious advantage of callbacks that occur BEFORE mutation is that 
they can be used to implement post-mutation notifications. The reverse 
is impossible.






Re: Mutation events replacement

2011-07-08 Thread Sean Hogan

On 8/07/11 8:28 AM, Jonas Sicking wrote:

On Thu, Jul 7, 2011 at 3:21 PM, John J Barton
johnjbar...@johnjbarton.com  wrote:

Jonas Sicking wrote:

  We are definitely
short on use cases for mutation events in general which is a problem.


3. Client side dynamic translation. Intercept mutations and replace or
extend them. This could be for user tools like scriptish or stylish, dev
tools to inject marks or code, or for re-engineering complex sites for newer
browser features.

I don't fully understand this. Can you give more concrete examples?


- MathJax (http://mathjax.org) is a JS lib that facilitates putting math 
onto the web by converting LaTeX or MathML markup in a page to HTML. By 
default MathJax triggers off the onload event to run this conversion on 
the page. When content containing math is dynamically added to the page, 
MathJax must be called manually to convert the new content. A DOM 
insertion listener could potentially be used to handle this conversion 
automatically.


- A similar use-case is element augmentation too complex for CSS :before 
and :after


- ARIA support in JS libs currently involves updating aria-attributes to 
be appropriate to behavior the lib is implementing. Attribute mutation 
listeners would allow an inverse approach - behaviors being triggered 
off changes to aria-attributes.


- DOM insertion and removal listeners could facilitate the 
implementation of automatically updating Table-of-* (Headings / Images / 
etc).





Re: Mutation events replacement

2011-07-08 Thread timeless
On Thu, Jul 7, 2011 at 6:21 PM, John J Barton
johnjbar...@johnjbarton.com wrote:
 1. Graphical breakpoints. The user marks some DOM element or attribute to
 trigger break. The debugger inserts mutation listeners to watch for the
 event that causes that element/attribute to be created/modified. Then the
 debugger re-executes some code sequence and halts when the appropriate
 listener is entered. Placing the listeners high in the tree and analyzing
 all of the events is easier than trying to precisely add a listener since
 the tree will be modified during re-execution.

 2. Graphical tracing. Recording all or part of the DOM creation. For
 visualization or analysis tools.  See for example Firebug's HTML panel with
 options Highlight Changes, Expand Changes, or Scroll Changes into View.

 3. Client side dynamic translation. Intercept mutations and replace or
 extend them. This could be for user tools like scriptish or stylish, dev
 tools to inject marks or code, or for re-engineering complex sites for newer
 browser features.

Most if not all of your use cases can be done by hooking the functions
you're worried about at the engine level.

You can do any of the following:
1. replace functions on object prototypes
2. instrument the js code before it executes
3. single step the js code and then instrument it as it executes

You could also just wait for this to be implemented and arrange for a
*private* non DOM API which would allow you to get hooks which do
things like any of the above.

Note that JSD historically has had a before method call callback.
And iirc someone was supposed to provide it again for the cases where
the JIT broke this. A before method call callback easily enables you
to check its name against a table and decide that you need to store
values, there's also a paired after callback, during which you can
do the rest of your work. If these don't work, you can file a bug
against the API and hopefully someone will work on it.

-- sadly, that won't be me.



Re: Mutation events replacement

2011-07-08 Thread Sean Hogan

On 8/07/11 10:21 PM, Sean Hogan wrote:

On 8/07/11 8:28 AM, Jonas Sicking wrote:

On Thu, Jul 7, 2011 at 3:21 PM, John J Barton
johnjbar...@johnjbarton.com  wrote:

Jonas Sicking wrote:

  We are definitely
short on use cases for mutation events in general which is a problem.


3. Client side dynamic translation. Intercept mutations and replace or
extend them. This could be for user tools like scriptish or stylish, 
dev
tools to inject marks or code, or for re-engineering complex sites 
for newer

browser features.

I don't fully understand this. Can you give more concrete examples?




A couple of comments on these use-cases:

- MathJax (http://mathjax.org) is a JS lib that facilitates putting 
math onto the web by converting LaTeX or MathML markup in a page to 
HTML. By default MathJax triggers off the onload event to run this 
conversion on the page. When content containing math is dynamically 
added to the page, MathJax must be called manually to convert the new 
content. A DOM insertion listener could potentially be used to handle 
this conversion automatically.


- A similar use-case is element augmentation too complex for CSS 
:before and :after




The previous cases respond to content being inserted into the page by 
(potentially) adding more content. Ideally these additional insertions 
wouldn't trigger additionally mutation listeners. I guess the current 
event system facilitates this with stopPropagation().


- ARIA support in JS libs currently involves updating aria-attributes 
to be appropriate to behavior the lib is implementing. Attribute 
mutation listeners would allow an inverse approach - behaviors being 
triggered off changes to aria-attributes.




As has been mentioned, listening for attribute mutations is horrendously 
inefficient because your handler has to receive every mutation, even if 
only interested in one attribute.


- DOM insertion and removal listeners could facilitate the 
implementation of automatically updating Table-of-* (Headings / Images 
/ etc).









Re: Mutation events replacement

2011-07-08 Thread Ryosuke Niwa
On Fri, Jul 8, 2011 at 5:21 AM, Sean Hogan shogu...@westnet.com.au wrote:

 - MathJax (http://mathjax.org) is a JS lib that facilitates putting math
 onto the web by converting LaTeX or MathML markup in a page to HTML. By
 default MathJax triggers off the onload event to run this conversion on the
 page. When content containing math is dynamically added to the page, MathJax
 must be called manually to convert the new content. A DOM insertion listener
 could potentially be used to handle this conversion automatically.

 - A similar use-case is element augmentation too complex for CSS :before
 and :after

 - ARIA support in JS libs currently involves updating aria-attributes to be
 appropriate to behavior the lib is implementing. Attribute mutation
 listeners would allow an inverse approach - behaviors being triggered off
 changes to aria-attributes.

 - DOM insertion and removal listeners could facilitate the implementation
 of automatically updating Table-of-* (Headings / Images / etc).


It seems like all 3 use cases here can be implemented by observers that are
called AFTER the fact, and do not requiere any events or callbacks before
mutation.

- Ryosuke


Re: Mutation events replacement

2011-07-08 Thread Jonas Sicking
On Fri, Jul 8, 2011 at 5:21 AM, Sean Hogan shogu...@westnet.com.au wrote:
 On 8/07/11 8:28 AM, Jonas Sicking wrote:

 On Thu, Jul 7, 2011 at 3:21 PM, John J Barton
 johnjbar...@johnjbarton.com  wrote:

 Jonas Sicking wrote:

  We are definitely
 short on use cases for mutation events in general which is a problem.

 3. Client side dynamic translation. Intercept mutations and replace or
 extend them. This could be for user tools like scriptish or stylish, dev
 tools to inject marks or code, or for re-engineering complex sites for
 newer
 browser features.

 I don't fully understand this. Can you give more concrete examples?

 - MathJax (http://mathjax.org) is a JS lib that facilitates putting math
 onto the web by converting LaTeX or MathML markup in a page to HTML. By
 default MathJax triggers off the onload event to run this conversion on the
 page. When content containing math is dynamically added to the page, MathJax
 must be called manually to convert the new content. A DOM insertion listener
 could potentially be used to handle this conversion automatically.

 - A similar use-case is element augmentation too complex for CSS :before and
 :after

 - ARIA support in JS libs currently involves updating aria-attributes to be
 appropriate to behavior the lib is implementing. Attribute mutation
 listeners would allow an inverse approach - behaviors being triggered off
 changes to aria-attributes.

 - DOM insertion and removal listeners could facilitate the implementation of
 automatically updating Table-of-* (Headings / Images / etc).

Do any of these require synchronous callbacks? Do any of these use
mutation events today?

/ Jonas



Re: Mutation events replacement

2011-07-08 Thread Tab Atkins Jr.
On Fri, Jul 8, 2011 at 6:55 AM, Sean Hogan shogu...@westnet.com.au wrote:
 On 8/07/11 10:21 PM, Sean Hogan wrote:
 - ARIA support in JS libs currently involves updating aria-attributes to
 be appropriate to behavior the lib is implementing. Attribute mutation
 listeners would allow an inverse approach - behaviors being triggered off
 changes to aria-attributes.

 As has been mentioned, listening for attribute mutations is horrendously
 inefficient because your handler has to receive every mutation, even if only
 interested in one attribute.

This is a limitation of current mutation events.  We don't have to
repeat this mistake.  Allowing a script to listen for changes to a
specific attribute is a big low-hanging fruit.

~TJ



Re: Mutation events replacement

2011-07-07 Thread Jonas Sicking
Hi All,

I've finally caught up on all the emails in this thread. Here are my
impressions so far.

I don't think John J Barton's proposal to fire before mutation
notifications is doable. Trying to make all APIs that would, or
could, mutate the DOM throw or otherwise fail would be much too
complex to implement and too surprising use. The list of APIs that
we'd have to black-list would be far too long and would be heavily
dependent on implementation strategies.

We could take the approach workers take and make such scripts execute
in a wholly different context where the harmful APIs simply aren't
accessible. However such a context would, like workers, not have
access to any of the normal functions or variables that the page
normally uses. So I don't really see what you would do in such a
context? I.e. what use cases would be solved if all you can do is run
calculations, but not otherwise synchronously communicate with the
outside world. And how would you tell such a notification which nodes
are being modified since you can't pass references to the nodes as
Nodes are one of the APIs that we don't want to expose. Additionally,
how would you even register the callback that should be called when
the notification fires? Note that workers always load a wholly new
javascript file, there is no way to pass function references to it.

In short before spending more time on this, I'd like to see a
comprehensive proposal, including a description of the use cases it
solves and how it solves them. I strongly doubt that this approach is
practical.


I really like Rafael's proposal to pass a list of mutations that has
happened to the notification callbacks. This has the advantage that
scripts get *all* the changes that has happened at once, making it
possible to make decisions based on all changes made, rather than
piece-wise getting the information in separate callbacks. It also has
the advantage that we can provide much more detailed information
without having to make multiple calls from C++ to JS which is good for
performance. For example it seems very doable to provide lists of all
nodes that has been removed and added while still keeping performance
reasonable.

I'll write up a proposal based on this idea. Others should feel free
to beat me to it :)


I'm less convinced of the part of Rafael's proposal that changes
*when* notifications are called. If I understand the proposal, the
idea is to not fire any notifications directly when the mutation
happens, but instead all notifications are as soon as control is
returned to the event loop. I.e. it's basically fully asynchronous,
except that instead of adding a task to the end of the event queue,
one is added to the beginning of it.

Rafael, please do correct me if I'm wrong on the above.

The main concern that I have with this proposal is that it's so
different from mutation events that it might not satisfy the same use
cases. Consider a widget implementation that currently observes the
DOM using mutation events and makes it possible to write code like:

myWidgetBackedElement.appendChild(someNode);
myWidgetBackedElement.someFunction();

where someFunction depends on state which is updated by the mutation
event handler. Such a widget implementation is simply not doable with
these semi-asynchronous callbacks.

On the other hand, maybe this isn't a big deal. We are definitely
short on use cases for mutation events in general which is a problem.


Finally, I actually do think it would be possible to create an API
which allow pages to batch notifications. Something like this should
work:

document.notificationBatch(function() {
  element1.appendChild(someNode);
  element2.setAttribute(foo, bar);
});

This would batch all notifications while inside the batch. However,
I'm not convinced we should add such an API, at least in version one.
And definitely not until there are proper use cases. Mutation events
sort have have the ability to batch notifications by calling
stopImmediatePropagation on events when they are fired and then
manually firing events once the batch is done. Is this something
people have done? Can anyone point to examples?

Additionally, we'd have to decide what to do if someone spins the
event loop inside a batch, for example by calling alert() or use
synchronous XHR.

/ Jonas



Re: Mutation events replacement

2011-07-07 Thread Ryosuke Niwa
On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking jo...@sicking.cc wrote:

 I don't think John J Barton's proposal to fire before mutation
 notifications is doable.


I concur.  Being synchronous was one of the reasons why the existing DOM
mutation events don't work.  We shouldn't adding yet-another synchronous
event here.

In short before spending more time on this, I'd like to see a
 comprehensive proposal, including a description of the use cases it
 solves and how it solves them. I strongly doubt that this approach is
 practical.


Totally agreed.

I really like Rafael's proposal to pass a list of mutations that has
 happened to the notification callbacks. This has the advantage that
 scripts get *all* the changes that has happened at once, making it
 possible to make decisions based on all changes made, rather than
 piece-wise getting the information in separate callbacks. It also has
 the advantage that we can provide much more detailed information
 without having to make multiple calls from C++ to JS which is good for
 performance. For example it seems very doable to provide lists of all
 nodes that has been removed and added while still keeping performance
 reasonable.


Enthusiastically agreed.

I'll write up a proposal based on this idea. Others should feel free
 to beat me to it :)


Nice!  Looking forward to it.

The main concern that I have with this proposal is that it's so
 different from mutation events that it might not satisfy the same use
 cases. Consider a widget implementation that currently observes the
 DOM using mutation events and makes it possible to write code like:

 myWidgetBackedElement.appendChild(someNode);
 myWidgetBackedElement.someFunction();

 where someFunction depends on state which is updated by the mutation
 event handler. Such a widget implementation is simply not doable with
 these semi-asynchronous callbacks.


Right.  But on the other hand, if this code were to run inside a mutation
observer, it won't work in your proposal either.  So the questions is
whether writing a function that depends on state updated by the mutation
observer without a mutation observer, and then later calling it inside a
mutation observer happens frequently enough to annoy developers or not.

On the other hand, maybe this isn't a big deal. We are definitely
 short on use cases for mutation events in general which is a problem.


Agreed.  We probably need more real-world use cases.

- Ryosuke


Re: Mutation events replacement

2011-07-07 Thread Ryosuke Niwa
On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa rn...@webkit.org wrote:

 Right.  But on the other hand, if this code were to run inside a mutation
 observer, it won't work in your proposal either.  So the questions is
 whether writing a function that depends on state updated by the mutation
 observer without a mutation observer, and then later calling it inside a
 mutation observer happens frequently enough to annoy developers or not.


I meant to say *writing a function that depends on state updated by the
mutation observer outside mutation observers, and later calling it inside a
mutation observer*

- Ryosuke


Re: Mutation events replacement

2011-07-07 Thread Olli Pettay

On 07/07/2011 10:18 PM, Jonas Sicking wrote:

Hi All,

I've finally caught up on all the emails in this thread. Here are my
impressions so far.

I don't think John J Barton's proposal to fire before mutation
notifications is doable. Trying to make all APIs that would, or
could, mutate the DOM throw or otherwise fail would be much too
complex to implement and too surprising use. The list of APIs that
we'd have to black-list would be far too long and would be heavily
dependent on implementation strategies.

We could take the approach workers take and make such scripts execute
in a wholly different context where the harmful APIs simply aren't
accessible. However such a context would, like workers, not have
access to any of the normal functions or variables that the page
normally uses. So I don't really see what you would do in such a
context? I.e. what use cases would be solved if all you can do is run
calculations, but not otherwise synchronously communicate with the
outside world. And how would you tell such a notification which nodes
are being modified since you can't pass references to the nodes as
Nodes are one of the APIs that we don't want to expose. Additionally,
how would you even register the callback that should be called when
the notification fires? Note that workers always load a wholly new
javascript file, there is no way to pass function references to it.

In short before spending more time on this, I'd like to see a
comprehensive proposal, including a description of the use cases it
solves and how it solves them. I strongly doubt that this approach is
practical.


I really like Rafael's proposal to pass a list of mutations that has
happened to the notification callbacks. This has the advantage that
scripts get *all* the changes that has happened at once, making it
possible to make decisions based on all changes made, rather than
piece-wise getting the information in separate callbacks. It also has
the advantage that we can provide much more detailed information
without having to make multiple calls from C++ to JS which is good for
performance. For example it seems very doable to provide lists of all
nodes that has been removed and added while still keeping performance
reasonable.

I'll write up a proposal based on this idea. Others should feel free
to beat me to it :)



I agree that having list of mutations is a good thing.



I'm less convinced of the part of Rafael's proposal that changes
*when* notifications are called. If I understand the proposal, the
idea is to not fire any notifications directly when the mutation
happens, but instead all notifications are as soon as control is
returned to the event loop. I.e. it's basically fully asynchronous,
except that instead of adding a task to the end of the event queue,
one is added to the beginning of it.


And I also agree with this.

I don't quite see the reason to postpone notification to happen
basically after all the other code in JS stack has run.
Having a queue of notifications which are handled after outermost
mutation should be quite easy to understand. Also, that way converting
code from mutation events to mutation listeners should be easier.




Rafael, please do correct me if I'm wrong on the above.

The main concern that I have with this proposal is that it's so
different from mutation events that it might not satisfy the same use
cases. Consider a widget implementation that currently observes the
DOM using mutation events and makes it possible to write code like:

myWidgetBackedElement.appendChild(someNode);
myWidgetBackedElement.someFunction();

where someFunction depends on state which is updated by the mutation
event handler. Such a widget implementation is simply not doable with
these semi-asynchronous callbacks.

On the other hand, maybe this isn't a big deal. We are definitely
short on use cases for mutation events in general which is a problem.


Finally, I actually do think it would be possible to create an API
which allow pages to batch notifications. Something like this should
work:

document.notificationBatch(function() {
   element1.appendChild(someNode);
   element2.setAttribute(foo, bar);
});

This would batch all notifications while inside the batch. However,
I'm not convinced we should add such an API, at least in version one.
And definitely not until there are proper use cases. Mutation events
sort have have the ability to batch notifications by calling
stopImmediatePropagation on events when they are fired and then
manually firing events once the batch is done. Is this something
people have done? Can anyone point to examples?

Additionally, we'd have to decide what to do if someone spins the
event loop inside a batch, for example by calling alert() or use
synchronous XHR.

/ Jonas







Re: Mutation events replacement

2011-07-07 Thread Rafael Weinstein
On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa rn...@webkit.org wrote:
 On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking jo...@sicking.cc wrote:

 I don't think John J Barton's proposal to fire before mutation
 notifications is doable.

 I concur.  Being synchronous was one of the reasons why the existing DOM
 mutation events don't work.  We shouldn't adding yet-another synchronous
 event here.

 In short before spending more time on this, I'd like to see a
 comprehensive proposal, including a description of the use cases it
 solves and how it solves them. I strongly doubt that this approach is
 practical.

 Totally agreed.

 I really like Rafael's proposal to pass a list of mutations that has
 happened to the notification callbacks. This has the advantage that
 scripts get *all* the changes that has happened at once, making it
 possible to make decisions based on all changes made, rather than
 piece-wise getting the information in separate callbacks. It also has
 the advantage that we can provide much more detailed information
 without having to make multiple calls from C++ to JS which is good for
 performance. For example it seems very doable to provide lists of all
 nodes that has been removed and added while still keeping performance
 reasonable.

 Enthusiastically agreed.

 I'll write up a proposal based on this idea. Others should feel free
 to beat me to it :)

 Nice!  Looking forward to it.

 The main concern that I have with this proposal is that it's so
 different from mutation events that it might not satisfy the same use
 cases. Consider a widget implementation that currently observes the
 DOM using mutation events and makes it possible to write code like:

 myWidgetBackedElement.appendChild(someNode);
 myWidgetBackedElement.someFunction();

 where someFunction depends on state which is updated by the mutation
 event handler. Such a widget implementation is simply not doable with
 these semi-asynchronous callbacks.

 Right.  But on the other hand, if this code were to run inside a mutation
 observer, it won't work in your proposal either.  So the questions is
 whether writing a function that depends on state updated by the mutation
 observer without a mutation observer, and then later calling it inside a
 mutation observer happens frequently enough to annoy developers or not.

 On the other hand, maybe this isn't a big deal. We are definitely
 short on use cases for mutation events in general which is a problem.

Right. Olli  Jonas, I'd really like to understand your thinking about this.

Are we not understanding something about your proposal?

If accommodating the above is a goal, it seems like the only option is
to have mutation events be fully synchronous. I.e. It doesn't seem
acceptable to encourage a widget author to expose an API that depends
on never being called inside a mutation callback and/or prevents it
from being used as a building block for a higher-level abstraction.


 Agreed.  We probably need more real-world use cases.
 - Ryosuke




Re: Mutation events replacement

2011-07-07 Thread Olli Pettay

On 07/08/2011 12:29 AM, Olli Pettay wrote:

On 07/07/2011 10:18 PM, Jonas Sicking wrote:

Hi All,

I've finally caught up on all the emails in this thread. Here are my
impressions so far.

I don't think John J Barton's proposal to fire before mutation
notifications is doable. Trying to make all APIs that would, or
could, mutate the DOM throw or otherwise fail would be much too
complex to implement and too surprising use. The list of APIs that
we'd have to black-list would be far too long and would be heavily
dependent on implementation strategies.

We could take the approach workers take and make such scripts execute
in a wholly different context where the harmful APIs simply aren't
accessible. However such a context would, like workers, not have
access to any of the normal functions or variables that the page
normally uses. So I don't really see what you would do in such a
context? I.e. what use cases would be solved if all you can do is run
calculations, but not otherwise synchronously communicate with the
outside world. And how would you tell such a notification which nodes
are being modified since you can't pass references to the nodes as
Nodes are one of the APIs that we don't want to expose. Additionally,
how would you even register the callback that should be called when
the notification fires? Note that workers always load a wholly new
javascript file, there is no way to pass function references to it.

In short before spending more time on this, I'd like to see a
comprehensive proposal, including a description of the use cases it
solves and how it solves them. I strongly doubt that this approach is
practical.


I really like Rafael's proposal to pass a list of mutations that has
happened to the notification callbacks. This has the advantage that
scripts get *all* the changes that has happened at once, making it
possible to make decisions based on all changes made, rather than
piece-wise getting the information in separate callbacks. It also has
the advantage that we can provide much more detailed information
without having to make multiple calls from C++ to JS which is good for
performance. For example it seems very doable to provide lists of all
nodes that has been removed and added while still keeping performance
reasonable.

I'll write up a proposal based on this idea. Others should feel free
to beat me to it :)



I agree that having list of mutations is a good thing.



I'm less convinced of the part of Rafael's proposal that changes
*when* notifications are called. If I understand the proposal, the
idea is to not fire any notifications directly when the mutation
happens, but instead all notifications are as soon as control is
returned to the event loop. I.e. it's basically fully asynchronous,
except that instead of adding a task to the end of the event queue,
one is added to the beginning of it.


And I also agree with this.

I don't quite see the reason to postpone notification to happen
basically after all the other code in JS stack has run.


Especially since the asynchronous-like approach behaves strangely when
spin the event loop is needed.






Having a queue of notifications which are handled after outermost
mutation should be quite easy to understand. Also, that way converting
code from mutation events to mutation listeners should be easier.




Rafael, please do correct me if I'm wrong on the above.

The main concern that I have with this proposal is that it's so
different from mutation events that it might not satisfy the same use
cases. Consider a widget implementation that currently observes the
DOM using mutation events and makes it possible to write code like:

myWidgetBackedElement.appendChild(someNode);
myWidgetBackedElement.someFunction();

where someFunction depends on state which is updated by the mutation
event handler. Such a widget implementation is simply not doable with
these semi-asynchronous callbacks.

On the other hand, maybe this isn't a big deal. We are definitely
short on use cases for mutation events in general which is a problem.


Finally, I actually do think it would be possible to create an API
which allow pages to batch notifications. Something like this should
work:

document.notificationBatch(function() {
element1.appendChild(someNode);
element2.setAttribute(foo, bar);
});

This would batch all notifications while inside the batch. However,
I'm not convinced we should add such an API, at least in version one.
And definitely not until there are proper use cases. Mutation events
sort have have the ability to batch notifications by calling
stopImmediatePropagation on events when they are fired and then
manually firing events once the batch is done. Is this something
people have done? Can anyone point to examples?

Additionally, we'd have to decide what to do if someone spins the
event loop inside a batch, for example by calling alert() or use
synchronous XHR.

/ Jonas











Re: Mutation events replacement

2011-07-07 Thread Jonas Sicking
On Thu, Jul 7, 2011 at 2:32 PM, Rafael Weinstein rafa...@google.com wrote:
 On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa rn...@webkit.org wrote:
 On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking jo...@sicking.cc wrote:

 I don't think John J Barton's proposal to fire before mutation
 notifications is doable.

 I concur.  Being synchronous was one of the reasons why the existing DOM
 mutation events don't work.  We shouldn't adding yet-another synchronous
 event here.

 In short before spending more time on this, I'd like to see a
 comprehensive proposal, including a description of the use cases it
 solves and how it solves them. I strongly doubt that this approach is
 practical.

 Totally agreed.

 I really like Rafael's proposal to pass a list of mutations that has
 happened to the notification callbacks. This has the advantage that
 scripts get *all* the changes that has happened at once, making it
 possible to make decisions based on all changes made, rather than
 piece-wise getting the information in separate callbacks. It also has
 the advantage that we can provide much more detailed information
 without having to make multiple calls from C++ to JS which is good for
 performance. For example it seems very doable to provide lists of all
 nodes that has been removed and added while still keeping performance
 reasonable.

 Enthusiastically agreed.

 I'll write up a proposal based on this idea. Others should feel free
 to beat me to it :)

 Nice!  Looking forward to it.

 The main concern that I have with this proposal is that it's so
 different from mutation events that it might not satisfy the same use
 cases. Consider a widget implementation that currently observes the
 DOM using mutation events and makes it possible to write code like:

 myWidgetBackedElement.appendChild(someNode);
 myWidgetBackedElement.someFunction();

 where someFunction depends on state which is updated by the mutation
 event handler. Such a widget implementation is simply not doable with
 these semi-asynchronous callbacks.

 Right.  But on the other hand, if this code were to run inside a mutation
 observer, it won't work in your proposal either.  So the questions is
 whether writing a function that depends on state updated by the mutation
 observer without a mutation observer, and then later calling it inside a
 mutation observer happens frequently enough to annoy developers or not.

 On the other hand, maybe this isn't a big deal. We are definitely
 short on use cases for mutation events in general which is a problem.

 Right. Olli  Jonas, I'd really like to understand your thinking about this.

 Are we not understanding something about your proposal?

 If accommodating the above is a goal, it seems like the only option is
 to have mutation events be fully synchronous. I.e. It doesn't seem
 acceptable to encourage a widget author to expose an API that depends
 on never being called inside a mutation callback and/or prevents it
 from being used as a building block for a higher-level abstraction.

It's definitely the case that APIs will behave differently inside the
mutation notification, and that the code example showed above would
not work from inside the notification.

Basically I'm asking people to tread carefully inside mutation
notifications and only do the minimal amount of data gathering.

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.

/ Jonas



Re: Mutation events replacement

2011-07-07 Thread John J Barton

Jonas Sicking wrote:

 We are definitely
short on use cases for mutation events in general which is a problem.
  
1. Graphical breakpoints. The user marks some DOM element or attribute 
to trigger break. The debugger inserts mutation listeners to watch for 
the event that causes that element/attribute to be created/modified. 
Then the debugger re-executes some code sequence and halts when the 
appropriate listener is entered. Placing the listeners high in the tree 
and analyzing all of the events is easier than trying to precisely add a 
listener since the tree will be modified during re-execution.


2. Graphical tracing. Recording all or part of the DOM creation. For 
visualization or analysis tools.  See for example Firebug's HTML panel 
with options Highlight Changes, Expand Changes, or Scroll Changes into View.


3. Client side dynamic translation. Intercept mutations and replace or 
extend them. This could be for user tools like scriptish or stylish, dev 
tools to inject marks or code, or for re-engineering complex sites for 
newer browser features.


jjb



Re: Mutation events replacement

2011-07-07 Thread Jonas Sicking
On Thu, Jul 7, 2011 at 3:21 PM, John J Barton
johnjbar...@johnjbarton.com wrote:
 Jonas Sicking wrote:

  We are definitely
 short on use cases for mutation events in general which is a problem.


 1. Graphical breakpoints. The user marks some DOM element or attribute to
 trigger break. The debugger inserts mutation listeners to watch for the
 event that causes that element/attribute to be created/modified. Then the
 debugger re-executes some code sequence and halts when the appropriate
 listener is entered. Placing the listeners high in the tree and analyzing
 all of the events is easier than trying to precisely add a listener since
 the tree will be modified during re-execution.

Debuggers can use internal APIs and doesn't need to be limited to
using APIs exposed to web pages.

In fact, in this case you probably want to use an internal API anyway
as to ensure that the debugger steps in before any other of the
mutation listeners have executed.

In gecko you can for example use the nsIMutationObserver notification
to implement this.

 2. Graphical tracing. Recording all or part of the DOM creation. For
 visualization or analysis tools.  See for example Firebug's HTML panel with
 options Highlight Changes, Expand Changes, or Scroll Changes into View.

See above.

 3. Client side dynamic translation. Intercept mutations and replace or
 extend them. This could be for user tools like scriptish or stylish, dev
 tools to inject marks or code, or for re-engineering complex sites for newer
 browser features.

I don't fully understand this. Can you give more concrete examples?

/ Jonas



Re: Mutation events replacement

2011-07-07 Thread John J Barton

Rafael Weinstein wrote:

On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa rn...@webkit.org wrote:
  

On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking jo...@sicking.cc wrote:


I don't think John J Barton's proposal to fire before mutation
notifications is doable.
  

I concur.  Being synchronous was one of the reasons why the existing DOM
mutation events don't work.  We shouldn't adding yet-another synchronous
event here.

However, my proposal need not be synchronous in the sense that is 
important here: 'before' mutation listeners need not able to mutate, 
only cancel.  So it's not yet another synchronous event.  Developers 
would use their handler to build a new mutation event and fire it on the 
next turn: it' s essentially asynchronous.

In short before spending more time on this, I'd like to see a
comprehensive proposal, including a description of the use cases it
solves and how it solves them. I strongly doubt that this approach is
practical.
  
There are lots of reasons why 'before' events may not be practical, 
including lack of enthusiasm on the part of the implementors.  You folks 
are the experts, I'm just trying to contribute another point of view. 
Thus I want to point out that for the critical issue of preventing 
mutation listeners from mutating, all you have to do to Jonas' algorithm 
is prepend:


0. If notifyingCallbacks is set to true, throw 
MutationNotAllowedInBeforeMutationCallbacks.


You don't have to to any thing to create a read-only DOM API because you 
already track all possible DOM modifications.   The clean-up from the 
throw is similar to the cancel and not different from any other clean-up 
you have to do if the mutation listener fails. 

This is of course not a comprehensive proposal.  I'm perfectly fine if 
you choose not to respond because you want to close off this discussion 
and I thank you for the replies so far.


jjb



Re: Mutation events replacement

2011-07-07 Thread Boris Zbarsky

On 7/7/11 6:28 PM, Jonas Sicking wrote:

In gecko you can for example use the nsIMutationObserver notification
to implement this.


Maybe you can, and maybe not.  Those fire at unsafe times and are 
limited in what you can do from them.  Arbitrary debugging doesn't fit 
the bill.


But yes, in general debuggers can use internal APIs with higher trust in 
the callee as needed.  The question is whether they can work within the 
resulting constraints.


-Boris



  1   2   3   >