- From: Erik Arvidsson <arv@chromium.org>
- Date: Tue, 5 Feb 2013 17:12:49 -0500
- To: public-webapps <public-webapps@w3.org>
- Cc: Dimitri Glazkov <dglazkov@google.com>, Boris Zbarsky <bzbarsky@mit.edu>, daniel@mozilla.com
The way document.register is currently proposed makes it future-hostile to ES6. I've heard several people from different organizations say that this is a blocking issue. Over the last couple of days we (me, Dimitri and others) have worked on some alterations to the current spec proposal. The discussion got pretty extensive so I'll try to summarize the main points. https://www.w3.org/Bugs/Public/show_bug.cgi?id=20831 With ES6 we really want to be able to write code like this: class MyButton extends HTMLButtonElement { ... } document.register('x-button', MyButton); In ES6 speak, we have split the "new Foo(...args)" expression into "Foo.call(Foo[@@create](), ...args)" which means that creating the instance has been separated from the call to the function. This allows us to subclass Array etc. It also opens up possibilities to subclass Element. All Element need is a @@create method that creates the instance (and sets the internal pointer to the underlying C++ object like we do today). For custom elements we can therefore generate the @@create method for the function passed to document.register. This function would create an instance in the same way as previously speced at https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-custom-element-instantiation == What about ES5/3? == In ES5/3 speak we don't have the @@create refactoring but we do have an internal [[Construct]] method. In the reformed version of document.create we can override [[Construct]] of the passed in function to create the instance and then do the [[Call]]. This also means API change from what is currently specified. Instead of document.register(‘x-button’, { prototype: Object.create(HTMLButtonElement.prototype, { ... }); We will have: function MyButton() { HTMLButtonElement.call(this); // yay, I can have a real constructor! // do constructor stuff ... } MyButton.prototype = Object.create(HTMLButtonElement.prototype, { ... }); document.register(‘x-button’, MyButton); We think it’s much better, because: a) we no longer have to spit out some magic generated constructor b) we can let developers have a constructor in their custom element c) there will be no API changes when ES6 classes arrive d) there is no longer a need for crazy callbacks “created” and “shadowRootCreated”, because they can just be code in the constructor == Does this mean that the user code now runs while parsing? == We’ve heard in the past that allowing user code execution while the parser is building a tree is undesirable due to performance and specific design issues. However, now that the custom element constructor is no longer generated, it may appear as if the user-specified constructor would run when each element is instantiated. We intend to address this as follows: * When the parser builds a tree, it only creates underlying C++ objects * Just before entering script, * we first instantiate all custom elements (think Object.create, but with all the baggage of being a wrapper around a C++ object), so that they all have the right prototype chains, in tree order * then, we invoke the respective internal [[Call]] methods of all custom elements, in tree order How does template fit into this? The dependencies on template and shadow DOM are now removed from document.register API. If people want to use a template and shadow DOM they can easily do this in code: class MyButton extends HTMLButtonElement { constructor() { super(); var template = ... var shadowRoot = this.createShadowRoot(); shadowRoot.appendChild(template.content.cloneNode(true)); } } document.register('x-button', MyButton); -- erik
Received on Tuesday, 5 February 2013 22:13:38 UTC