Re: Why can't we just use constructor instead of createdCallback?
On Fri, Feb 14, 2014 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote: What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. Okay, let me see if I got this right. The list is effectively a serialized representation of a document subtree. The parent + order in the list provides all the necessary information. We use this list to separate tree construction into two stages: one before custom element constructors are called, and one after. In cases when the custom element is just a like button widget (a leaf in the tree), the list is short and just contains the widgets. In cases like bodymy-appdiv ... the entire doc tree ... /my-app/body, the list contains the entire subtree of my-app, which is effectively the document. In cases like divmy-bar../my-barspan.../span ... more siblings ... /div, the list will contain at least all siblings of my-bar, because they can't be inserted into tree until after my-bar's constructor runs. When run, the constructors are free to explore the partially-completed tree, which enables interesting hacks like this: in document: div id=amy-bar/my-bar lots more markup... in my-bar constructor: var myFutureParent = document.querySelector(#a); // redirect tree construction to resume at a new point. document.body.appendChild(myFutureParent); Or, in my-bar constructor: var myFutureParent = document.querySelector(#a); var iframe = document.body.appendChild(document.createElement(iframe)); // teleport the tree into another frame iframe.contentDocument.body.appendChild(myFutureParent); I can't immediately tell whether these hacks are cool or scary. The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. Here's an alternative proposal: 1) The Web developers are already aware of the fact that you can create new instances of JS objects without running their constructors with Object.create 2) Let's make sure that when they call constructors directly (as in var b = new MyB(arg1,arg2);), the constructor is actually called. 3) When the parser instantiates the element, it does the equivalent of Object.create, so no constructor is called. It seems simple and easy to understand. :DG
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 1:35 PM, Dimitri Glazkov dglaz...@google.comwrote: Here's an alternative proposal: 1) The Web developers are already aware of the fact that you can create new instances of JS objects without running their constructors with Object.create These are not the instances you are looking for. We need to have real instances here. @@create gives us the semantics for doing this. 2) Let's make sure that when they call constructors directly (as in var b = new MyB(arg1,arg2);), the constructor is actually called. 3) When the parser instantiates the element, it does the equivalent of Object.create, so no constructor is called. That would not set the internal state as needed. It would not know how to associate the instance object with the C++ backing object for example. Also, all the DOM methods are using brand checks (and not instanceof checks) so the brand needs to be setup as well. The solution is to call `MyElement[Symbol.create]()` which would setup the internal state as needed. We can make this non writable, non configurable which allows the implementation to skip calling any js code at that point because the semantics is unobservable. It seems simple and easy to understand. With ES6 it is possible to create instance objects without ever calling the constructor (this is NOT possible in ES5) so maybe we should just give up on having the parser calling the constructor? -- erik
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 11:24 AM, Erik Arvidsson a...@chromium.org wrote: On Tue, Feb 18, 2014 at 1:35 PM, Dimitri Glazkov dglaz...@google.comwrote: Here's an alternative proposal: 1) The Web developers are already aware of the fact that you can create new instances of JS objects without running their constructors with Object.create These are not the instances you are looking for. We need to have real instances here. @@create gives us the semantics for doing this. Ah, yes. Sorry :) 2) Let's make sure that when they call constructors directly (as in var b = new MyB(arg1,arg2);), the constructor is actually called. 3) When the parser instantiates the element, it does the equivalent of Object.create, so no constructor is called. That would not set the internal state as needed. It would not know how to associate the instance object with the C++ backing object for example. Also, all the DOM methods are using brand checks (and not instanceof checks) so the brand needs to be setup as well. The solution is to call `MyElement[Symbol.create]()` which would setup the internal state as needed. We can make this non writable, non configurable which allows the implementation to skip calling any js code at that point because the semantics is unobservable. It seems simple and easy to understand. With ES6 it is possible to create instance objects without ever calling the constructor (this is NOT possible in ES5) so maybe we should just give up on having the parser calling the constructor? Sounds good to me. :DG
Re: Why can't we just use constructor instead of createdCallback?
On Feb 18, 2014, at 10:35 AM, Dimitri Glazkov dglaz...@google.com wrote: On Fri, Feb 14, 2014 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote: What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. Okay, let me see if I got this right. The list is effectively a serialized representation of a document subtree. The parent + order in the list provides all the necessary information. We use this list to separate tree construction into two stages: one before custom element constructors are called, and one after. In cases when the custom element is just a like button widget (a leaf in the tree), the list is short and just contains the widgets. In cases like bodymy-appdiv ... the entire doc tree ... /my-app/body, the list contains the entire subtree of my-app, which is effectively the document. In cases like divmy-bar../my-barspan.../span ... more siblings ... /div, the list will contain at least all siblings of my-bar, because they can't be inserted into tree until after my-bar's constructor runs. When run, the constructors are free to explore the partially-completed tree, which enables interesting hacks like this: in document: div id=amy-bar/my-bar lots more markup... in my-bar constructor: var myFutureParent = document.querySelector(#a); // redirect tree construction to resume at a new point. document.body.appendChild(myFutureParent); Or, in my-bar constructor: var myFutureParent = document.querySelector(#a); var iframe = document.body.appendChild(document.createElement(iframe)); // teleport the tree into another frame iframe.contentDocument.body.appendChild(myFutureParent); You can already do this in a regular script element so this isn't a new issue custom element's constructor is introducing. The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. Have you tried implementing this and found that you can't implement it efficiently? Could you quantify the runtime or memory cost? Here's an alternative proposal: 1) The Web developers are already aware of the fact that you can create new instances of JS objects without running their constructors with Object.create 2) Let's make sure that when they call constructors directly (as in var b = new MyB(arg1,arg2);), the constructor is actually called. 3) When the parser instantiates the element, it does the equivalent of Object.create, so no constructor is called. I don't see why we want to make the edge case like the one you described above prevent us from making common cases easy to use and understand. Just call constructor whenever a custom element is created. If you're doing weird stuff in constructor, then weird things happen. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 10:35 AM, Dimitri Glazkov dglaz...@google.com wrote: On Fri, Feb 14, 2014 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote: What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. Okay, let me see if I got this right. The list is effectively a serialized representation of a document subtree. The parent + order in the list provides all the necessary information. We use this list to separate tree construction into two stages: one before custom element constructors are called, and one after. Yes In cases when the custom element is just a like button widget (a leaf in the tree), the list is short and just contains the widgets. Yes In cases like bodymy-appdiv ... the entire doc tree ... /my-app/body, the list contains the entire subtree of my-app, which is effectively the document. Well. With the optimization I mentioned you only need to make the my-app element and it's immediate children into the list. Any grand-children of my-app can immediately be inserted into their parent. So I guess you could say that the list contains basically the whole document. But most nodes would only indirectly be in the list. I.e. the list would be short, but each entry could contain large subtrees. In cases like divmy-bar../my-barspan.../span ... more siblings ... /div, the list will contain at least all siblings of my-bar, because they can't be inserted into tree until after my-bar's constructor runs. Yes When run, the constructors are free to explore the partially-completed tree, which enables interesting hacks like this: in document: div id=amy-bar/my-bar lots more markup... in my-bar constructor: var myFutureParent = document.querySelector(#a); // redirect tree construction to resume at a new point. document.body.appendChild(myFutureParent); Or, in my-bar constructor: var myFutureParent = document.querySelector(#a); var iframe = document.body.appendChild(document.createElement(iframe)); // teleport the tree into another frame iframe.contentDocument.body.appendChild(myFutureParent); Yup I can't immediately tell whether these hacks are cool or scary. You can already do exactly this with script elements. I also am not sure if that's cool or scary. But I also haven't heard of anyone running into trouble because of it. I suspect it's not a common thing to do. The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. The above paragraph appears to assume that creating this list is slow. Do you have data to back that up? The whole premise of my original email was that we should not do this if it's slow. And that we should measure if it's slow or not. I absolutely agree that we should not add features that are slow anytime they are used. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 5:59 PM, Jonas Sicking jo...@sicking.cc wrote: On Tue, Feb 18, 2014 at 10:35 AM, Dimitri Glazkov dglaz...@google.com wrote: On Fri, Feb 14, 2014 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote: What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. Okay, let me see if I got this right. The list is effectively a serialized representation of a document subtree. The parent + order in the list provides all the necessary information. We use this list to separate tree construction into two stages: one before custom element constructors are called, and one after. Yes In cases when the custom element is just a like button widget (a leaf in the tree), the list is short and just contains the widgets. Yes In cases like bodymy-appdiv ... the entire doc tree ... /my-app/body, the list contains the entire subtree of my-app, which is effectively the document. Well. With the optimization I mentioned you only need to make the my-app element and it's immediate children into the list. Any grand-children of my-app can immediately be inserted into their parent. So I guess you could say that the list contains basically the whole document. But most nodes would only indirectly be in the list. I.e. the list would be short, but each entry could contain large subtrees. Would that cause issues with the order of the mutation record reported by mutation observers? In cases like divmy-bar../my-barspan.../span ... more siblings ... /div, the list will contain at least all siblings of my-bar, because they can't be inserted into tree until after my-bar's constructor runs. Yes When run, the constructors are free to explore the partially-completed tree, which enables interesting hacks like this: in document: div id=amy-bar/my-bar lots more markup... in my-bar constructor: var myFutureParent = document.querySelector(#a); // redirect tree construction to resume at a new point. document.body.appendChild(myFutureParent); Or, in my-bar constructor: var myFutureParent = document.querySelector(#a); var iframe = document.body.appendChild(document.createElement(iframe)); // teleport the tree into another frame iframe.contentDocument.body.appendChild(myFutureParent); Yup I can't immediately tell whether these hacks are cool or scary. You can already do exactly this with script elements. I also am not sure if that's cool or scary. But I also haven't heard of anyone running into trouble because of it. I suspect it's not a common thing to do. The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. The above paragraph appears to assume that creating this list is slow. Do you have data to back that up? The whole premise of my original email was that we should not do this if it's slow. And that we should measure if it's slow or not. I absolutely agree that we should not add features that are slow anytime they are used. / Jonas -- erik
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 2:59 PM, Jonas Sicking jo...@sicking.cc wrote: On Tue, Feb 18, 2014 at 10:35 AM, Dimitri Glazkov dglaz...@google.com wrote: On Fri, Feb 14, 2014 at 3:58 PM, Jonas Sicking jo...@sicking.cc wrote: What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. Okay, let me see if I got this right. The list is effectively a serialized representation of a document subtree. The parent + order in the list provides all the necessary information. We use this list to separate tree construction into two stages: one before custom element constructors are called, and one after. Yes In cases when the custom element is just a like button widget (a leaf in the tree), the list is short and just contains the widgets. Yes In cases like bodymy-appdiv ... the entire doc tree ... /my-app/body, the list contains the entire subtree of my-app, which is effectively the document. Well. With the optimization I mentioned you only need to make the my-app element and it's immediate children into the list. Any grand-children of my-app can immediately be inserted into their parent. So I guess you could say that the list contains basically the whole document. But most nodes would only indirectly be in the list. I.e. the list would be short, but each entry could contain large subtrees. In cases like divmy-bar../my-barspan.../span ... more siblings ... /div, the list will contain at least all siblings of my-bar, because they can't be inserted into tree until after my-bar's constructor runs. Yes When run, the constructors are free to explore the partially-completed tree, which enables interesting hacks like this: in document: div id=amy-bar/my-bar lots more markup... in my-bar constructor: var myFutureParent = document.querySelector(#a); // redirect tree construction to resume at a new point. document.body.appendChild(myFutureParent); Or, in my-bar constructor: var myFutureParent = document.querySelector(#a); var iframe = document.body.appendChild(document.createElement(iframe)); // teleport the tree into another frame iframe.contentDocument.body.appendChild(myFutureParent); Yup I can't immediately tell whether these hacks are cool or scary. You can already do exactly this with script elements. I also am not sure if that's cool or scary. But I also haven't heard of anyone running into trouble because of it. I suspect it's not a common thing to do. I see. I am continually amazed at the exciting world we live in. The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. The above paragraph appears to assume that creating this list is slow. Do you have data to back that up? No, of course not :) It's a first intuitive reaction. :DG
Re: Why can't we just use constructor instead of createdCallback?
On Tue, Feb 18, 2014 at 3:26 PM, Dimitri Glazkov dglaz...@google.com wrote: On Tue, Feb 18, 2014 at 2:59 PM, Jonas Sicking jo...@sicking.cc wrote: On Tue, Feb 18, 2014 at 10:35 AM, Dimitri Glazkov dglaz...@google.com wrote: The thing that really bothers me is that this approach is contradicting itself. We go to into pretty elaborate lengths to enable running constructors during parsing, but the key performance lesson developers will immediately learn is to avoid constructors on custom elements, because they will trigger the two-phase code path during parsing. Here's a thing that you can use, but you probably don't want to ever use it. The above paragraph appears to assume that creating this list is slow. Do you have data to back that up? No, of course not :) It's a first intuitive reaction. One example that seems intuitively problematic about this approach (to me, anyway) are those parts of the parser that inspect the tree during parsing: foster parenting. I'm not sure it's a performance issue so much as a complexity issue. Consider: div my-element table div When following the spec (http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#appropriate-place-for-inserting-a-node), any text in that algorithm which says parent node need to be patched to say something about this new list of to-be-attached nodes. Patching all such references seems likely to be difficult and error-prone, but I admit that I can't quantify those worries. - Adam
Re: Why can't we just use constructor instead of createdCallback?
On Thu, Feb 13, 2014 at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. I am not sure I fully understand how this will work. Let me try to repeat it back and see if I got this right. Basically, we are modifying the tree construction algorithm to be a 3-pass system: 1) Build a meta tree (each node in the tree is a meta object that represents an element that will be constructed) 2) Instantiate all elements by calling constructors on them 3) Build the tree of elements from the meta tree. Right? :DG
Re: Why can't we just use constructor instead of createdCallback?
On Fri, Feb 14, 2014 at 9:25 AM, Dimitri Glazkov dglaz...@google.com wrote: On Thu, Feb 13, 2014 at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. I am not sure I fully understand how this will work. Let me try to repeat it back and see if I got this right. Basically, we are modifying the tree construction algorithm to be a 3-pass system: 1) Build a meta tree (each node in the tree is a meta object that represents an element that will be constructed) 2) Instantiate all elements by calling constructors on them 3) Build the tree of elements from the meta tree. Right? I'd rather put it as: 1) Construct the objects, but rather than inserting them in their parents, remember which parent they should be inserted in. 2) Call constructors on all elements 3) Insert elements in their parent So no need to construct any meta objects. You can further optimize by only doing this for custom elements with a constructor. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
On Fri, Feb 14, 2014 at 10:36 AM, Jonas Sicking jo...@sicking.cc wrote: On Fri, Feb 14, 2014 at 9:25 AM, Dimitri Glazkov dglaz...@google.com wrote: On Thu, Feb 13, 2014 at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. I am not sure I fully understand how this will work. Let me try to repeat it back and see if I got this right. Basically, we are modifying the tree construction algorithm to be a 3-pass system: 1) Build a meta tree (each node in the tree is a meta object that represents an element that will be constructed) 2) Instantiate all elements by calling constructors on them 3) Build the tree of elements from the meta tree. Right? I'd rather put it as: 1) Construct the objects, but rather than inserting them in their parents, remember which parent they should be inserted in. Sure, this is the meta tree construction. At the limit, if every element is a custom element, then you're effectively building a tree of things that remember where their respective elements need to be. 2) Call constructors on all elements Yup. 3) Insert elements in their parent Yup. So no need to construct any meta objects. Okay, we don't have to call them meta objects, but we need some storage to remember where the element should go :) You can further optimize by only doing this for custom elements with a constructor. Interesting. What if the element's constructor decides to walk the DOM tree or mutate it? What does it see? Are there holes for elements that haven't yet been inserted, or are the elements just appended regardless of their initial position in the tree? :DG
Re: Why can't we just use constructor instead of createdCallback?
Another alternative is to disallow DOM traversal and DOM mutation inside these constructors. By disallow I mean throw an error! Here is a rough outline of what the algorithm might look like. Let there be a global counter CostomElementConstructionCounter which is initially set to 0. 1. Parse and build the DOM tree as usual. Keep track of all custom elements we encounter. 2. At some later point, before any script is run: 3. For each pending custom element (in tree order): 1. Create the instance objects for the custom element. 2. Increment CostomElementConstructionCounter 3. Call the constructructor for the custom element, passing the object instance as `this`. 4. Decrement CostomElementConstructionCounter Then we need to guard all DOM traversal and DOM mutation methods and throw if the counter is non zero. The point is that the timing of the constructor invocation is mostly not observable. If an implementation wants to invoke it as it builds the DOM that also works since there is no way to traverse the tree at that time. On Fri, Feb 14, 2014 at 1:50 PM, Dimitri Glazkov dglaz...@google.comwrote: On Fri, Feb 14, 2014 at 10:36 AM, Jonas Sicking jo...@sicking.cc wrote: On Fri, Feb 14, 2014 at 9:25 AM, Dimitri Glazkov dglaz...@google.com wrote: On Thu, Feb 13, 2014 at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. I am not sure I fully understand how this will work. Let me try to repeat it back and see if I got this right. Basically, we are modifying the tree construction algorithm to be a 3-pass system: 1) Build a meta tree (each node in the tree is a meta object that represents an element that will be constructed) 2) Instantiate all elements by calling constructors on them 3) Build the tree of elements from the meta tree. Right? I'd rather put it as: 1) Construct the objects, but rather than inserting them in their parents, remember which parent they should be inserted in. Sure, this is the meta tree construction. At the limit, if every element is a custom element, then you're effectively building a tree of things that remember where their respective elements need to be. 2) Call constructors on all elements Yup. 3) Insert elements in their parent Yup. So no need to construct any meta objects. Okay, we don't have to call them meta objects, but we need some storage to remember where the element should go :) You can further optimize by only doing this for custom elements with a constructor. Interesting. What if the element's constructor decides to walk the DOM tree or mutate it? What does it see? Are there holes for elements that haven't yet been inserted, or are the elements just appended regardless of their initial position in the tree? :DG -- erik
Re: Why can't we just use constructor instead of createdCallback?
On 2/14/14 2:03 PM, Erik Arvidsson wrote: Then we need to guard all DOM traversal and DOM mutation methods and throw if the counter is non zero. This is a fairly nontrivial whack-a-mole exercise, sadly (starting with defining traversal). -Boris
Re: Why can't we just use constructor instead of createdCallback?
On Fri, Feb 14, 2014 at 10:50 AM, Dimitri Glazkov dglaz...@google.com wrote: On Fri, Feb 14, 2014 at 10:36 AM, Jonas Sicking jo...@sicking.cc wrote: On Fri, Feb 14, 2014 at 9:25 AM, Dimitri Glazkov dglaz...@google.com wrote: On Thu, Feb 13, 2014 at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. I am not sure I fully understand how this will work. Let me try to repeat it back and see if I got this right. Basically, we are modifying the tree construction algorithm to be a 3-pass system: 1) Build a meta tree (each node in the tree is a meta object that represents an element that will be constructed) 2) Instantiate all elements by calling constructors on them 3) Build the tree of elements from the meta tree. Right? I'd rather put it as: 1) Construct the objects, but rather than inserting them in their parents, remember which parent they should be inserted in. Sure, this is the meta tree construction. At the limit, if every element is a custom element, then you're effectively building a tree of things that remember where their respective elements need to be. I don't think that you need a tree of things. What you need is an array of objects that need to be inserted, and an array of parents that they need to insert them into. That's it. So no need to construct any meta objects. Okay, we don't have to call them meta objects, but we need some storage to remember where the element should go :) Sure. You'll need two arrays. Or really, you'll need one array of node+parent tuples. You can further optimize by only doing this for custom elements with a constructor. Interesting. What if the element's constructor decides to walk the DOM tree or mutate it? What does it see? Are there holes for elements that haven't yet been inserted, or are the elements just appended regardless of their initial position in the tree? What I mean is that for nodes that doesn't have a constructor, and whose parent doesn't have a constructor, no need to add them to the above arrays. Just insert them into their parent. That means that when that the constructor of an element runs, the element doesn't have any parents or children. So no need to hide parents or children anywhere. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. / Jonas On Wed, Jan 22, 2014 at 5:21 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jan 9, 2014 at 9:27 PM, Boris Zbarsky bzbar...@mit.edu wrote: One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all pending elements before they're being constructed into the DOM tree. Isn't that the bad thing we _don't_ want to do? That is, invoke arbitrary page JS from the middle of the parsing algorithm? The idea was to do something like this: 1. While parsing, when you hit a custom element (with a constructor) don't insert that element into its parent, nor insert any of its children into the element. 2. Put each such element into an array along with meta-info about what parent and children it should have. 3. Once you're done parsing as much as you want to parse (i.e. until you hit a network boundary or feel the need to return to the event loop), unwind enough of the calling stack until you feel comfortable running content JS. 4. Run the constructor for the first element in the array. 5. After a constructor has been run, insert the element into its parent, and insert its children into the element. 6. Remove the element from the array and, unless the array is empty, go back to step 4. This is somewhat simplified. You also have to make sure not to insert an elements into a parent where previous siblings are still pending to be inserted. The big question of course is if tracking the elements in the separate array and inserting them after their constructor has run will be a performance issue. In Gecko it might be a bit of a problem since we can get O(n^2) performance issues where n is the nesting depth of custom elements. This is due to our recursive BindToTree notification which cause problems when trees are constructed bottom up But possibly this can be solved. And possibly other implementations doesn't have the same problem. Or possibly they have worse problems. But it wasn't immediately obvious to me that this wouldn't work. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
We (Apple) support this proposal assuming that we can implement the proposed algorithm efficiently. We would try to prototype it in WebKit in the coming months and will report implementation issues, including the feasibility of efficient implementation, if exists. - R. Niwa On Feb 13, 2014, at 6:50 PM, Jonas Sicking jo...@sicking.cc wrote: Dimitri, I'd still love to hear feedback from you on the idea above. Seems like it could fix one of the design issues that a lot of people have reacted to. / Jonas On Wed, Jan 22, 2014 at 5:21 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jan 9, 2014 at 9:27 PM, Boris Zbarsky bzbar...@mit.edu wrote: One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all pending elements before they're being constructed into the DOM tree. Isn't that the bad thing we _don't_ want to do? That is, invoke arbitrary page JS from the middle of the parsing algorithm? The idea was to do something like this: 1. While parsing, when you hit a custom element (with a constructor) don't insert that element into its parent, nor insert any of its children into the element. 2. Put each such element into an array along with meta-info about what parent and children it should have. 3. Once you're done parsing as much as you want to parse (i.e. until you hit a network boundary or feel the need to return to the event loop), unwind enough of the calling stack until you feel comfortable running content JS. 4. Run the constructor for the first element in the array. 5. After a constructor has been run, insert the element into its parent, and insert its children into the element. 6. Remove the element from the array and, unless the array is empty, go back to step 4. This is somewhat simplified. You also have to make sure not to insert an elements into a parent where previous siblings are still pending to be inserted. The big question of course is if tracking the elements in the separate array and inserting them after their constructor has run will be a performance issue. In Gecko it might be a bit of a problem since we can get O(n^2) performance issues where n is the nesting depth of custom elements. This is due to our recursive BindToTree notification which cause problems when trees are constructed bottom up But possibly this can be solved. And possibly other implementations doesn't have the same problem. Or possibly they have worse problems. But it wasn't immediately obvious to me that this wouldn't work. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
On Thu, Jan 9, 2014 at 9:27 PM, Boris Zbarsky bzbar...@mit.edu wrote: One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all “pending” elements before they’re being constructed into the DOM tree. Isn't that the bad thing we _don't_ want to do? That is, invoke arbitrary page JS from the middle of the parsing algorithm? The idea was to do something like this: 1. While parsing, when you hit a custom element (with a constructor) don't insert that element into its parent, nor insert any of its children into the element. 2. Put each such element into an array along with meta-info about what parent and children it should have. 3. Once you're done parsing as much as you want to parse (i.e. until you hit a network boundary or feel the need to return to the event loop), unwind enough of the calling stack until you feel comfortable running content JS. 4. Run the constructor for the first element in the array. 5. After a constructor has been run, insert the element into its parent, and insert its children into the element. 6. Remove the element from the array and, unless the array is empty, go back to step 4. This is somewhat simplified. You also have to make sure not to insert an elements into a parent where previous siblings are still pending to be inserted. The big question of course is if tracking the elements in the separate array and inserting them after their constructor has run will be a performance issue. In Gecko it might be a bit of a problem since we can get O(n^2) performance issues where n is the nesting depth of custom elements. This is due to our recursive BindToTree notification which cause problems when trees are constructed bottom up But possibly this can be solved. And possibly other implementations doesn't have the same problem. Or possibly they have worse problems. But it wasn't immediately obvious to me that this wouldn't work. / Jonas
Re: Why can't we just use constructor instead of createdCallback?
On Thu, Jan 9, 2014 at 10:57 PM, Ryosuke Niwa rn...@apple.com wrote: 1. The parser does not know that it needs to use MyElement.@@create to create the JS objects when it sees a my-element. On the other hand, solving this seems to require running some author scripts at the element creation time, at some later time but before the node is inserted into the document. My hope was that it would be rare to override Symbol.create for Elements so in most cases we would not need to call user code. After further discussing this with Dominic and others I've given up the hope that an object instance cannot be reached before the constructor has been called. This can happen due to navigating the DOM tree but also be manually calling `MyCustomElement[Symbol.create]()`. At this point I believe we should just resolve to best practice and that is to not use @@create directly and do not navigate the DOM tree in your constructors. -- erik
Re: Why can't we just use constructor instead of createdCallback?
On 1/10/14 11:10 AM, Erik Arvidsson wrote: My hope was that it would be rare to override Symbol.create for Elements so in most cases we would not need to call user code. For spec purposes and parser implementation design purposes that doesn't matter. If user code can be called, the algorithms involved have to handle user code being called at that point and potentially tearing the world down (or apart, or just rearranging it in macabre ways like user code tends to do)... After further discussing this with Dominic and others I've given up the hope that an object instance cannot be reached before the constructor has been called. This can happen due to navigating the DOM tree but also be manually calling `MyCustomElement[Symbol.create]()`. At this point I believe we should just resolve to best practice and that is to not use @@create directly and do not navigate the DOM tree in your constructors. Yeah, agreed. -Boris
Re: Why can't we just use constructor instead of createdCallback?
On Jan 10, 2014, at 8:16 AM, Boris Zbarsky bzbar...@mit.edu wrote: On 1/10/14 11:10 AM, Erik Arvidsson wrote: My hope was that it would be rare to override Symbol.create for Elements so in most cases we would not need to call user code. For spec purposes and parser implementation design purposes that doesn't matter. If user code can be called, the algorithms involved have to handle user code being called at that point and potentially tearing the world down (or apart, or just rearranging it in macabre ways like user code tends to do)... After further discussing this with Dominic and others I've given up the hope that an object instance cannot be reached before the constructor has been called. This can happen due to navigating the DOM tree but also be manually calling `MyCustomElement[Symbol.create]()`. At this point I believe we should just resolve to best practice and that is to not use @@create directly and do not navigate the DOM tree in your constructors. Yeah, agreed. An alternative idea we had was to “recursively” call constructors on the “unconstructed” elements in such situations. So if the constructor of an element A tries to access to another unconstrcuted element B, then call B’s constructor at that point. Either solution is better than not being able to use ES6 classes in my opinion. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
Jonas, William, Ted, and I had some discussion about this last month. (Sorry for the delayed response). On Dec 5, 2013, at 10:58 PM, Boris Zbarsky bzbar...@mit.edu wrote: On 12/6/13 1:49 AM, Ryosuke Niwa wrote: Then how do we define a custom element using ES6 classes? Are we going to not call the constructor? An excellent question, indeed. I don't have a good answer for you. If we do make elements subclassable (which it seems like we should), we would presumably need to make the actual default constructor of the built-in element classes a no-op, since we can't actually rely on the subclass calling it. All the relevant setup would need to be done by @@create. Given that, we could maybe cheat and in fact do some sort of delayed calling of the constructor of ES6 subclasses of elements. You'd still be able to observe these objects in an unconstructed state from the subclass pov, but at least it wouldn't be a security issue in terms of operating on a DOM that's in an inconsistent state from the point of view of privileged code. Calling constructors after the tree had been constructed will be an issue because then you could access “unconstructed” nodes via nextSibling, parentNode, etc... It's not clear to me how OK such an unconstructed state is. I suppose it's no worse than someone extending the ES6 subclass in question and then never calling its constructor... But that's a programming error on the someone's part, typically, while here we would be effectively forcing this sort of thing on all Element subclasses. All that still seems more palatable than trying to completely revise HTML parsing to be robust to constructors running when the element is created…. One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all “pending” elements before they’re being constructed into the DOM tree. On Dec 6, 2013, at 7:05 AM, Erik Arvidsson a...@chromium.org wrote: The custom element draft does add a new synchronization point. After setting innerHTML (for example), before returning to the script the callbacks for the custom elements created by innerHTML are called in tree order. This does lead to the possibility to observer objects that have not yet had their created callback been called yet. I think this trade off is inevitable, no matter whether we use @@create, constructor or created. I just don't see us being able to call user code every time a node is created. I do understand your concern. On Dec 6, 2013, at 7:37 AM, Erik Arvidsson a...@chromium.org wrote: The things that fail here are: 1. The parser does not know that it needs to use MyElement.@@create to create the JS objects when it sees a my-element. On the other hand, solving this seems to require running some author scripts at the element creation time, at some later time but before the node is inserted into the document. 2. No callbacks for enteredView, leftView and attributeChanged. 3. It depend on the magic of document.createElement which is circular. A better way would be to do `super('my-element')` or something like that. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On 1/9/14 10:57 PM, Ryosuke Niwa wrote: Given that, we could maybe cheat and in fact do some sort of delayed calling of the constructor of ES6 subclasses of elements. You'd still be able to observe these objects in an unconstructed state from the subclass pov, but at least it wouldn't be a security issue in terms of operating on a DOM that's in an inconsistent state from the point of view of privileged code. Calling constructors after the tree had been constructed will be an issue because then you could access “unconstructed” nodes via nextSibling, parentNode, etc... Right, I did say that above. Is that really a problem in practice, though? One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all “pending” elements before they’re being constructed into the DOM tree. Isn't that the bad thing we _don't_ want to do? That is, invoke arbitrary page JS from the middle of the parsing algorithm? On the other hand, solving this seems to require running some author scripts at the element creation time, at some later time but before the node is inserted into the document. The parser is expected to insert the nodes into the document pretty much immediately after creating them, no? -Boris
Re: Why can't we just use constructor instead of createdCallback?
On Thu, Jan 9, 2014 at 9:27 PM, Boris Zbarsky bzbar...@mit.edu wrote: On 1/9/14 10:57 PM, Ryosuke Niwa wrote: Given that, we could maybe cheat and in fact do some sort of delayed calling of the constructor of ES6 subclasses of elements. You'd still be able to observe these objects in an unconstructed state from the subclass pov, but at least it wouldn't be a security issue in terms of operating on a DOM that's in an inconsistent state from the point of view of privileged code. Calling constructors after the tree had been constructed will be an issue because then you could access “unconstructed” nodes via nextSibling, parentNode, etc... Right, I did say that above. Is that really a problem in practice, though? One idea that came out of our discussion is was to add an additional step in the parser to call constructors on all “pending” elements before they’re being constructed into the DOM tree. Isn't that the bad thing we _don't_ want to do? That is, invoke arbitrary page JS from the middle of the parsing algorithm? On the other hand, solving this seems to require running some author scripts at the element creation time, at some later time but before the node is inserted into the document. The parser is expected to insert the nodes into the document pretty much immediately after creating them, no? -Boris Calling of constructors (in JS/ES sense) instead of callbacks is not semantically correct I would say. Consider this declaration: class MyElement inherits HTMLElement { public prop = 1; constructor( text ) { super(my-element); this.textContent = text; } } UA simply cannot call such constructor as it requires parameter. UA can call only implicit default constructor, : ({ prop:1 }).__proto__ = MyElement; in this case. And call some designated method of the class ('callback' in this discussion) when element gets attached to the DOM tree. Constructors are used for 'external' element creation: var myel = new MyElement(woo-hoo!); About callbacks: It should be two callbacks actually: attached() - called when element *gets attached to the DOM*, it is not a constructor, sic! detached() - when it gets detached from the DOM. so here: var myel = new MyElement(woo-hoo!); someParent.append(myel); call of myel.attached() will happen inside the append() above. detached() is called when parent.removeChild() for the element is called. Just in case: this is how it is implemented and works in my Sciter [1] and I didn't find any problems with element/script life cycles. And yet. I also have dynamic element class assignment by CSS with custom 'prototype' property, e.g.: input[type=masked] { prototype: MaskedInput url(code/widgets.tis); } In that case attached/detached are also called when the element gets/looses that style. But that's probably another story. [1] http://terrainformatica.com/sciter -- Andrew Fedoniouk. http://terrainformatica.com
[WebComponents] List for new bug announcements [Was: Re: Why can't we just use constructor instead of createdCallback?]
On 12/6/13 3:28 PM, ext Ryosuke Niwa wrote: On Dec 6, 2013, at 7:37 AM, Erik Arvidsson a...@chromium.org mailto:a...@chromium.org wrote: 1. The parser does not know that it needs to use MyElement.@@create to create the JS objects when it sees a my-element. 2. No callbacks for enteredView, leftView and attributeChanged. 3. It depend on the magic of document.createElement which is circular. A better way would be to do `super('my-element')` or something like that. I wish we could resolve these remaining issues. In fact, fixing these issues is a requirement for us. I just noticed that yesterday Erik filed three Custom Element bugs but I didn't receive an announcement about them: * [Custom]: No way to pass parameters to constructor https://www.w3.org/Bugs/Public/show_bug.cgi?id=24018 * [Custom]: No way to associate class/constructor function https://www.w3.org/Bugs/Public/show_bug.cgi?id=24019 * [Custom]: A tag name should be associated with the constructor and not the prototype https://www.w3.org/Bugs/Public/show_bug.cgi?id=24020 Unlike other specs/components, new Web Components bugs are not announced on public-webapps (although like all of WebApps' components, new Web Components bugs are announced on public-webapps-bugzilla [which only has 5 subscribers]). For consistency reasons, and to facilitate transparency, it seems like all new Web Component bugs should be announced on public-webapps. Does anyone object to that? -AB
Re: [WebComponents] List for new bug announcements [Was: Re: Why can't we just use constructor instead of createdCallback?]
Art wrote: For consistency reasons, and to facilitate transparency, it seems like all new Web Component bugs should be announced on public-webapps. Does anyone object to that? I think that's a fine idea. Ted
Re: Why can't we just use constructor instead of createdCallback?
The custom element draft does add a new synchronization point. After setting innerHTML (for example), before returning to the script the callbacks for the custom elements created by innerHTML are called in tree order. This does lead to the possibility to observer objects that have not yet had their created callback been called yet. I think this trade off is inevitable, no matter whether we use @@create, constructor or created. I just don't see us being able to call user code every time a node is created. On Fri, Dec 6, 2013 at 1:58 AM, Boris Zbarsky bzbar...@mit.edu wrote: On 12/6/13 1:49 AM, Ryosuke Niwa wrote: Then how do we define a custom element using ES6 classes? Are we going to not call the constructor? An excellent question, indeed. I don't have a good answer for you. If we do make elements subclassable (which it seems like we should), we would presumably need to make the actual default constructor of the built-in element classes a no-op, since we can't actually rely on the subclass calling it. All the relevant setup would need to be done by @@create. Given that, we could maybe cheat and in fact do some sort of delayed calling of the constructor of ES6 subclasses of elements. You'd still be able to observe these objects in an unconstructed state from the subclass pov, but at least it wouldn't be a security issue in terms of operating on a DOM that's in an inconsistent state from the point of view of privileged code. It's not clear to me how OK such an unconstructed state is. I suppose it's no worse than someone extending the ES6 subclass in question and then never calling its constructor... But that's a programming error on the someone's part, typically, while here we would be effectively forcing this sort of thing on all Element subclasses. All that still seems more palatable than trying to completely revise HTML parsing to be robust to constructors running when the element is created Might be worth checking on public-script-coord to see what the TC39 folks think about all this. -Boris -- erik
Re: Why can't we just use constructor instead of createdCallback?
On Fri, Dec 6, 2013 at 2:33 AM, Ryosuke Niwa rn...@apple.com wrote: It appears to me that we should definitely have a good answer for this question before the specification reaches CR given that the definition of ES6 classes is pretty stable at this point. ES6 classes do not introduce any new semantics over ES5. However I do agree that we need to think about user ergonomics here. If we cannot use ES6 class syntax to do custom elements we have failed. What we have now is OK but not perfect. Here is how you can use class syntax today ```js class MyElement extends HTMLElement { createdCallback() { console.log(I'm not a real boy); } } MyElement = document.create('my-element, MyElement); // or var MyElement = document.create('my-element, class extends HTMLElement { createdCallback() { console.log(I'm not a real boy); } }); ``` This works fine in Blink today: http://goo.gl/HZLpqG The relevant addition to ES6 is how @@create participates in object creation. I've previously voiced concern that custom elements do not use @@create and the spec draft was changed in a way where it would allow using it in the future. There was strong concern from Mozilla that they did not want custom elements to depend on this ES6 feature due to them needing document.register ASAP. The way this would work without custom elements is: ```js class MyElement extends HTMLElement { constructor(name) { console.log(`I'm a real boy and my name is ${name}`); } [Symbol.create]() { return document.createElement('my-element'); } } new MyElement('Pinocchio'); ``` The things that fail here are: 1. The parser does not know that it needs to use MyElement.@@create to create the JS objects when it sees a my-element. 2. No callbacks for enteredView, leftView and attributeChanged. 3. It depend on the magic of document.createElement which is circular. A better way would be to do `super('my-element')` or something like that. I wish we could resolve these remaining issues. -- erik
Re: Why can't we just use constructor instead of createdCallback?
On Dec 6, 2013, at 7:37 AM, Erik Arvidsson a...@chromium.org wrote: On Fri, Dec 6, 2013 at 2:33 AM, Ryosuke Niwa rn...@apple.com wrote: It appears to me that we should definitely have a good answer for this question before the specification reaches CR given that the definition of ES6 classes is pretty stable at this point. ES6 classes do not introduce any new semantics over ES5. However I do agree that we need to think about user ergonomics here. If we cannot use ES6 class syntax to do custom elements we have failed. What we have now is OK but not perfect. Here is how you can use class syntax today ```js class MyElement extends HTMLElement { createdCallback() { console.log(I'm not a real boy); } } MyElement = document.create('my-element, MyElement); // or var MyElement = document.create('my-element, class extends HTMLElement { createdCallback() { console.log(I'm not a real boy); } }); ``` What if that class definition had constructor() in it? It would be called? If so, when? 1. The parser does not know that it needs to use MyElement.@@create to create the JS objects when it sees a my-element. 2. No callbacks for enteredView, leftView and attributeChanged. 3. It depend on the magic of document.createElement which is circular. A better way would be to do `super('my-element')` or something like that. I wish we could resolve these remaining issues. In fact, fixing these issues is a requirement for us. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
There were several threads around this in March/April, but the main gist is that we can't allow running user code when the parser is building the tree, and thus we would need to decouple the timing of the constructor being called from the [[Construct]] internal method to make constructors workable. But then they aren't constructors, but callbacks, since the object would already be exist (and be in a tree). So we decided to not lie and just call them callbacks, rather than constructors. http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0728.html http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/thread.html#msg152 I am probably forgetting some... :DG On Thu, Dec 5, 2013 at 5:31 PM, Ryosuke Niwa rn...@apple.com wrote: Could someone point me to a discussion/reasoning behind why we're using createdCallback as opposed to the constructor as a way of instantiating a custom element? It's so awkward to have a separate callback in the world where we have ES6 classes. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On Dec 5, 2013, at 8:43 PM, Dimitri Glazkov dglaz...@chromium.org wrote: There were several threads around this in March/April, but the main gist is that we can't allow running user code when the parser is building the tree, and thus we would need to decouple the timing of the constructor being called from the [[Construct]] internal method to make constructors workable. That sounds like an implementation detail of Blink/WebKit. Also, JS wrappers aren't even constructed immediately for builtin elements in WebKit and Blink so delaying the construction of elements until later time (e.g. end of micro task) seems fine. But then they aren't constructors, but callbacks, since the object would already be exist (and be in a tree). So we decided to not lie and just call them callbacks, rather than constructors. It would be extremely unfortunate if authors can't use constructor() in the world we have ES6 classes. class MyButtonElement extends HTMLElement { constructor() { ... } } looks much more natural than class MyButtonElement extends HTMLElement { constructor() { // Would I ever be called? If so, when? Is that safe? } createdCallback() { ... } } http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0728.html http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/thread.html#msg152 I am probably forgetting some… Thanks for the pointers again. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On Thu, Dec 5, 2013 at 9:03 PM, Ryosuke Niwa rn...@apple.com wrote: On Dec 5, 2013, at 8:43 PM, Dimitri Glazkov dglaz...@chromium.org wrote: There were several threads around this in March/April, but the main gist is that we can't allow running user code when the parser is building the tree, and thus we would need to decouple the timing of the constructor being called from the [[Construct]] internal method to make constructors workable. That sounds like an implementation detail of Blink/WebKit. Also, JS wrappers aren't even constructed immediately for builtin elements in WebKit and Blink so delaying the construction of elements until later time (e.g. end of micro task) seems fine. FWIW, the concern was brought up first by Microsoft's Tony Ross and then separately Mozilla's Jonas Sicking. Technically, both Blink and WebKit are capable of doing this. It's just mostly a terrible idea to interrupt tree construction with user code. Delaying construction of elements until end of microtask doesn't solve the problem -- you're just shifting the timing of tree construction. :DG
Re: Why can't we just use constructor instead of createdCallback?
On Dec 5, 2013, at 9:23 PM, Dimitri Glazkov dglaz...@chromium.org wrote: On Thu, Dec 5, 2013 at 9:03 PM, Ryosuke Niwa rn...@apple.com wrote: On Dec 5, 2013, at 8:43 PM, Dimitri Glazkov dglaz...@chromium.org wrote: There were several threads around this in March/April, but the main gist is that we can't allow running user code when the parser is building the tree, and thus we would need to decouple the timing of the constructor being called from the [[Construct]] internal method to make constructors workable. That sounds like an implementation detail of Blink/WebKit. Also, JS wrappers aren't even constructed immediately for builtin elements in WebKit and Blink so delaying the construction of elements until later time (e.g. end of micro task) seems fine. FWIW, the concern was brought up first by Microsoft's Tony Ross and then separately Mozilla's Jonas Sicking. Technically, both Blink and WebKit are capable of doing this. It's just mostly a terrible idea to interrupt tree construction with user code. Delaying construction of elements until end of microtask doesn't solve the problem -- you're just shifting the timing of tree construction. I'm not suggesting to do that. Simply call the constructor at when createdCallback is currently called. - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On 12/6/13 12:03 AM, Ryosuke Niwa wrote: That sounds like an implementation detail of Blink/WebKit. It seems like a pretty fundamental restriction for all current HTML parsers. In particular, the HTML parsing algorithm has no provisions for script mutating the DOM at random points in the algorithm (in fact, pretty much anywhere other than when script elements are inserted). Also, JS wrappers aren't even constructed immediately for builtin elements in WebKit and Blink Now _that_ is a WebKit/Blink specific implementation detail (though one shared by other browser engines at the moment). Last I checked, Servo plans to have a single JS object for an element, not separate actual object and JS wrapper objects. And it seems like we don't want to preclude that sort of implementation strategy, which requires that the ES object be created when the element is created.. so delaying the construction of elements until later time (e.g. end of micro task) seems fine. End of microtask is not acceptable, unfortunately. It would mean that if you set innerHTML on a node and then try to touch its new kids you get not-yet-constructed objects. :( We could try to invent a new synchronization point for this sort of thing (e.g. similar to Gecko's scriptrunners), but any such setup with delayed construction has a significant failure mode in the innerHTML case: When one of the constructors is running, the other nodes that got inserted are still in a not-constructed state. That doesn't seem great. Which then puts us back to running script while the innerHTML parser is running. Which is also not great. For example it would completely preclude any attempts to parallelize innerHTML parsing. And yes, I know those might be doomed to failure anyway... I can't tell you how you should weight these various drawbacks, obviously. -Boris
Re: Why can't we just use constructor instead of createdCallback?
On Dec 5, 2013, at 10:44 PM, Boris Zbarsky bzbar...@mit.edu wrote: On 12/6/13 12:03 AM, Ryosuke Niwa wrote: That sounds like an implementation detail of Blink/WebKit. It seems like a pretty fundamental restriction for all current HTML parsers. In particular, the HTML parsing algorithm has no provisions for script mutating the DOM at random points in the algorithm (in fact, pretty much anywhere other than when script elements are inserted). Also, JS wrappers aren't even constructed immediately for builtin elements in WebKit and Blink Now _that_ is a WebKit/Blink specific implementation detail (though one shared by other browser engines at the moment). Last I checked, Servo plans to have a single JS object for an element, not separate actual object and JS wrapper objects. And it seems like we don't want to preclude that sort of implementation strategy, which requires that the ES object be created when the element is created.. so delaying the construction of elements until later time (e.g. end of micro task) seems fine. End of microtask is not acceptable, unfortunately. It would mean that if you set innerHTML on a node and then try to touch its new kids you get not-yet-constructed objects. :( We could try to invent a new synchronization point for this sort of thing (e.g. similar to Gecko's scriptrunners), but any such setup with delayed construction has a significant failure mode in the innerHTML case: When one of the constructors is running, the other nodes that got inserted are still in a not-constructed state. That doesn't seem great. Which then puts us back to running script while the innerHTML parser is running. Which is also not great. For example it would completely preclude any attempts to parallelize innerHTML parsing. And yes, I know those might be doomed to failure anyway... I can't tell you how you should weight these various drawbacks, obviously. Then how do we define a custom element using ES6 classes? Are we going to not call the constructor? - R. Niwa
Re: Why can't we just use constructor instead of createdCallback?
On Dec 5, 2013, at 10:58 PM, Boris Zbarsky bzbar...@mit.edu wrote: On 12/6/13 1:49 AM, Ryosuke Niwa wrote: Then how do we define a custom element using ES6 classes? Are we going to not call the constructor? An excellent question, indeed. I don't have a good answer for you. It appears to me that we should definitely have a good answer for this question before the specification reaches CR given that the definition of ES6 classes is pretty stable at this point. If we do make elements subclassable (which it seems like we should), we would presumably need to make the actual default constructor of the built-in element classes a no-op, since we can't actually rely on the subclass calling it. All the relevant setup would need to be done by @@create. Given that, we could maybe cheat and in fact do some sort of delayed calling of the constructor of ES6 subclasses of elements. You'd still be able to observe these objects in an unconstructed state from the subclass pov, but at least it wouldn't be a security issue in terms of operating on a DOM that's in an inconsistent state from the point of view of privileged code. Right. I know it sounds so cheesy but I was suggesting something along that line. It's not clear to me how OK such an unconstructed state is. I suppose it's no worse than someone extending the ES6 subclass in question and then never calling its constructor... But that's a programming error on the someone's part, typically, while here we would be effectively forcing this sort of thing on all Element subclasses. I think the question is how observable such a delay is and what implications it would have. All that still seems more palatable than trying to completely revise HTML parsing to be robust to constructors running when the element is created…. Indeed. Might be worth checking on public-script-coord to see what the TC39 folks think about all this. Sounds like a good idea. - R. Niwa