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 a 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 advanta

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


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: 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: Findings from implementing "net-effect" projections

2011-08-17 Thread Rafael Weinstein
On Wed, Aug 17, 2011 at 3:17 AM, Olli Pettay  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 curren

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

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 any

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


That sounds great!  Thanks for doing that.

On Tue, Aug 16, 2011 at 8:21 AM, Anne van Kesteren  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


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   
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  as well? Any other constructs?  
showModalDialog() may be bad practice, but  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-15 Thread Anne van Kesteren
On Sat, 13 Aug 2011 17:41:32 +0200, Anne van Kesteren   
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   
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 Rafael Weinstein
On Thu, Aug 11, 2011 at 9:12 AM, Olli Pettay  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: 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
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  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 a> 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

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 a  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: Mutation events replacement

2011-08-10 Thread Ryosuke Niwa
On Wed, Aug 10, 2011 at 8:32 PM, Boris Zbarsky  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 > > 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-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 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 12: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.
>

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



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



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.



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  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: 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-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; 

Re: Mutation events replacement

2011-08-04 Thread David Flanagan

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?


- The pattern of passing a callback function to a constructor is novel 
in the DOM.  Will this confuse people too much?  Also, if the callback 
is passed to the constructor, there isn't a deregistration method.  I 
assume that you achieve this by calling unbatchAll(), but that seems 
non-parallel.  What if the ModificationCallback was passed to the 
individual batch/unbatch methods instead?


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


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

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: More use-cases for mutation events replacement

2011-07-26 Thread Aryeh Gregor
On Mon, Jul 25, 2011 at 11:12 PM, Sean Hogan  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: 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:



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:



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-25 Thread Dave Raggett

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

On Fri, Jul 22, 2011 at 6:58 PM, Jonas Sicking  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 Raggett  http://www.w3.org/People/Raggett




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

Re: Mutation events replacement

2011-07-24 Thread Aryeh Gregor
On Fri, Jul 22, 2011 at 11:54 AM, Boris Zbarsky  wrote:
> Actually, you can pretty easily do it in the other order (move the text into
> the , and then put the  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  wrote:
> On Fri, Jul 22, 2011 at 2:08 AM, Dave Raggett  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 

Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Fri, Jul 22, 2011 at 4:44 PM, Ryosuke Niwa  wrote:
> On Fri, Jul 22, 2011 at 3:58 PM, Jonas Sicking  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-22 Thread Ryosuke Niwa
On Fri, Jul 22, 2011 at 3:58 PM, Jonas Sicking  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 Thu, Jul 21, 2011 at 4:30 PM, Jonas Sicking  wrote:
> On Thu, Jul 21, 2011 at 8:19 AM, Olli Pettay  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 Jonas Sicking
On Fri, Jul 22, 2011 at 9:32 AM, 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.

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 Rafael Weinstein
On Tue, Jul 19, 2011 at 4:56 PM, Jonas Sicking  wrote:
> On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwa  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  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  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 

Re: Mutation events replacement

2011-07-22 Thread Jonas Sicking
On Thu, Jul 21, 2011 at 6:26 PM, Adam Klein  wrote:
> On Thu, Jul 21, 2011 at 4:30 PM, Jonas Sicking  wrote:
>> On Thu, Jul 21, 2011 at 8:19 AM, Olli Pettay  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 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 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 Raggett  http://www.w3.org/People/Raggett




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 a
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 , and then put the  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 Aryeh Gregor
On Thu, Jul 21, 2011 at 4:21 PM, Boris Zbarsky  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 
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: foo[barbaz]quz.  The result will be
something like foo[]quz.  How do you do that?  First delete
"bar" and "baz", then move "quz" to the , then remove the .
Or let's say you have foo[]bar and the user hits Enter -- you
first create an empty  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 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 Raggett  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-21 Thread Adam Klein
On Thu, Jul 21, 2011 at 4:30 PM, Jonas Sicking  wrote:
> On Thu, Jul 21, 2011 at 8:19 AM, Olli Pettay  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-21 Thread Jonas Sicking
On Thu, Jul 21, 2011 at 8:19 AM, Olli Pettay  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 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 Aryeh Gregor
On Wed, Jul 20, 2011 at 3: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?

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  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 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 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 Dave Raggett

On 20/07/11 21:34, David Flanagan wrote:

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




For the distributed editing use case, preserving the order of mutations 
is critical as is being able to identity the nodes involved. In my 
editing application I serialize changes to the DOM as JSON as an array 
of changes where each change is an object with properties that name the 
node (via a tumbler notation), the operation (e.g. insert, remove, 
move), and additional operation specific properties. Each mutation event 
is added to a temporary queue of sets of reversible changes. The changes 
are periodically serialized and transmitted via websockets.  The queue 
of reversible changes includes the DOM nodes and this is exploited to 
permit undo/redo operations. The tumbler references are computed during 
the mutation event handler since they may well change before the 
mutation is serialized and transmitted to the server.


I listen to mutation events for a div element with the content editable 
flag. I don't use the execCommand API as it currently varies too much 
from one browser to the next. However, I do have to deal with how 
browsers change the DOM in response to keystrokes, especially, enter, 
backspace and delete. Inserting a node is expensive as I have to 
serialize all of its content nodes. This is why a move operation is 
valuable.


The local undo/redo queue of changes has to be updated to reflect 
changes by other editing clients, but that is another story!


--
 Dave Raggett  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 Pettay  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 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 Raggett  http://www.w3.org/People/Raggett




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-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 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 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 
mailto:simetrical%2b...@gmail.com>> wrote:


On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan
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 Jonas Sicking
On Wed, Jul 20, 2011 at 10:30 AM, Olli Pettay  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 Ojan Vafai
On Wed, Jul 20, 2011 at 10:30 AM, Olli Pettay 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.
>

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 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 Ryosuke Niwa
On Tue, Jul 19, 2011 at 8:23 PM, Boris Zbarsky  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/20/11 12:11 PM, Ryosuke Niwa wrote:
On Wed, Jul 20, 2011 at 11:56 AM, Aryeh Gregor 
mailto:simetrical%2b...@gmail.com>> wrote:


On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan
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 helloworldw3c

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 Wed, Jul 20, 2011 at 12:53 PM, David Flanagan wrote:

>  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 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 11:56 AM, Aryeh Gregor wrote:

> On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan 
> 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?

e.g. say we have helloworldw3c

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 Aryeh Gregor
On Wed, Jul 20, 2011 at 1:43 AM, David Flanagan  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 Boris Zbarsky

On 7/20/11 2:19 PM, Bjoern Hoehrmann 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


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.



So the information being available as part of the API does
not necessarily imply performance problems.


It really does, sorry

-Boris



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 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 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:

  
  
  
document.body.style.marginLeft = "5px";
document.body.style.marginRight = "5px";
document.body.style.top = "17px";
alert(document.body.getAttribute("style"));
  

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

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

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 Ryosuke Niwa
On Wed, Jul 20, 2011 at 9:32 AM, Dave Raggett  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 Dave Raggett

On 20/07/11 17:05, Ryosuke Niwa wrote:
On Wed, Jul 20, 2011 at 8:43 AM, 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.


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 Raggett  http://www.w3.org/People/Raggett




Re: Mutation events replacement

2011-07-20 Thread Ryosuke Niwa
On Wed, Jul 20, 2011 at 8:43 AM, 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.
>

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 Jonas Sicking
On Wed, Jul 20, 2011 at 5:20 AM, Olli Pettay  wrote:
> On 07/20/2011 02:01 AM, Jonas Sicking wrote:
>>
>> On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sicking  wrote:
>>>
>>> On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein
>>>  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 i

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 Raggett  http://www.w3.org/People/Raggett




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 Olli Pettay

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

On Thu, Jul 7, 2011 at 6:38 PM, Jonas Sicking  wrote:

On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein  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 noti

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 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 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 Raggett  http://www.w3.org/People/Raggett




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 Raggett  http://www.w3.org/People/Raggett




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-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 Ryosuke Niwa
On Tue, Jul 19, 2011 at 4:56 PM, Jonas Sicking  wrote:

> On Tue, Jul 19, 2011 at 4: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 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 Jonas Sicking
On Tue, Jul 19, 2011 at 4:18 PM, Ryosuke Niwa  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  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
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  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 Thu, Jul 7, 2011 at 6:38 PM, Jonas Sicking  wrote:
> On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein  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 n

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 > 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 Tab Atkins Jr.
On Fri, Jul 8, 2011 at 6:55 AM, Sean Hogan  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-08 Thread Jonas Sicking
On Fri, Jul 8, 2011 at 5:21 AM, 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
>>   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 Ryosuke Niwa
On Fri, Jul 8, 2011 at 5:21 AM, Sean Hogan  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 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
  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 timeless
On Thu, Jul 7, 2011 at 6:21 PM, John J Barton
 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 8:28 AM, Jonas Sicking wrote:

On Thu, Jul 7, 2011 at 3:21 PM, John J Barton
  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-07 Thread John J. Barton

On 7/7/2011 6:38 PM, Jonas Sicking wrote:

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

/ Jonas
Hmm... you don't believe the use cases and info on how mutation events 
are being used that Dave and I have posted and you don't have any 
alternatives.  Perhaps the conservative solution is do nothing.


You might ask Prof. Jan Vitek if his infrastructure can give you any 
information on mutation event uses.  He may also other ways to get such 
answers.


jjb

jjb




Re: Mutation events replacement

2011-07-07 Thread Jonas Sicking
On Thu, Jul 7, 2011 at 5:23 PM, Rafael Weinstein  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.

/ Jonas



Re: Mutation events replacement

2011-07-07 Thread Rafael Weinstein
On Thu, Jul 7, 2011 at 3:19 PM, Jonas Sicking  wrote:
> On Thu, Jul 7, 2011 at 2:32 PM, Rafael Weinstein  wrote:
>> On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa  wrote:
>>> On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking  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.

Heh. We're kind of in violent agreement here. =-)

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

It occurs to me that the main use case raised in favor of sync
mutation actions is more or less the custom component/widget use case.
This seems to border alot on XBL2. Maybe it's useful to look at it
through that lens?

/me grabs blindly at the air for good ideas about how to come to consensus.

>
> / Jonas
>



Re: Mutation events replacement

2011-07-07 Thread John J Barton

Olli Pettay wrote:

On 07/08/2011 01:43 AM, John J Barton wrote:

Rafael Weinstein wrote:

On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa  wrote:
On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking  
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.


I don't understand how this could really work.

Just as an example:
What if the mutation listener spins event loop which ends up
touching parser so that it tries to insert new content to the document.
I would like to learn what you mean here.  The only way I know how to 
suspend an event and spin a new one is via the debugger API.  Is that 
the case you are concerned with?

That mutation wouldn't be allowed. What should be done to that
data which the parser can't add to the document?

Discard, same as any exception, not a special case.

jjb






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 Jonas Sicking
On Thu, Jul 7, 2011 at 3:43 PM, John J Barton
 wrote:
 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.

This is more comprehensive than anything else so far ;-)

Unfortunately being able to mutate the DOM itself isn't enough. We
also need to prevent a whole host of other things, such as performing
synchronous XHR, navigating a document, calling alert() or
showModalDialog(), likely setting document.domain, setting scroll
positions etc. The list will be long and implementation dependent.
This is because all these things can indirectly mutate the DOM.

So more comprehensive is still needed. And unfortunately you'd likely
need to do research into each browser implementation and see what
invariants they depend on and which APIs can change those invariants.
This is extra hard given that there are at least two non-open-source
implementations out there.

/ Jonas



Re: Mutation events replacement

2011-07-07 Thread Olli Pettay

On 07/08/2011 01:43 AM, John J Barton wrote:

Rafael Weinstein wrote:

On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa  wrote:

On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking  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.


I don't understand how this could really work.

Just as an example:
What if the mutation listener spins event loop which ends up
touching parser so that it tries to insert new content to the document.
That mutation wouldn't be allowed. What should be done to that
data which the parser can't add to the document?





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 Ryosuke Niwa
On Thu, Jul 7, 2011 at 3:43 PM, John J Barton
wrote:
>
>  On Thu, Jul 7, 2011 at 1:58 PM, Ryosuke Niwa  wrote:
>>
>>
>>> On Thu, Jul 7, 2011 at 12:18 PM, Jonas Sicking  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.


Being able to cancel is dangerous enough for me.

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


Implementing this feature is excessively hard and time-consuming for many
implementors as far as I know.

- Ryosuke


  1   2   3   >