Custom Element Upgrade Order

178 views
Skip to first unread message

Pete Blois

unread,
Oct 21, 2013, 4:50:18 PM10/21/13
to polym...@googlegroups.com
From bug 22899 I see that the upgrade order of custom elements should occur bottom up, but this doesn't seem to be reflected for node trees.

For example, I'd expect that code such as:
body.innerHTML = '<x-foo><x-bar></x-bar></x-foo>';

And:
var range = document.createRange();
range.selectNodeContents(document.body);
var fragment = range.createContextualFragment('<x-foo><x-bar></x-bar></x-foo>');
document.body.appendChild(fragment.cloneNode(true));

Are roughly equivalent with respect to top-down and bottom-up ordering.

But what I'm seeing is:
  1. cloneNode appears to upgrade top-down, while innerHTML upgrades bottom-up.
  2. appendChild of a tree appears to call enteredView top-down while innerHTML calls enteredView bottom-up.
Should these not be consistent?

Dominic Cooney

unread,
Oct 21, 2013, 10:22:38 PM10/21/13
to Pete Blois, polymer-dev
I think there's a mare's nest of issues here.

On Tue, Oct 22, 2013 at 5:50 AM, Pete Blois <bl...@google.com> wrote:
From bug 22899 I see that the upgrade order of custom elements should occur bottom up, but this doesn't seem to be reflected for node trees.

For example, I'd expect that code such as:
body.innerHTML = '<x-foo><x-bar></x-bar></x-foo>';

And:
var range = document.createRange();
range.selectNodeContents(document.body);
var fragment = range.createContextualFragment('<x-foo><x-bar></x-bar></x-foo>');
document.body.appendChild(fragment.cloneNode(true));

Are roughly equivalent with respect to top-down and bottom-up ordering.

But what I'm seeing is:
  1. cloneNode appears to upgrade top-down, while innerHTML upgrades bottom-up.
This is because innerHTML runs the HTML parser. "Bottom-up" (actually it is postorder) upgrade order is speced in terms of the parser popping elements from the element stack.

Whereas cloneNode creates elements top-down <http://dom.spec.whatwg.org/#concept-node-clone>. Hence you see top-down upgrade there.
  1. appendChild of a tree appears to call enteredView top-down while innerHTML calls enteredView bottom-up.
The specs don't really define the order in which nodes enter or leave a document as a result of appendChild. There has been some feedback that could be defined.

enteredView is bottom-up in the innerHTML case because the timing of enteredView is tied to being right after the created callback. This is per the spec. I think it makes sense to run these back-to-back like this.
 
Should these not be consistent?

I'm not sure. One thing that is unfortunate is that if you're writing your own HTML parser in script, you can't easily achieve its magic order (because you need to create the parent element to append children to it, but somehow suppress its created, entered view callbacks.) However the callback ordering of parser-created elements has received the most attention; my impression from the Polymer team is that this porridge is Just Right. What's the problem with the way it is?

Dominic 

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

Pete Blois

unread,
Oct 22, 2013, 12:10:35 PM10/22/13
to polym...@googlegroups.com, Pete Blois


On Monday, October 21, 2013 7:22:38 PM UTC-7, Dominic Cooney wrote:
I think there's a mare's nest of issues here.

On Tue, Oct 22, 2013 at 5:50 AM, Pete Blois <bl...@google.com> wrote:
From bug 22899 I see that the upgrade order of custom elements should occur bottom up, but this doesn't seem to be reflected for node trees.

For example, I'd expect that code such as:
body.innerHTML = '<x-foo><x-bar></x-bar></x-foo>';

And:
var range = document.createRange();
range.selectNodeContents(document.body);
var fragment = range.createContextualFragment('<x-foo><x-bar></x-bar></x-foo>');
document.body.appendChild(fragment.cloneNode(true));

Are roughly equivalent with respect to top-down and bottom-up ordering.

But what I'm seeing is:
  1. cloneNode appears to upgrade top-down, while innerHTML upgrades bottom-up.
This is because innerHTML runs the HTML parser. "Bottom-up" (actually it is postorder) upgrade order is speced in terms of the parser popping elements from the element stack.

Whereas cloneNode creates elements top-down <http://dom.spec.whatwg.org/#concept-node-clone>. Hence you see top-down upgrade there.
  1. appendChild of a tree appears to call enteredView top-down while innerHTML calls enteredView bottom-up.
The specs don't really define the order in which nodes enter or leave a document as a result of appendChild. There has been some feedback that could be defined.

enteredView is bottom-up in the innerHTML case because the timing of enteredView is tied to being right after the created callback. This is per the spec. I think it makes sense to run these back-to-back like this.
 
Should these not be consistent?

I'm not sure. One thing that is unfortunate is that if you're writing your own HTML parser in script, you can't easily achieve its magic order (because you need to create the parent element to append children to it, but somehow suppress its created, entered view callbacks.) However the callback ordering of parser-created elements has received the most attention; my impression from the Polymer team is that this porridge is Just Right. What's the problem with the way it is?
 
"the callback ordering of parser-created elements has received the most attention; my impression from the Polymer team is that this porridge is Just Right."

I agree with this, my concern is that the model that we're seeing for Polymer apps is that the vast majority of the elements from from template instantiation which do not follow this order.

I find it quite odd that:
<body>
  <x-a>
    <x-b></x-b>
  </x-a>
</body>

and:
<body>
  <template bind>
    <x-a>
      <x-b></x-b>
    </x-a>
  </template>
</body> 

Run the createdCallback and enteredViewCallback in different orders.

Dominic Cooney

unread,
Oct 22, 2013, 7:15:41 PM10/22/13
to Pete Blois, polymer-dev
I guess this is one way to encourage authors not to mess with the tree outside the element _at all_ :) It does seem inconsistent.

Anyone else have a dog in this fight? Otherwise I am in danger of being persuaded.

In terms of implementation, this doesn't line up so cleanly with the way Blink does insertedInto notifications, but so be it, we can make it go.

Pete: I think your next stop is public-webapps. Dimitri is trying to get Custom Elements to Last Call; changes reset the clock on that. So you might want to give him a heads up/I guess this is the sort of thing to act on quickly.

Also... what about leftView notifications?
 

Dominic 

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.



--

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

Dimitri Glazkov

unread,
Oct 22, 2013, 7:20:13 PM10/22/13
to Dominic Cooney, Pete Blois, polymer-dev
On Tue, Oct 22, 2013 at 4:15 PM, Dominic Cooney <domi...@google.com> wrote:
 
Anyone else have a dog in this fight? Otherwise I am in danger of being persuaded.

In terms of implementation, this doesn't line up so cleanly with the way Blink does insertedInto notifications, but so be it, we can make it go.

Pete: I think your next stop is public-webapps. Dimitri is trying to get Custom Elements to Last Call; changes reset the clock on that. So you might want to give him a heads up/I guess this is the sort of thing to act on quickly.

If you do, I am putting on you on my special Board of People Who Delayed Shipping Web Components :P

:DG<

Pete Blois

unread,
Oct 23, 2013, 12:11:00 PM10/23/13
to polym...@googlegroups.com, Dominic Cooney, Pete Blois
Thinking about this more, what I *think* I'm looking for is another lifecycle callback, which I'll call 'initialized'.

The contract is:
  • At parse time it is called after all child elements have been added and all attributes have been set
  • Via cloneNode it is called after all child elements have been added and all attributes have been set
  • It is bottom-up (postorder)
  • Is only ever called once for the lifecycle of the element
  • If it has not been called when an element is attached to the tree (not live tree! just having it's parent be set), it is called at that point
    • This is to ensure it gets called for procedurally created elements- once the element is inserted *anywhere* it is considered initialized.
Why?
  • Unified 'initialized' model for parser, cloneNode and procedurally generated elements
  • I find it conceptually odd to have enteredView be postorder when appending a tree to a live view- it really feels like it should be top-down
  • Created also feels like it should be top-down for the parser scenario
In short, I guess what I'm saying is that the createdCallback and enteredViewCallback contracts feel like they have been coerced to cover this initialized scenario and i'm not sure it works out all that well. So I actually prefer the cloneNode timing of these callbacks, but the 'all my children are ready' callback is absolutely critical.

Apologies for the late feedback, just getting a chance to really play with this.

Dominic Cooney

unread,
Oct 24, 2013, 4:01:51 AM10/24/13
to Pete Blois, polymer-dev
The idea of another event around "initialize" is interesting. Some Polymer people have alluded to possible new events; we've been focussing on the core set we have and are deferring proposing new ones to Custom Elements: Level 2.

Dominic
--

Scott Miles

unread,
Oct 25, 2013, 1:13:41 PM10/25/13
to Dominic Cooney, Pete Blois, polymer-dev
We went many rounds on this topic in the Polymer bullpen. Everybody agrees there needs to be some determinism in the system somewhere, but there are various methods and theories to how it can be accomplished.

There was general agreement that it would be better if `cloneNode` had the same create-ordering as nodes created via the parser. However, concerns were three fold: 

(1) this may be easy (relatively) to do in Blink, but to be able to rely on the ordering we would have to enforce it across all browsers. That requires a low level spec change, and potentially changes to low level code in other browsers (which can be hard to get done).
(2) Blink engineers like 'document order' very much more than 'depth-first tree order'. The latter makes them uncomfortable on an architectural level.
(3) the basic notions of custom-element lifecycle and when it's 'ok to access' a particular nodes or sub-tree is a larger problem.

With regard to (3): the instinct of the Polymer team has been that the platform needs to enforce at least one ordering so the developer can have a synchronous-like initialization model in most cases. However, DOM itself generally does not work this way and so we are trying to rejigger some of our thinking.

Specifically, the notion is that DOM elements do not generally try to act on other nodes (or internal data for that matter) at some initialization time, and instead wait for triggers. E.g., my understanding is that the `options` property does not populate until you ask for it.

The upshot is that we are going to do some experiments at the front end to see if it's feasible to create a lazier system that is more tolerant to non-deterministic create order. 

Sorry for the hand-waving here, but I wanted to get something on the record right away as we are so close to LC for Web Components. The reality is that it will take us a few days to concretize what we are imagining.


Follow Polymer on Google+: plus.google.com/107187849809354688692

Dominic Cooney

unread,
Oct 28, 2013, 1:07:10 AM10/28/13
to Scott Miles, Pete Blois, polymer-dev
On Sat, Oct 26, 2013 at 2:13 AM, Scott Miles <sjm...@google.com> wrote:
We went many rounds on this topic in the Polymer bullpen. Everybody agrees there needs to be some determinism in the system somewhere, but there are various methods and theories to how it can be accomplished.

There was general agreement that it would be better if `cloneNode` had the same create-ordering as nodes created via the parser. However, concerns were three fold: 

(1) this may be easy (relatively) to do in Blink, but to be able to rely on the ordering we would have to enforce it across all browsers. That requires a low level spec change, and potentially changes to low level code in other browsers (which can be hard to get done).
(2) Blink engineers like 'document order' very much more than 'depth-first tree order'. The latter makes them uncomfortable on an architectural level.
(3) the basic notions of custom-element lifecycle and when it's 'ok to access' a particular nodes or sub-tree is a larger problem.

With regard to (3): the instinct of the Polymer team has been that the platform needs to enforce at least one ordering so the developer can have a synchronous-like initialization model in most cases. However, DOM itself generally does not work this way and so we are trying to rejigger some of our thinking.

Specifically, the notion is that DOM elements do not generally try to act on other nodes (or internal data for that matter) at some initialization time, and instead wait for triggers. E.g., my understanding is that the `options` property does not populate until you ask for it.

The upshot is that we are going to do some experiments at the front end to see if it's feasible to create a lazier system that is more tolerant to non-deterministic create order. 

+1 to experiments.
 
Sorry for the hand-waving here, but I wanted to get something on the record right away as we are so close to LC for Web Components. The reality is that it will take us a few days to concretize what we are imagining.

OK. LC ends Nov 21. I'm hoping to ship Custom Elements in M33 which branches 1/6, however things get *very* quiet in MTV around holidays and in Japan around New Year. So sooner >> later.

Scott Miles

unread,
Oct 29, 2013, 2:40:13 PM10/29/13
to Dominic Cooney, Pete Blois, polymer-dev
At this point I'm reasonably confident that the best course of action is to use techniques that inure elements against construction order. 

In Polymer, we will begin this process by adding an (opt-in) async callback (installed at createdCallback) that can be used to do tasks that require contemporary nodes to have been created. This allows for a space between 'created' and 'can communicate with the DOM at large' that protects against attempting to access API on un-constructed nodes.

Sidebar: local DOM (aka DOM managed by the custom element, for example, shadow dom stamped from templates) is constructed as before, so the above won't change the basic lifecycle most elements are using today.

There are a lot of details, but the light is green for CE from my perspective. 

Dominic Cooney

unread,
Oct 29, 2013, 9:19:06 PM10/29/13
to Scott Miles, Pete Blois, polymer-dev
Thanks for digging into this and looping back.
Reply all
Reply to author
Forward
0 new messages