Model-driven Views

2011-04-25 Thread Rafael Weinstein
Myself and a few other chromium folks have been working on a design
for a formalized separation between View and Model in the browser,
with needs of web applications being the primary motivator.

Our ideas are implemented as an experimental Javascript library:
https://code.google.com/p/mdv/ and the basic design is described here:
http://mdv.googlecode.com/svn/trunk/docs/design_intro.html. It's not
complete and there are things we're not happy with, but it's
self-consistent enough that you can start to imagine what a full
design might look like.

We hope to get others interested in collecting requirements/use cases
and fleshing out a good solution.

We're starting the discussion here because a few people in this group
from whom we got early feedback felt that it would be most appropriate
place and, further, that this work bears some relation to XBL.

What do you think?




Re: Model-driven Views

2011-04-26 Thread Rafael Weinstein
Thank, Nathan.

I hadn't known of Knockout, but it looks pretty great. Conceptually,
its design is very similar to MDV. Notably, the two big items:

-Observable JS object properties and Arrays.
-DOM-based template production (although they work with JQuery
templates which are string-based).

The automatic dependency tracking is interesting. We looked at some
work published by Adobe/Texas A&M on declarative property models
(http://parasol.cs.tamu.edu/~jarvi/papers/gpce08.pdf,  a very
unsophisticated example in our use_cases
http://mdv.googlecode.com/svn/trunk/use_cases/property_model.html).

Knockout re-affirms for me that:

-Dynamic web apps increasingly load one or more views as HTML but talk
to their servers via a data API. Having to write lots of imperative
code which shuttles data into & out-of the DOM is a bummer. It's not
especially interesting code and it tends to be error prone. A
declarative approach can do much better.

-There are basically only two approaches to templating: DOM-based
(MDV, Knockout, Angular, JSTemplate) & String-based (JQuery, and a ton
of others). In the video, Steve explains (at about 12:40), indirectly,
that DOM-based is required if you are going to dynamically update your
view: performance. The other reason is DOM-stability.

-In order to get this kind of approach to work, you need some way of
observing when data has changed. There are four options for this that
I know of:

1) Run-time support for direct observation (e.g. Adobe Flex, WPF)
2) Value-holder pattern (e.g. Knockout, Sproutcore)
3) Proxy pattern (e.g. MDV)
4) Dirty checking (e.g. Angular)

The only two options available to webdevs are 2 & 4, and both
currently require some fairly unnatural contortions.


On Mon, Apr 25, 2011 at 12:26 PM, Nathan Kitchen  wrote:
> Have you heard of knockout.js? It's an MVVM pattern based on JQuery, if
> you're not aware of it you may be interested to see their approach.
> Official site:
> http://knockoutjs.com/
> Recent MIX event:
> http://channel9.msdn.com/Events/MIX/MIX11/FRM08
> Just FYI as it was related...
>
> On 23 April 2011 01:35, Rafael Weinstein  wrote:
>>
>> Myself and a few other chromium folks have been working on a design
>> for a formalized separation between View and Model in the browser,
>> with needs of web applications being the primary motivator.
>>
>> Our ideas are implemented as an experimental Javascript library:
>> https://code.google.com/p/mdv/ and the basic design is described here:
>> http://mdv.googlecode.com/svn/trunk/docs/design_intro.html. It's not
>> complete and there are things we're not happy with, but it's
>> self-consistent enough that you can start to imagine what a full
>> design might look like.
>>
>> We hope to get others interested in collecting requirements/use cases
>> and fleshing out a good solution.
>>
>> We're starting the discussion here because a few people in this group
>> from whom we got early feedback felt that it would be most appropriate
>> place and, further, that this work bears some relation to XBL.
>>
>> What do you think?
>>
>>
>
>



Re: Model-driven Views

2011-04-27 Thread Rafael Weinstein
Hi Olli,

Thanks very much for taking time to read & digest our design.

I'm going to use this thread to start a FAQ on our project site. Some
of these questions have answers that deserve more detail than is wise
to put in an email =-).

On Wed, Apr 27, 2011 at 8:33 AM, Olli Pettay  wrote:
> HI Rafael,
>
>
> few random comments, or mainly just random questions :)
>
> On 04/23/2011 03:35 AM, Rafael Weinstein wrote:
>>
>> Myself and a few other chromium folks have been working on a design
>> for a formalized separation between View and Model in the browser,
>> with needs of web applications being the primary motivator.
>>
>> Our ideas are implemented as an experimental Javascript library:
>> https://code.google.com/p/mdv/ and the basic design is described here:
>> http://mdv.googlecode.com/svn/trunk/docs/design_intro.html. It's not
>> complete and there are things we're not happy with, but it's
>> self-consistent enough that you can start to imagine what a full
>> design might look like.
>>
>> We hope to get others interested in collecting requirements/use cases
>> and fleshing out a good solution.
>
> Would be good to know what are the use cases you had in mind.

I'm never sure if I'm using the term "use case" correctly =-).

Our primary motivator is the needs of web applications, but if we can
create a good solution for more static cases in the process it'll be
that much more a win.

By "collecting use cases" I was thinking more about specific patterns
that emerge in composing views for applications. One very common
example is master/detail.

>
>
>>
>> We're starting the discussion here because a few people in this group
>> from whom we got early feedback felt that it would be most appropriate
>> place and, further, that this work bears some relation to XBL.
>
> Not sure why this had some relation with XBL. Unless you are
> planning to put the template based DOM nodes to anonymous DOM.

I'm creating a FAQ question for this. Will reply again when its ready.

>
>
>>
>> What do you think?
>>
>
> - Is this something you'd like to be implemented in the browsers,

Yes.

>  and if yes, why? What would be the reasons to not just use script
>  libraries (like your prototype).

FAQ item also coming for this.

>  If the have-to-change-HTML-parser like parts could be removed,
>  this all feels like something for JS + Proxy objects, just like what
>  you've done in the prototype.
>
> - Is there a reason why you have chosen to create yet another
>  datastorage like object (although your Model is just a kind of
>  proxy)? Couldn't you reuse for example IndexedDB?
>  XUL Templates can be bound to sqlite, XML and RDF.

The goal isn't to create a storage mechanism, its to free the
application from directly manipulating the DOM and allow it to operate
on its data. To *say* what it wants to happen to the UI rather than to
do it. If JS objects were directly observable, that would be ideal --
but it seems unlikely that runtime implementors will take the perf
hit.

Allowing bindings to IndexedDB is a cool idea. We should explore that
more. In general, there are a number of things we should consider for
sourcing data.

>
> - Model looks in many ways like JS-fied and simplified XForms
>  model/instance. Have you investigated if there are important use cases
>  which XForms can handle (because it has more powerful binding
>  mechanism) but MDV can't? Are there plans to extend

I've read the XForms spec, but I don't feel qualified to comment on
use cases that it can better handle. It sounds like you might...? ;-)

>  the "Path language", or ideas how it could be extended? (XForms has
>  extensions to XPath)
>  In general I'm not too happy to see yet another
>  selector/path language. Would be great if some existing one could
>  be reused. Sure, some parts of the proposed language look quite a bit
>  like JSON, but there are other parts which are something very
>  different.

The goal wasn't to create a path/selector language. The basic idea is
simple JSON dot-notation. The funny syntax you see (../, ./, /) is our
solution to three patterns that come up frequently in templating:

-You're in an iteration, but you need a value which is "just above
your head" (e.g.
http://mdv.googlecode.com/svn/trunk/capabilities/path_reference_ancestor.html)

-You're down deep in a object graph rendering you're output, but you
need a global value. An example might be composing a URL for an
application which is versioned. You need to grab a "global" static
path and combine it with some local state (e.g. userId). (e.g.
https://code.google.com/p/mdv/s

Re: Model-driven Views

2011-05-02 Thread Rafael Weinstein
XBL
is apropos for similar reasons.


8) Script has no ability to expand the declarative semantics of HTML

-The operation of web apps is already opaque to robots on the web. The
data already canonically live in the JS heap.

-Allowing declarative support for separating view & model opens the
way for web-automation to better understand what's going on.


9) We need to lead here and create a "pit of success". The current pit
is full of nasty stuff.

-Creating a templating library that mostly works is easy and fun.
Every framework is going to have one and everyone who creates one is
going to make the same XSS mistakes (by use of innerHTML), and finally
discover that it doesn't really work with dynamic applications.

-The web is deeply engrained in the approach of imperative templating.
It's non-obvious to typical webdevs why this approach doesn't have
legs.

-The web has succeeded dramatically by making complex things easy.
Offering support here will raise the bar substantively for what a
novice webdev can achieve. Web applications shouldn't just be the
domain of web ninjas.



On Thu, Apr 28, 2011 at 2:02 AM, Maciej Stachowiak  wrote:
>
> On Apr 27, 2011, at 6:46 PM, Rafael Weinstein wrote:
>
>>>
>>>
>>>>
>>>> What do you think?
>>>>
>>>
>>> - Is this something you'd like to be implemented in the browsers,
>>
>> Yes.
>>
>>>  and if yes, why? What would be the reasons to not just use script
>>>  libraries (like your prototype).
>>
>> FAQ item also coming for this.
>
> Having heard Rafael's spiel for this previously, I believe there are some 
> things that templating engines want to do, which are hard to do efficiently 
> and conveniently using the existing Web platform.
>
> However, I think it would be better to add primitives to the Web platform 
> that could be used by the many templating libraries that already exist, at 
> least as a first step:
>
> - There is a lot of code built using many of the existing templating 
> solutions. If we provide primitives that let those libraries become more 
> efficient, that is a greater immediate payoff than creating a new templating 
> system, where Web apps would have to be rewritten to take advantage.
>
> - It seems somewhat hubristic to assume that a newly invented templating 
> library is so superior to all the already existing solutions that we should 
> encode its particular design choices into the Web platform immediately.
>
> - This new templating library doesn't have enough real apps built on it yet 
> to know if it is a good solution to author problems.
>
> - Creating APIs is best done incrementally. API is forever, on the Web.
>
> - Looking at the history of querySelector(), I come to the following 
> conclusion: when there are already a lot of library-based solutions to a 
> problem, the best approach is to provide technology that can be used inside 
> those libraries to improve them; this is more valuable than creating an API 
> with a primary goal of direct use. querySelector gets used a lot more via 
> popular JavaScript libraries than directly, and should have paid more 
> attention to that use case in the first place.
>
> Perhaps there are novel arguments that will dissuade me from this line of 
> thinking, but these are my tentative thoughts.
>
> Regards,
> Maciej
>
>



Re: Model-driven Views

2011-05-06 Thread Rafael Weinstein
Thanks everyone for your consideration.

It sounds like the group wants to proceed by looking first at missing
primitives. Maciej is right that one of them is the ability to declare
inert DOM structures, but my feeling is that it's probably best to
start with the central problem:

-There's no way to observe mutations to JS objects.

Current approaches resort to a number of hacks which create really terrible
artifacts in application code. The ECMA Script Proxy mechanism may be the
best starting point, but there's a related
problem with timing that isn't addressed.

I'll start a thread shortly on this problem detailing our best
understanding, and make an attempt to loop in library authors to get their
view.

On Thu, Apr 28, 2011 at 2:02 AM, Maciej Stachowiak  wrote:
>
> On Apr 27, 2011, at 6:46 PM, Rafael Weinstein wrote:
>
>>>
>>>
>>>>
>>>> What do you think?
>>>>
>>>
>>> - Is this something you'd like to be implemented in the browsers,
>>
>> Yes.
>>
>>>  and if yes, why? What would be the reasons to not just use script
>>>  libraries (like your prototype).
>>
>> FAQ item also coming for this.
>
> Having heard Rafael's spiel for this previously, I believe there are some
things that templating engines want to do, which are hard to do efficiently
and conveniently using the existing Web platform.
>
> However, I think it would be better to add primitives to the Web platform
that could be used by the many templating libraries that already exist, at
least as a first step:
>
> - There is a lot of code built using many of the existing templating
solutions. If we provide primitives that let those libraries become more
efficient, that is a greater immediate payoff than creating a new templating
system, where Web apps would have to be rewritten to take advantage.
>
> - It seems somewhat hubristic to assume that a newly invented templating
library is so superior to all the already existing solutions that we should
encode its particular design choices into the Web platform immediately.
>
> - This new templating library doesn't have enough real apps built on it
yet to know if it is a good solution to author problems.
>
> - Creating APIs is best done incrementally. API is forever, on the Web.
>
> - Looking at the history of querySelector(), I come to the following
conclusion: when there are already a lot of library-based solutions to a
problem, the best approach is to provide technology that can be used inside
those libraries to improve them; this is more valuable than creating an API
with a primary goal of direct use. querySelector gets used a lot more via
popular JavaScript libraries than directly, and should have paid more
attention to that use case in the first place.
>
> Perhaps there are novel arguments that will dissuade me from this line of
thinking, but these are my tentative thoughts.
>
> Regards,
> Maciej
>
>


Re: Mutation events replacement

2011-06-29 Thread Rafael Weinstein
On Wed, Jun 29, 2011 at 7:13 AM, Aryeh Gregor  wrote:
> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking  wrote:
>> This new proposal solves both these by making all the modifications
>> first, then firing all the events. Hence the implementation can
>> separate implementing the mutating function from the code that sends
>> out notifications.
>>
>> Conceptually, you simply queue all notifications in a queue as you're
>> making modifications to the DOM, then right before returning from the
>> function you insert a call like "flushAllPendingNotifications()". This
>> way you don't have to care at all about what happens when those
>> notifications fire.
>
> So when exactly are these notifications going to be fired?  In
> particular, I hope non-DOM Core specifications are going to have
> precise control over when they're fired.  For instance, execCommand()
> will ideally want to do all its mutations at once and only then fire
> the notifications (which I'm told is how WebKit currently works).  How
> will this work spec-wise?  Will we have hooks to say things like
> "remove a node but don't fire the notifications yet", and then have to
> add an extra line someplace saying to fire all the notifications?
> This could be awkward in some cases.  At least personally, I often say
> things like "call insertNode(foo) on the range" in the middle of a
> long algorithm, and I don't want magic happening at that point just
> because DOM Range fires notifications before returning from
> insertNode.
>
> Also, even if specs have precise control, I take it the idea is
> authors won't, right?  If a library wants to implement some fancy
> feature and be compatible with users of the library firing these
> notifications, they'd really want to be able to control when
> notifications are fired, just like specs want to.  In practice, the
> only reason this isn't an issue with DOM mutation events is because
> they can say "don't use them", and in fact people rarely do use them,
> but that doesn't seem ideal -- it's just saying library authors
> shouldn't bother to be robust.

In working on Model Driven Views (http://code.google.com/p/mdv), we've
run into exactly this problem, and have developed an approach we think
is promising.

The idea is to more or less take Jonas's proposal, but instead of
firing callbacks immediately before the outer-most mutation returns,
mutations are recorded for a given observer and handed to it as an
in-order sequence at the "end" of the event.

var observer = window.createMutationObserver(callback);
document.body.addSubtreeChangedObserver(observer);
document.body.addSubtreeAttributeChangedObserver(observer);
...
var div = document.createElement('div');
document.body.appendChild(div);
div.setAttribute('data-foo', 'bar');
div.innerHTML = 'something something else';
div.removeChild(div.childNodes[1]);
...

// mutationList is an array, all the entries added to
// |observer| during the preceding script event
function callback(mutationList) {
// mutationList === [
//  { type: 'ChildlistChanged', target: document.body, inserted: [div] },
//  { type: 'AttributeChanged', target: div, attrName: 'data-foo' },
//  { type: 'ChildlistChanged', target: div, inserted: [b, i] },
//  { type: 'ChildlistChanged', target: div, removed: [i] }
// ];
}

>
> Maybe this is a stupid question, since I'm not familiar at all with
> the use-cases involved, but why can't we delay firing the
> notifications until the event loop spins?  If we're already delaying
> them such that there are no guarantees about what the DOM will look
> like by the time they fire, it seems like delaying them further
> shouldn't hurt the use-cases too much more.  And then we don't have to
> put further effort into saying exactly when they fire for each method.

Agreed.

For context, after considering this issue, we've tentatively concluded
a few things that don't seem to be widely agreed upon:

1) In terms of when to notify observers: Sync is too soon. Async (add
a Task) is too late.

- The same reasoning for why firing sync callbacks in the middle of
DOM operations is problematic for C++ also applies to application
script. Calling mutation observers synchronously can invalidate the
assumptions of the code which is making the modifications. It's better
to allow one bit of code to finish doing what it needs to and let
mutation observers operate "later" over the changes.

- Many uses of mutation events would actually *prefer* to not run sync
because the "originating" code may be making multiple changes which
more or less comprise a "transaction". For consistency and
performance, the abstraction which is watching changes would like to
operate on the final state.

- However, typical uses of mutation events do want to operate more or
less "in the same event" because they are trying to create a new
consistent state. They'd like to run after the "application code" is
finished, but before paint occurs or the next scheduled event runs.

2) Because the system must allow mult

Re: Mutation events replacement

2011-06-30 Thread Rafael Weinstein
On Thu, Jun 30, 2011 at 4:05 AM, Olli Pettay  wrote:
> On 06/30/2011 12:54 AM, Rafael Weinstein wrote:
>>
>> On Wed, Jun 29, 2011 at 7:13 AM, Aryeh Gregor
>>  wrote:
>>>
>>> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking  wrote:
>>>>
>>>> This new proposal solves both these by making all the modifications
>>>> first, then firing all the events. Hence the implementation can
>>>> separate implementing the mutating function from the code that sends
>>>> out notifications.
>>>>
>>>> Conceptually, you simply queue all notifications in a queue as you're
>>>> making modifications to the DOM, then right before returning from the
>>>> function you insert a call like "flushAllPendingNotifications()". This
>>>> way you don't have to care at all about what happens when those
>>>> notifications fire.
>>>
>>> So when exactly are these notifications going to be fired?  In
>>> particular, I hope non-DOM Core specifications are going to have
>>> precise control over when they're fired.  For instance, execCommand()
>>> will ideally want to do all its mutations at once and only then fire
>>> the notifications (which I'm told is how WebKit currently works).  How
>>> will this work spec-wise?  Will we have hooks to say things like
>>> "remove a node but don't fire the notifications yet", and then have to
>>> add an extra line someplace saying to fire all the notifications?
>>> This could be awkward in some cases.  At least personally, I often say
>>> things like "call insertNode(foo) on the range" in the middle of a
>>> long algorithm, and I don't want magic happening at that point just
>>> because DOM Range fires notifications before returning from
>>> insertNode.
>>>
>>> Also, even if specs have precise control, I take it the idea is
>>> authors won't, right?  If a library wants to implement some fancy
>>> feature and be compatible with users of the library firing these
>>> notifications, they'd really want to be able to control when
>>> notifications are fired, just like specs want to.  In practice, the
>>> only reason this isn't an issue with DOM mutation events is because
>>> they can say "don't use them", and in fact people rarely do use them,
>>> but that doesn't seem ideal -- it's just saying library authors
>>> shouldn't bother to be robust.
>>
>> In working on Model Driven Views (http://code.google.com/p/mdv), we've
>> run into exactly this problem, and have developed an approach we think
>> is promising.
>>
>> The idea is to more or less take Jonas's proposal, but instead of
>> firing callbacks immediately before the outer-most mutation returns,
>> mutations are recorded for a given observer and handed to it as an
>> in-order sequence at the "end" of the event.
>
> What is the advantage comparing to Jonas' proposal?

You guys did the conceptual heavy lifting WRT this problem. Jonas's
proposal solves the main problems with current mutation events: (1)
they fire too often, (2) they are expensive because of event
propagation, (3) they are crashy WRT some DOM operations.

If Jonas's proposal is the ultimate solution, I think it's a good
outcome and a big improvement over existing spec or tearing out
mutation events. I'm asking the group to consider a few changes which
I'm hoping are improvements.

I'll be happy if I fail =-).

---

My concern with Jonas's proposal is that its semantics depend on
context (inside vs. outside of a mutation notification). I feel like
this is at least a conceptual problem. That, and I kind of shudder
imagining trying to explain to a webdev why and when mutation
notifications are sync vs async.

The place it seems likely to fall down is when someone designs an
abstraction using mutation events and depends on them firing
synchronously -- then they or someone else attempt to use it inside
another abstraction which uses mutation events. How likely is that? I
don't even have a guess, but I'm pretty surprised at the crazy things
people did with current mutation events.

Our proposal's semantics aren't dependent on context.

Additionally, our proposal makes it clear that handling a mutation
notification is an exercise in dealing with an arbitrary number of
ways the DOM could have changed since you were last called. I.e.
providing the list of changes.

In short, I feel like our proposal is just a small tweak on Jonas's.
It is more direct in its form and API about the actua

Re: Mutation events replacement

2011-07-01 Thread Rafael Weinstein
On Fri, Jul 1, 2011 at 12:43 PM, David Flanagan  wrote:
> On 7/1/11 12:13 PM, Boris Zbarsky wrote:
>>
>> On 7/1/11 3:05 PM, David Flanagan wrote:
>>>
>>> I don't think I really explained my use case on this list. See
>>> https://bugzilla.mozilla.org/show_bug.cgi?id=641821#c23 and
>>> https://bugzilla.mozilla.org/show_bug.cgi?id=641821#c25
>>
>> And https://bugzilla.mozilla.org/show_bug.cgi?id=641821#c26 please
>>
> Yes, that too.  I agree that my use case is different than the one that
> Olli's proposal is addressing.  But Ryosuke asked about my use case :-)
>
>> > Similarly, I've found it important to
>>>
>>> be able to distinguish between nodes that are being removed from a
>>> document tree and nodes that are being moved within the document tree,
>>
>> Interesting, given that Gecko's DOM implementation does NOT make such a
>> distinction at the moment.  Why did you find this to be important?
>>
> As I see it, the test of sufficiency of set of mutation event is if you can
> use them to mirror a document tree. And in my case I'm trying to do that
> across a process boundary where I can't share nodes--I have to serialize
> everything to a string or something similar.  If I call appendChild() on a
> node that is already in the tree, that removes it from its current parent
> and inserts it into a new parent.  If that generates a remove event and then
> an insert event I'll end up having to completely re-serialize the node (and
> all of its children) to re-insert it from scratch into the mirror tree.  If
> the appendChild() generates a move event then I can simply serialize the
> fact that an existing node has moved.  There are probably ways I could make
> this work without a move event, but that's why I found it useful to make the
> distinction.

To clarify: Are you worried that it isn't possible to detect the move
and avoid serializing the non-novel node without the "move"
information in the underlying mutationList? Or are you concerned that
detecting this is work and it might be good to just provide the
information and remove the need for the observer to do that work?

Your point about the sufficiency test being mirroring a tree seems
exactly right to me. I may be missing something, but I think "move"
isn't strictly necessary.

>
>>> Implementations will presumably maintain one list of all current
>>> mutations, and then will have to filter that list to deliver only those
>>> that match the desired type and desired subtree for which a listener is
>>> registered.
>>
>> That's unclear.  Maintaining this list (if it were done) sounds very
>> expensive; in Gecko's case we're more likely to drop on the floor the ones
>> which have no registered listeners.
>>
> Sure, but if there were multiple listeners on different subtrees, listening
> for different types of events, would you
> built up a custom mutation list for each one as the mutations were occuring?
>  Or build up one master list and then filter it lazily when the events are
> dispatched?
>
>    David
>>
>> -Boris
>
>
>



Re: Mutation events replacement

2011-07-01 Thread Rafael Weinstein
On Fri, Jul 1, 2011 at 3:25 PM, David Flanagan  wrote:
> On 7/1/11 3:06 PM, Olli Pettay wrote:
>>
>> On 07/02/2011 12:59 AM, David Flanagan wrote:
>>>
>>> But, and I think this is an interesting but, what happens if a node is
>>> removed from the document, has its attributes or data or children
>>> changed and is then re-inserted into the document? If the node has no
>>> parent when it is changed, no mutation events will be generated, will
>>> they?
>>
>> Sure they are. If the node has listeners, they will get called.
>>
> I'm assuming the listeners are further up the tree.
>
> To give a concrete example, to a mutation event listener (under Rafael's
> proposal, but maybe yours, too?) on the document, these two sequences of
> operations will be indistinguishable:
>
> // Generates one event for removing the title text from the  and
> another for
> // inserting it into the .  (Assume
> document.head.firstChild.firstChild is the text node inside
> // the  tag.
> document.body.appendChild(document.head.firstChild.firstChild);
>
> // Here we generate the same sequence of mutation events
> var titletext = document.head.firstChild.firstChild.
> titletext.parentNode.removeChild(titletext);  // Generates a remove event
> titletext.data = "foobar";                               // Generates a
> mutation event no one sees
> document.body.appendChild(titletext);         // Generates an insert event
>
> I claim that it is useful to be able to distinguish these two cases with
> some sort of move event.  If moves are treated as remove/insert pairs, then
> listeners have to assume that arbitrary changes could have taken place
> between the two.

If you want to discover mutations to nodes while outside the tree,
then having a single subtree observer isn't sufficient. You'll need an
attribute observer registered on all elements reachable from the root.
I believe this is the same with both proposals.

>
>    David
>
>
>



Re: Mutation events replacement

2011-07-01 Thread Rafael Weinstein
On Fri, Jul 1, 2011 at 4:15 PM, David Flanagan  wrote:
> On 7/1/11 4:09 PM, Ryosuke Niwa wrote:
>
> On Fri, Jul 1, 2011 at 3:47 PM, Rafael Weinstein  wrote:
>>
>> If you want to discover mutations to nodes while outside the tree,
>> then having a single subtree observer isn't sufficient. You'll need an
>> attribute observer registered on all elements reachable from the root.
>> I believe this is the same with both proposals.
>
> I don't think that's what he meant.  He's saying that it's useful to
> distinguish a node that's been removed from the document in order to insert
> it to somewhere else (i.e. the node was attached to the document prior to
> the insertion) from a node that was not attached to the document prior to
> the insertion.
> - Ryosuke
>
> Yes, that's mostly what I meant.
>
> It looks like I responded off-list to Rafael when I meant to do a reply all.
> Here's what I said:
>
> I'm not trying to discover mutations on nodes outside the tree.  I'm trying
> to explain why you cannot correctly model node moves with pairs of
> remove/insert mutation events.
>
> Note that when I say "move" I only care about the case where appendChild or
> insertBefore() is called on a node that is already in the document.
>
>     David
>

Apologies if I'm not understanding.

In your example, it looks like the problem you're pointing to is that
the move isn't the *only* thing that happened (the title data was also
changed). Is that right?

We've been prototyping more useful higher-level semantics on top of
what we've proposed (akin to what you're asking about). In general, if
you want to know something correct about what happened to a tree from
state (a) to state (b) you need to register an observer at all nodes
in the tree for the types of changes you care about. Subtree
observation isn't sufficient for this purpose exactly for the reason
you are observing: you don't hear about when something that was in
tree was removed, modified, then returned.

We've prototyped the semantics of our proposal and are working on
implementing higher-level observation semantics on top of them. It's
still work in progress, but you can get an idea of what's required.

For example, Document.observeElement(tagName) is implemented here:

http://code.google.com/p/mdv/source/browse/trunk/platform/dom_observe.js#30

With the interesting algorithm described here:

http://code.google.com/p/mdv/source/browse/trunk/platform/dom_observe.js#299

observeElement reports when an element entered the tree or left the
tree, and tolerates any combination of intermediate mutations:

http://code.google.com/p/mdv/source/browse/trunk/platform/dom_observe_test.html#205



Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
Respond

On Tue, Jul 5, 2011 at 10:44 AM, Olli Pettay  wrote:
> On 07/01/2011 02:17 AM, Rafael Weinstein wrote:
>>
>> On Thu, Jun 30, 2011 at 4:05 AM, Olli Pettay
>>  wrote:
>>>
>>> On 06/30/2011 12:54 AM, Rafael Weinstein wrote:
>>>>
>>>> On Wed, Jun 29, 2011 at 7:13 AM, Aryeh Gregor
>>>>  wrote:
>>>>>
>>>>> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking
>>>>>  wrote:
>>>>>>
>>>>>> This new proposal solves both these by making all the modifications
>>>>>> first, then firing all the events. Hence the implementation can
>>>>>> separate implementing the mutating function from the code that sends
>>>>>> out notifications.
>>>>>>
>>>>>> Conceptually, you simply queue all notifications in a queue as you're
>>>>>> making modifications to the DOM, then right before returning from the
>>>>>> function you insert a call like "flushAllPendingNotifications()". This
>>>>>> way you don't have to care at all about what happens when those
>>>>>> notifications fire.
>>>>>
>>>>> So when exactly are these notifications going to be fired?  In
>>>>> particular, I hope non-DOM Core specifications are going to have
>>>>> precise control over when they're fired.  For instance, execCommand()
>>>>> will ideally want to do all its mutations at once and only then fire
>>>>> the notifications (which I'm told is how WebKit currently works).  How
>>>>> will this work spec-wise?  Will we have hooks to say things like
>>>>> "remove a node but don't fire the notifications yet", and then have to
>>>>> add an extra line someplace saying to fire all the notifications?
>>>>> This could be awkward in some cases.  At least personally, I often say
>>>>> things like "call insertNode(foo) on the range" in the middle of a
>>>>> long algorithm, and I don't want magic happening at that point just
>>>>> because DOM Range fires notifications before returning from
>>>>> insertNode.
>>>>>
>>>>> Also, even if specs have precise control, I take it the idea is
>>>>> authors won't, right?  If a library wants to implement some fancy
>>>>> feature and be compatible with users of the library firing these
>>>>> notifications, they'd really want to be able to control when
>>>>> notifications are fired, just like specs want to.  In practice, the
>>>>> only reason this isn't an issue with DOM mutation events is because
>>>>> they can say "don't use them", and in fact people rarely do use them,
>>>>> but that doesn't seem ideal -- it's just saying library authors
>>>>> shouldn't bother to be robust.
>>>>
>>>> In working on Model Driven Views (http://code.google.com/p/mdv), we've
>>>> run into exactly this problem, and have developed an approach we think
>>>> is promising.
>>>>
>>>> The idea is to more or less take Jonas's proposal, but instead of
>>>> firing callbacks immediately before the outer-most mutation returns,
>>>> mutations are recorded for a given observer and handed to it as an
>>>> in-order sequence at the "end" of the event.
>>>
>>> What is the advantage comparing to Jonas' proposal?
>>
>> You guys did the conceptual heavy lifting WRT this problem. Jonas's
>> proposal solves the main problems with current mutation events: (1)
>> they fire too often, (2) they are expensive because of event
>> propagation, (3) they are crashy WRT some DOM operations.
>>
>> If Jonas's proposal is the ultimate solution, I think it's a good
>> outcome and a big improvement over existing spec or tearing out
>> mutation events. I'm asking the group to consider a few changes which
>> I'm hoping are improvements.
>>
>> I'll be happy if I fail =-).
>>
>> ---
>>
>> My concern with Jonas's proposal is that its semantics depend on
>> context (inside vs. outside of a mutation notification). I feel like
>> this is at least a conceptual problem. That, and I kind of shudder
>> imagining trying to explain to a webdev why and when mutation
>> notifications are sync vs async.
>>
>> The place it seems like

Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Mon, Jul 4, 2011 at 6:43 PM, Boris Zbarsky  wrote:
> On 7/4/11 12:28 PM, Ojan Vafai wrote:
>>
>> I'm not sure there really is a performance tradeoff. I believe that the
>> proposal Rafael put forward should almost always be faster. Storing the
>> list of changes and doing a JS callback once, for nearly all use-cases,
>> should be faster than frequent, semi-synchronous callbacks.
>
> That depends on whether your list of changes gets big enough that your start
> swapping, for example.

Just to be clear here (since there's been some discussion of a flag
which would cause "fine-grain" notifications) -- I don't believe
there's any need to describe *more* mutations. I.e. My proposal is
*not* to record more than one mutation record for a call to innerHTML
which affects more than one element. That case would still be a single
'ChildlistChanged' mutation. This is a key part of Jonas's proposal
that we want to keep: It is (I think) optimally succinct, while still
containing (nearly -- see below) all the bits of information that you
need.

I.e. The set of mutation records during any given event should be more
or less on the order of the number of DOM *operations* -- not affected
nodes. Also note that it seems like the multiple observers can share
the same mutation record (just like multiple event listeners share the
same event object).

The question is how much data is contained in the mutation record.
Here's my sense:

For ChildlistChanged, the potential data to be included:
-Target node*
-Removed nodes*
-Added nodes
-one of nextSibling or previousSibling*

My belief is that including the starred (*) data above would be
sufficient to meet David's test of mirroring a tree *without* a ton of
processing or O(N) memory.

If people think it's worth while seeing example js code that
implements this properly and it'll convince the group that this data
is sufficient, I'll sign up for implementing it.

>
>> The only bit that might be slower is what data you include in the
>> mutation list.
>
> That's one of the things that could get slower, yes.  It's not the only one.
>
>> -The index of the child that changed for ChildListChanged (is this
>> actually expensive?)
>
> It could be.  In Gecko's case it's cheap right now because kids are stored
> in arrays, but WebKit uses doubly linked lists with a cache of some sort for
> indices last I checked.  So for some access patterns this could be pretty
> expensive in WebKit.
>
>> -The old value of an attribute/text node. I know this is expensive in
>> Gecko's engine at least.
>
> This can be expensive in all engines, if they do it right.  Consider having
> to serialize out values of the 'style' attribute (possibly twice) on every
> .style.foo set.  Or having to serialize out path attributes as the SVG DOM
> is mutating paths.  This is not a Gecko-specific issue.
>
>> I'd be fine with excluding that information by default, but having a
>> flag you pass at some point saying to include those. That way, only
>> sites that need it take the performance hit.
>
> This would be a lot more palatable to me, yes.  This brings us back to
> having ways to ask for different levels of detail in mutation notifications.
>
> -Boris
>
>



Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Mon, Jul 4, 2011 at 9:57 AM, Olli Pettay  wrote:
> On 07/04/2011 07:28 PM, Ojan Vafai wrote:
>>
>> Apologies in advance if my comment makes no sense. This is a long
>> thread, I tried to digest it all. :)
>>
>> On Sat, Jul 2, 2011 at 7:07 AM, Boris Zbarsky > > wrote:
>>
>>    That may be ok, if the use cases that incur this cost are rare and
>>    the common case can be better served by a different approach.
>>
>>    Or put another way, if 1% of consumers want the full list because it
>>    makes them 4x faster and the other 99% don't want the full list, and
>>    the full list is 3x slower for the browser to build than just
>>    providing the information the 99% want, what's the right tradeoff?
>>
>>
>> I'm not sure there really is a performance tradeoff. I believe that the
>> proposal Rafael put forward should almost always be faster. Storing the
>> list of changes and doing a JS callback once, for nearly all use-cases,
>> should be faster than frequent, semi-synchronous callbacks.
>>
>> The only bit that might be slower is what data you include in the
>> mutation list. I believe that all the data you'd need is cheap except
>> for possibly the following two:
>> -The index of the child that changed for ChildListChanged (is this
>> actually expensive?)
>
> You may need more than just an index. element.innerHTML = null removes
> all the child nodes.
> And element.inserBefore(some_document_fragment, element.lastChild)
> may insert several child nodes.
> Depending on whether we want to get notified for each mutation
> or batch the mutations, simple index may or may not be enough.

I think both of these can be a single "ChildlistChanged" mutation (not
a batch of mutations).

>
>
>> -The old value of an attribute/text node. I know this is expensive in
>> Gecko's engine at least.
>
> Shouldn't be that slow.
>
> Mutation listener could easily
> implement old/new value handling itself, especially if it knows which
> attributes it is interested in.
>
>
>
>>
>> I'd be fine with excluding that information by default, but having a
>> flag you pass at some point saying to include those. That way, only
>> sites that need it take the performance hit.
>>
>>    The numbers above are made up, of course; it would be useful to have
>>    some hard data on the actual use cases.
>>
>>    Maybe we need both sorts of APIs: one which generates a fine-grained
>>    change list and incurs a noticeable DOM mutation performance hit and
>>    one which batches changes more but doesn't slow the browser down as
>>    much...
>>
>>    -Boris
>>
>>
>
>
>



Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Tue, Jul 5, 2011 at 2:27 PM, Boris Zbarsky  wrote:
> On 7/5/11 5:21 PM, Rafael Weinstein wrote:
>>
>> For ChildlistChanged, the potential data to be included:
>> -Target node*
>> -Removed nodes*
>> -Added nodes
>> -one of nextSibling or previousSibling*
>>
>> My belief is that including the starred (*) data above would be
>> sufficient to meet David's test of mirroring a tree *without* a ton of
>> processing or O(N) memory.
>
> How is that not O(N) memory?

Sorry - that was imprecise. What I meant was: the application script
wouldn't need to maintain more or less it's own copy of the DOM to
know for certain whether a node has effectively been added, removed,
or moved elsewhere in the document.

>
> -Boris
>



Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Tue, Jul 5, 2011 at 2:29 PM, Rafael Weinstein  wrote:
> On Tue, Jul 5, 2011 at 2:27 PM, Boris Zbarsky  wrote:
>> On 7/5/11 5:21 PM, Rafael Weinstein wrote:
>>>
>>> For ChildlistChanged, the potential data to be included:
>>> -Target node*
>>> -Removed nodes*
>>> -Added nodes
>>> -one of nextSibling or previousSibling*
>>>
>>> My belief is that including the starred (*) data above would be
>>> sufficient to meet David's test of mirroring a tree *without* a ton of
>>> processing or O(N) memory.

Doh! Sorry. Just to note: if you omit Added Nodes, I think you'd need
AddedNodeCount.

Generally speaking, to avoid application script needing to make copies
of wholesale DOM structures, it needs to be given "the old data" and
"the shape of the new data"

E.g.:
For ChildlistChanged: removedNodes + addedNodeCount & next/previousSibling.
For AttributeChanged: added/removed/updated + oldValue
For TextChanged: oldValue.

This may be an unreasonable burden for UA's to deliver all the time.
If so, a "give me old data" flag seems worth considering.

Many use-cases will be aiming to minimize expensive work (typically
creating more DOM or writing to the wire) -- and it'll be tempting to
guarantee that work *has* to be done. Lacking "old data", the
application will need to preemptively make a copy of all data that
*could* change.

>>
>> How is that not O(N) memory?
>
> Sorry - that was imprecise. What I meant was: the application script
> wouldn't need to maintain more or less it's own copy of the DOM to
> know for certain whether a node has effectively been added, removed,
> or moved elsewhere in the document.
>
>>
>> -Boris
>>
>



Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Tue, Jul 5, 2011 at 2:38 PM, Olli Pettay  wrote:
> On 07/06/2011 12:18 AM, Olli Pettay wrote:
>>
>> On 07/06/2011 12:06 AM, Rafael Weinstein wrote:
>>>
>>> Respond
>>>
>>> On Tue, Jul 5, 2011 at 10:44 AM, Olli Pettay
>>> wrote:
>>>>
>>>> On 07/01/2011 02:17 AM, Rafael Weinstein wrote:
>>>>>
>>>>> On Thu, Jun 30, 2011 at 4:05 AM, Olli Pettay
>>>>> wrote:
>>>>>>
>>>>>> On 06/30/2011 12:54 AM, Rafael Weinstein wrote:
>>>>>>>
>>>>>>> On Wed, Jun 29, 2011 at 7:13 AM, Aryeh
>>>>>>> Gregor
>>>>>>> wrote:
>>>>>>>>
>>>>>>>> On Tue, Jun 28, 2011 at 5:24 PM, Jonas Sicking
>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>> This new proposal solves both these by making all the modifications
>>>>>>>>> first, then firing all the events. Hence the implementation can
>>>>>>>>> separate implementing the mutating function from the code that
>>>>>>>>> sends
>>>>>>>>> out notifications.
>>>>>>>>>
>>>>>>>>> Conceptually, you simply queue all notifications in a queue as
>>>>>>>>> you're
>>>>>>>>> making modifications to the DOM, then right before returning
>>>>>>>>> from the
>>>>>>>>> function you insert a call like
>>>>>>>>> "flushAllPendingNotifications()". This
>>>>>>>>> way you don't have to care at all about what happens when those
>>>>>>>>> notifications fire.
>>>>>>>>
>>>>>>>> So when exactly are these notifications going to be fired? In
>>>>>>>> particular, I hope non-DOM Core specifications are going to have
>>>>>>>> precise control over when they're fired. For instance, execCommand()
>>>>>>>> will ideally want to do all its mutations at once and only then fire
>>>>>>>> the notifications (which I'm told is how WebKit currently works).
>>>>>>>> How
>>>>>>>> will this work spec-wise? Will we have hooks to say things like
>>>>>>>> "remove a node but don't fire the notifications yet", and then
>>>>>>>> have to
>>>>>>>> add an extra line someplace saying to fire all the notifications?
>>>>>>>> This could be awkward in some cases. At least personally, I often
>>>>>>>> say
>>>>>>>> things like "call insertNode(foo) on the range" in the middle of a
>>>>>>>> long algorithm, and I don't want magic happening at that point just
>>>>>>>> because DOM Range fires notifications before returning from
>>>>>>>> insertNode.
>>>>>>>>
>>>>>>>> Also, even if specs have precise control, I take it the idea is
>>>>>>>> authors won't, right? If a library wants to implement some fancy
>>>>>>>> feature and be compatible with users of the library firing these
>>>>>>>> notifications, they'd really want to be able to control when
>>>>>>>> notifications are fired, just like specs want to. In practice, the
>>>>>>>> only reason this isn't an issue with DOM mutation events is because
>>>>>>>> they can say "don't use them", and in fact people rarely do use
>>>>>>>> them,
>>>>>>>> but that doesn't seem ideal -- it's just saying library authors
>>>>>>>> shouldn't bother to be robust.
>>>>>>>
>>>>>>> In working on Model Driven Views (http://code.google.com/p/mdv),
>>>>>>> we've
>>>>>>> run into exactly this problem, and have developed an approach we
>>>>>>> think
>>>>>>> is promising.
>>>>>>>
>>>>>>> The idea is to more or less take Jonas's proposal, but instead of
>>>>>>> firing callbacks immediately before the outer-most mutation returns,
>>>>>>> mutations are recorded for a gi

Re: Mutation events replacement

2011-07-05 Thread Rafael Weinstein
On Tue, Jul 5, 2011 at 3:55 PM, Ryosuke Niwa  wrote:
> On Tue, Jul 5, 2011 at 3:24 PM, Olli Pettay  wrote:
>>
>> On 07/06/2011 12:48 AM, Rafael Weinstein wrote:
>>>
>>> On Tue, Jul 5, 2011 at 2:38 PM, Olli Pettay
>>>  wrote:
>>>>
>>>> What is the reason to require a new mechanism for async handling? Could
>>>> listeners be handled in a task?Basically, if DOM is mutated during task A, 
>>>> a
>>>> new task, B, is scheduled and all the mutation listeners will be
>>>> called there.
>>>
>>> It's too late by then. Most importantly, visual artifacts of incomplete
>>> DOM work may have been seen by the user.
>>
>> If that is the reason, I don't think it really holds. The script may force
>> a layout flush right after DOM mutation and then cause some popup to shows
>> up which may cause repainting in the main page.
>
> Layout is different from paint at least in WebKit.
> But I agree with you that if any observer calls alert, showModalDialog, and
> other APIs that forces repaint, you can't really do anything about it.
>  Also, UA vendors can decide to delay the repaint until all mutation
> observers are called in common cases even if they were completely
> asynchronous.

It seems like these are rarified enough cases that visual artifacts
are acceptable collateral damage if you do this. [Put another way, if
you care enough about the visual polish of your app that you will put
energy into avoiding flickr, you probably aren't using alert and
showModalDialog anyway].

Also, it's up to the app when to do it, so it's entirely in its
control (and thus avoid visual artifacts).

Note that this is a problem with both proposals. Work done in (at
least some) mutation observers is delayed. If a sync paint occurs
before it, it's work won't be reflected on the screen.

> - Ryosuke
>



Re: Mutation events replacement

2011-07-07 Thread Rafael Weinstein
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.

>
> Agreed.  We probably need more real-world use cases.
> - Ryosuke
>



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



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

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

My goals here are to:

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


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

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


Solution:

*Agreed upon design points:

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

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

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

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

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

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

Thus:

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

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


*Design points lacking consensus:

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

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

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

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



DOM Mutation Events Replacement: When to deliver mutations

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

  When, exactly, should mutations be delivered?

The four options I'm aware of are:

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

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

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

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

---

Discussion:

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

Option 1, Immediately:

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

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


Option 4, Scheduled as a future Task:

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

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

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

---

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


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

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

Option 3: At the end of the current Task.

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



Re: DOM Mutation Events Replacement: When to deliver mutations

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

On Wed, Aug 10, 2011 at 5:44 PM, Rafael Weinstein  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.



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 outsi

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 = '';",
>>> "",
>>> "");
>>>
>>
>>
>
>



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

Re: Mutation Observers: a replacement for DOM Mutation Events

2011-10-12 Thread Rafael Weinstein
Hi Sean,

I find it hard to reason about cases in the abstract. None of the
examples you list seem concerning to me (i.e. I believe they can be
properly handled), but perhaps it's a failure of my imagination.

Maybe you can provide concrete examples (i.e. with code snippets,
actual instances of use cases, etc...)

On Wed, Oct 12, 2011 at 4:00 AM, Sean Hogan  wrote:
> On 12/10/11 3:26 AM, Tab Atkins Jr. wrote:
>>
>> On Mon, Oct 10, 2011 at 7:51 PM, Sean Hogan
>>  wrote:
>>>
>>> On 24/09/11 7:16 AM, Adam Klein wrote:

 - Is free of the faults of the existing Mutation Events mechanism
 (enumerated in detail here:
 http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html)
>>>
>>> A simpler solution that is free from the faults listed in that email
>>> would
>>> be to have (at max) one mutation observer for the whole page context. I
>>> guess this would be called at the end of the task or immediately before
>>> page
>>> reflows.
>>>
>>> If a js lib (or multiple libs) want to provide finer grained mutation
>>> handling then let them work out the details.
>>
>> That seems unworkably restrictive.  It's very easy to imagine multiple
>> libraries listening for different kinds of things at the same time.
>> Libraries would just end up re-implementing event distribution, which
>> is something we can avoid by doing it correctly now.
>
> This proposal doesn't entirely avoid the issue of event distribution. There
> is no equivalent of event.stopPropagation() and hence no way to prevent
> mutation records being delivered to observers. The observers may have to be
> written with this is in mind.
>
> For example, what if two observers can potentially handle the same mutation
> - which one should handle it?
>
> Alternatively, some code might respond to an attribute by adding content to
> the DOM. What if there are mutation listeners that could respond to that
> added content? Is it desired that they ignore or handle it?
>
> Another pattern that doesn't seem to be reliably handled is mutations within
> DOM fragments that are temporarily removed from the document. That is:
> - if the fragment always remains in the document then all mutations can be
> monitored by observers on the document (or document.body), but
> - if the fragment is removed from the document followed by mutation
> observers being called, then  any further mutations won't be delivered to
> the observers, even when the fragment is reinserted into the document.
>
> The exact behavior in this scenario depends on whether mutations complete
> within one microtask or more than one
>
> Sean.
>
>
>



Re: [webcomponents] HTML Parsing and the element

2012-02-08 Thread Rafael Weinstein
[This time from the right email]

On Wed, Feb 8, 2012 at 2:10 PM, Adam Barth  wrote:
> Re-using the generic raw text element parsing algorithm would be the
> simplest change to the parser.  Do you have a concrete example of
> where nested  declarations are required?  For example,
> rather than including nested templates, you might instead consider
> referencing other template elements by id.

Referencing templates rather than including sub-templates inline is
certainly a solution. In fact, it's a common feature of templating
systems. It's useful when a single component is used in multiple
disparate or random places throughout the page.

However, it's worth backing up here and thinking about what templating is.

Templating is about convenience and maintainability of pages  -- Not
about any core capability. Templating is useful and near ubiquitous
because it makes it easy to think about authoring your page.

Web pages are highly complex and often deeply nested repeating tree
structures. You can certainly de-construct the page into some sort of
templating-4th-normal-form and dump each "component" at the top level
of the document.

However, doing this abandons the largely coherent structure of
template, e.g. where table rows are defined in the context of the
table in which they are used, etc... -- which is sort of the idea of
templating -- that you get to describe your page in more or less the
way that it will be rendered.

>
> Adam
>
>
> On Wed, Feb 8, 2012 at 2:00 PM, Dimitri Glazkov  wrote:
>> Hello folks!
>>
>> You may be familiar with the work around the  element, or a
>> way to declare document fragments in HTML (see
>> http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-November/033868.html
>> for some background).
>>
>> In trying to understand how this newfangled beast would work, I
>> started researching HTML parsing, and--oh boy was I ever sorry! Err..
>> I mean.. --and investigating how the contents of the 
>> element could be parsed.
>>
>> So far, I have two ideas. Both introduce changes to HTML parsing
>> algorithm. Both have flaws, and I thought the best thing to do would
>> be to share the data with the experts and seek their opinions. Those
>> of you cc'd -- you're the designated experts :)
>>
>> == IDEA 1: Keep template contents parsing in the tokenizer ==
>>
>> PRO: if we could come up with a way to perceive the stuff between
>>  and  as a character stream, we enable a set of
>> use cases where the template contents does not need to be a complete
>> HTML subtree. For example, I could define a template that sets up a
>> start of a table, then a few that provide repetition patterns for
>> rows/cells, and then one to close out a table:
>>
>> Nyan-nyan ...
>> 
>>  ... 
>> 
>>
>> Then I could slam these templates together with some API and produce
>> an arbitrary set of tables.
>>
>> PRO: Since the template contents are parsed as string, we create
>> opportunities for performance optimizations at the UA level. If a
>> bunch of templates is declared, but only a handful is used, we could
>> parse template contents on demand, thus reducing the churn of DOM
>> elements.
>>
>> CON: Tokenizer needs to be really smart and will start looking a lot
>> like a specialized parser. At first glance,  behaves much
>> like a  -- any tags inside will just be treated as
>> characters. It works until you realize that templates sometimes need
>> to be nested. Any use case that involves building a
>> larger-than-one-dimensional data representation (like tables) will
>> involve nested templates. This makes things rather tricky. I made an
>> attempt of sketching this out here:
>> http://dvcs.w3.org/hg/webcomponents/raw-file/a28e16cc4167/spec/templates/index.html#parsing.
>> As you can see, this adds a largish set of new states to tokenizer.
>> And it is still incomplete, breaking in cases like
>> alert('