RE: Custom element design with ES6 classes and Element constructors

From: Boris Zbarsky [mailto:bzbarsky@mit.edu] 

> Just to be clear, this still didn't allow you to upgrade a <my-img> to be a subclass of <img>, because that required a change in allocation, right?

Agreed. That needs to be done with <img is="my-img">, IMO. (Assuming the upgrading design doesn't get switched to DOM mutation, of course.)

Although! Briefly yesterday Arv mentioned that for Blink's DOM implementation there's no real difference in "internal slots" between <img> and <span>: both just have a single internal slot pointing to the C++ backing object. So in practice maybe it could. But, in theory the internal slots would be quite different between <img> and <span>, so I wouldn't really want to go down this road.

> Well, I was skipping several steps and making a few assumptions. 
> Roughly, my thought process was that you want *some* constructor that 
> corresponds to parser/document.createElement behavior. And, since as 
> discussed it definitely can't be your own constructor

> This is the part I'm not quite following.  Why can't it be your own constructor?  Sorry for losing the thread of the argument here....

No problem, I'm still skipping steps. There is an alternative design, which Arv outlined, which does allow the constructor to be the same.

The argument for different constructors is that: **assuming we want a design such that parsing-then-upgrading an element is the same as calling its constructor**, then we need the constructor to be split into two pieces: allocation, which happens on parse and is not overridden by author-defined subclasses, and initialization, which happens at upgrade time and is what authors define.

However, when authors design constructors with `class C1 extends HTMLElement { constructor(...) { ... } }`, their constructor will do both allocation and initialization. We can't separately call the initialization part of it at upgrade time without also allocating a new object. Thus, given a parse-then-upgrade scenario, we can essentially never call C1's constructor.

We *could* call some other method of C1 at upgrade time. Say, createdCallback. (Or upgradedCallback.) We could even generate a constructor, call it C2, that does HTMLElement allocation + calls createdCallback. That's what the current spec does.

In summary, the current spec design is:

 - parse-then-upgrade: HTMLElement allocation, then createdCallback.
 - parse an already-registered element: HTMLElement allocation, then createdCallback.
 - generated constructor: HTMLElement allocation, then createdCallback.
 - author-supplied constructor: ignored

Arv's message had a different design, that does indeed give C1 === C2:

 - parse-then-upgrade: HTMLElement allocation, then upgradedCallback.
 - parse an already-registered element: call author-supplied constructor (requires some trickiness to avoid executing user code during parse, but can use the same tricks createdCallback uses today)
 - generated constructor = author-supplied constructor

This requires a bit of manual synchronization to ensure that parse-then-upgrade behaves the same as the constructor/already-registered element case, as he illustrates in his message. In other words, it *doesn't* assume we want parsing-then-upgrading to be the same as calling the constructor. That might be the right tradeoff. Yesterday I was convinced it was. Today I have written so many emails that I'm not sure what I think anymore.

Received on Tuesday, 13 January 2015 17:50:49 UTC