Re: Custom element design with ES6 classes and Element constructors
On Thu, Jan 15, 2015 at 5:11 AM, Yehuda Katz wyc...@gmail.com wrote: On Wed, Jan 14, 2015 at 3:47 PM, Domenic Denicola d...@domenic.me wrote: I had a chat with Dmitry Lomov (V8 team/TC39, helped shape the new ES6 classes design, CC'ed). His perspective was helpful. He suggested a way of evolving the current createdCallback design that I think makes it more palatable, and allows us to avoid all of the teeth-gnashing we've been doing in this thread. - Define a default constructor for HTMLElement that includes something like: ```js const createdCallback = this.createdCallback; if (typeof createdCallback === function) { createdCallback(); } ``` - Detect whether the constructor passed to document.registerElement is the default ES class constructor or not. (By default, classes generate a constructor like `constructor(...args) { super(...args); }`.) If it is not the default constructor, i.e. if an author tried to supply one, throw an error. This functionality doesn't currently exist in ES, but it exists in V8 and seems like a useful addition to ES (e.g. as `Reflect.isDefaultConstructor`). This seems useful to me too. Something is nagging me about it though, as if there's a better way to help people with this use-case, but I'm not sure what. - Define the HTMLElement constructor to be smart enough to work without any arguments. It could do this by e.g. looking up `new.target` in the registry. I can prototype this in https://github.com/domenic/element-constructors. Isn't this at least a little future-hostile to things like `new MyElement(attrs)`? Is there a way we could get back to that in the future, in your mind? If this is what is desired, HTMLElement can pass all its arguments to createdCallback, i.e. call createdCallback(...args) in its constructor. Since default constructors pass all their arguments upstream, new MyElement(attrs) will pass attrs as arguments to HTMLElement constructor. HTMLElement constructor needs no arguments itself, so it will pass all of those to MyElement's createdCallback. With these tweaks, the syntax for registering an element becomes: ```js class MyEl extends HTMLElement { createdCallback() { // initialization code goes here } } document.registerElement(my-el, MyEl); ``` Note how we don't need to save the return value of `document.registerElement`. `new MyEl()` still works, since it just calls the default `HTMLElement` constructor. (It can get its tag name by looking up `new.target === MyEl` in the custom element registry.) And, `new MyEl()` is equivalent to the parse-then-upgrade dance, since parsing corresponds to the main body of the HTMLElement constructor, and upgrading corresponds to proto-munging plus calling `this.createdCallback()`. Compare this to the ideal syntax that we've been searching for a way to make work throughout this thread: ```js class MyEl extends HTMLElement { constructor() { super(); // initialization code goes here } } document.registerElement(my-el, MyEl); ``` It's arguable just as good. You can't use the normal constructor mechanism, which feels sad. But let's talk about that. I think this works *perfectly* (kudos!) Thanks! as long as we don't want to support constructor args being sent to readyCallback. It seems to me this can still be supported, if I understand correctly what do you want to to do. Dmitry As a framework hook, that seems totally fine with me. Whenever you're working within a framework, be it Rails or ASP.NET or Polymer or the DOM, sometimes the extension mechanism for the framework is to allow you to derive from a given base class. When you do so, there's a contract of what methods you implement, what methods you *don't* override, and so on. Indeed. I've working on both Rails and Ember, and in both cases, it's *very rare* to subclass from the internal constructor. There's usually a hook (or event) API that subclasses can use so that the superclass implementation can provide a more ergonomic API to implementations. When doing this kind of base-class extension, you're implementing a specific protocol that the framework tells you to, and you don't have complete freedom. So, it seems pretty reasonable if you're working within a framework that says, you must not override the constructor; that's my domain. Instead, I've provided a protocol for how you can do initialization. Especially for a framework as complicated and full of initialization issues as the DOM. Agreed. This design seems pretty nice to me. It means we can get upgrading (which, a few people have emphasized, is key in an ES6 modules world), Can you say more about why same-identity upgrading is critical to the design (as opposed to dom-mutation upgrading)? I asked up-thread but didn't get any takers. we don't need to generate new constructors, and we can still use class syntax without it becoming just
Re: postMessage is the new wtf
Hi Rick, here are some clarifications. There were many (long!) discussions on public-webapps about the new signature for postMessage: http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/thread.html http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0304.html http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0805.html http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0985.html window.webkitPostMessage(msg, transferables) does not really exist (it is an error in the blog post, and I am told the post will be amended). What exists is window.webkitPostMessage(msg, targetOrigin, transferables). Regarding second argument to worker.(webkit)postMessage, this is the new spec for it: http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#dedicated-workers-and-the-dedicatedworkerglobalscope-interface . The spec mandates 'void postMessage(any message, optional sequenceTransferable transfer)'. Transferable includes both MessagePorts and ArrayBuffers, so in the new spec the transfer argument to postMessage may be a mix of both types, and a backwards-compat extension to what we had before (sequenceMessagePort aka MessagePortArray). Note that message ports got an additional change in semantics, and can now be mentioned in the message as well - both worker.postMessage({p:port}, [port]) and worker.postMessage({p:port, ab:arrayBuffer}, [post, arrayBuffer]) work. Therefore this extension to postMessage semantics is both backwards-compatible and consistent. On the receiving side, the 'ports' property of event object will still contain only the message ports from the transfer list, so that behavior is preserved as well. I hope this helps, Thanks, Dmitry On Tue, Dec 13, 2011 at 8:24 AM, Rick Waldron waldron.r...@gmail.comwrote: Following the recent blog post http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fastand subsequent Twitter discussion regarding changes to the parameter list of: Worker.prototype.postMessage( message [, transfer ] ) [1] DedicatedWorkerGlobalScope void postMessagehttp://dev.w3.org/html5/workers/#dom-dedicatedworkerglobalscope-postmessage(any message, optional sequenceTransferable transfer) [2][3] I'm unable to find documentation or discussion that would clarify the rationale of over-using and over-loading the postMessage Identifier; considering the the blog cited above shows this example: [window|worker].webkitPostMessage(uInt8Array.buffer, [uInt8Array.buffer]); which conflicts with: window.postMessage(message, targetOrigin [, transfer ]) [4][5] and they both conflict with: DedicatedWorkerGlobalScope : WorkerGlobalScope ... void postMessage(in any message, in optional MessagePortArray ports); [6] Currently, passing a second arg to worker.postMessage(), that is not a MessagePortArray raises Uncaught TypeError: MessagePortArray argument must contain only MessagePorts in Chrome and Could not get domain warning in Firefox. Any reasonable clarification would be greatly appreciated. Thanks in advance Rick [1] http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#dedicated-workers-and-the-worker-interface [2] http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#dedicated-workers-and-the-dedicatedworkerglobalscope-interface [3] http://dev.w3.org/html5/workers/#dedicated-workers-and-the-dedicatedworkerglobalscope-interface [4] http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#web-messaging [5] http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#posting-messages [6] PREVIOUS SPECIFICATION STATE!!! http://www.w3.org/TR/2011/WD-workers-20110208/#dedicated-workers-and-the-dedicatedworkerglobalscope-interface
Re: postMessage is the new wtf
(resending to list - Rick, sorry if you get this twice) On Tue, Dec 13, 2011 at 11:59 AM, Rick Waldron waldron.r...@gmail.com wrote: On Tue, Dec 13, 2011 at 2:11 PM, Dmitry Lomov dslo...@chromium.org wrote: window.webkitPostMessage(msg, transferables) does not really exist (it is an error in the blog post, and I am told the post will be amended). What exists is window.webkitPostMessage(msg, targetOrigin, transferables). This supports the subject line postMessage is the new wtf You are obviously entitled to your own opinions, but folks were working hard to make sure that changes to the spec result in API that is both makes sense in its final form and does not break the Web. The spec mandates 'void postMessage(any message, optional sequenceTransferable transfer)'. Transferable includes both MessagePorts and ArrayBuffers, so in the new spec the transfer argument to postMessage may be a mix of both types, and a backwards-compat extension to what we had before (sequenceMessagePort aka MessagePortArray). Note that message ports got an additional change in semantics, and can now be mentioned in the message as well - both worker.postMessage({p:port}, [port]) and worker.postMessage({p:port, ab:arrayBuffer}, [post, arrayBuffer]) work. Therefore this extension to postMessage semantics is both backwards-compatible and consistent. Can you provide a reference for this? I'm unable to locate anything that covers these semantics. A spec for structured cloning algorithm (ready-for-implementations state): http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data A spec for typed array tarnsfer (strawman state): http://www.khronos.org/registry/typedarray/specs/latest/#9 (I agree it is not super easy to find these, but all this is bleeding-edge still - when dust settles, I am sure there will be more blog posts, articles and all that jazz) Thanks! Dmitry On Tue, Dec 13, 2011 at 8:24 AM, Rick Waldron waldron.r...@gmail.comwrote: Following the recent blog post http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fastand subsequent Twitter discussion regarding changes to the parameter list of: Worker.prototype.postMessage( message [, transfer ] ) [1] DedicatedWorkerGlobalScope void postMessagehttp://dev.w3.org/html5/workers/#dom-dedicatedworkerglobalscope-postmessage(any message, optional sequenceTransferable transfer) [2][3] I'm unable to find documentation or discussion that would clarify the rationale of over-using and over-loading the postMessage Identifier; considering the the blog cited above shows this example: [window|worker].webkitPostMessage(uInt8Array.buffer, [uInt8Array.buffer ]); which conflicts with: window.postMessage(message, targetOrigin [, transfer ]) [4][5] and they both conflict with: DedicatedWorkerGlobalScope : WorkerGlobalScope ... void postMessage(in any message, in optional MessagePortArray ports); [6] Currently, passing a second arg to worker.postMessage(), that is not a MessagePortArray raises Uncaught TypeError: MessagePortArray argument must contain only MessagePorts in Chrome and Could not get domain warning in Firefox. Any reasonable clarification would be greatly appreciated. Thanks in advance Rick [1] http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#dedicated-workers-and-the-worker-interface [2] http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#dedicated-workers-and-the-dedicatedworkerglobalscope-interface [3] http://dev.w3.org/html5/workers/#dedicated-workers-and-the-dedicatedworkerglobalscope-interface [4] http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#web-messaging [5] http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#posting-messages [6] PREVIOUS SPECIFICATION STATE!!! http://www.w3.org/TR/2011/WD-workers-20110208/#dedicated-workers-and-the-dedicatedworkerglobalscope-interface
Re: postMessage is the new wtf
On Tue, Dec 13, 2011 at 12:30 PM, Charles Pritchard ch...@jumis.com wrote: On 12/13/11 11:11 AM, Dmitry Lomov wrote: worker.postMessage({p:port, ab:arrayBuffer}, [post, arrayBuffer]) work. Therefore this extension to postMessage semantics is both backwards-compatible and consistent. On the receiving side, the 'ports' property of event object will still contain only the message ports from the transfer list, so that behavior is preserved as well. What's the behavior if an array buffer or port is not on the transferrables list? For example: worker.postMessage({p:port, ab:arrayBuffer}) In what we ship today in WebKit, this will be an exception, because cloning of message ports is not supported. The clone example you posted makes sense: worker.postMessage({p:port, ab:arrayBuffer}, [post, arrayBuffer]) If transferrables is supported, it'll ensure the vars are neutered and referenced appropriately, and if they aren't supported, it'll still pass a copy of array buffer through the message data. -Charles
Re: What changes to Web Messaging spec are proposed? [Was: Re: Using ArrayBuffer as payload for binary data to/from Web Workers]
On Thu, Jun 2, 2011 at 10:17 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jun 2, 2011 at 4:41 PM, David Levin le...@chromium.org wrote: On Thu, Jun 2, 2011 at 4:24 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jun 2, 2011 at 2:01 PM, David Levin le...@chromium.org wrote: On Thu, Jun 2, 2011 at 1:27 PM, Glenn Maynard gl...@zewt.org wrote: port.postMessage({frameBuffer: frame}, {transfer: [frame], ports: [port]}); There are two properties of this approach that I like: 1. It means that objects which you'd like to transfer ownership are not second class citizens and can live as part of the normal object graph that is posted, together with metadata that goes with it (or even as metadata for other things). 2. The receiving side doesn't need to worry about the difference, all it gets is the graph of objects that was sent to it. Yep, I totally agree with this. All of the current solutions being discussed satisfy both of these in fact. It also raises questions when I see it. When I list an object there does it imply that all children are also transfered or do I have to list each of them explicitly as well?) None of the objects which allow transferring of ownership has children so this doesn't appear to be a problem at this time. If it indeed does turn into a problem, it would seem like a problem no matter what solution is used, no? Not if all objects are transferred. Define all objects. Consider something like: a = { x: myArrayBuffer1, y: myArrayBuffer2 }; worker.postMessage(a, { transfer: true }); In this case the 'a' object is obviously not transferred. Or are you proposing that it'd be transferred too somehow? Then I wonder what is the use case for this complexity. Why not something simpler like this? port.postMessage({frameBuffer: frame}, {transfer: true, ports: [port]}); where you can just say indicate that you want the message transfered. This means that you have to choose between transferring all arrays and transferring none of them. Yep, why add the complication of picking individual items to transfer over? (I'm wary of second system syndrome, which I've seen happen many times in various designs.) It also makes it much less explicit which objects ends up being mutated. It is all of them. Sure, but what all includes might not be obvious to the web developer in all cases. For example if you're receiving an object from some subsystem that you intend to do processing on. You later decide to do the processing in a thread and throw that object into an array alongside with some other data. Some of the other data you are throwing in includes some big arrays that you want to avoid copying and so you choose to use the transfer rather than copy mode. Now you all of a sudden start modifying the data that you got from the other subsystem. This is data that you might normally not be even looking at. The fact that it happened to contain some ArrayBuffers was just a side effect of that that was the data structures that the other subsystem uses and was easy for it to return to you. Here's a simple use case, suppose I create an array of arrays (a 2d array) which contains ArrayBuffers.Now I want to transfer this as fast as possible using postMessage. What does my code look like for each of these apis? Your proposal: w.postMessage(my2darray, {transfer: true}); vs. w.postMessage(my2darray, Array.concat.apply(Array, my2darray)); Hmm, if my2darray is large, the latter is pretty wasteful. Now show me the code needed to send a message which contains one big buffer from you that you want to transfer, along with some data that you got from some other piece of code and which you do not want to modify and which may or may not contain ArrayBuffers. I think this can be reversed: what if you got some data from some other piece of code that you do not really control and you want to transfer (i.e. transfer all the ArrayBuffers that are inside that data)? As in, the library gives you two methods, compute() that returns data and present(data) that presents it, and you want to pass data from worker to main thread without really caring what is inside that data. / Jonas Thanks! Dmitry
Re: What changes to Web Messaging spec are proposed? [Was: Re: Using ArrayBuffer as payload for binary data to/from Web Workers]
On Thu, Jun 2, 2011 at 11:44 PM, Dmitry Lomov dslo...@chromium.org wrote: On Thu, Jun 2, 2011 at 10:17 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jun 2, 2011 at 4:41 PM, David Levin le...@chromium.org wrote: On Thu, Jun 2, 2011 at 4:24 PM, Jonas Sicking jo...@sicking.cc wrote: On Thu, Jun 2, 2011 at 2:01 PM, David Levin le...@chromium.org wrote: On Thu, Jun 2, 2011 at 1:27 PM, Glenn Maynard gl...@zewt.org wrote: port.postMessage({frameBuffer: frame}, {transfer: [frame], ports: [port]}); There are two properties of this approach that I like: 1. It means that objects which you'd like to transfer ownership are not second class citizens and can live as part of the normal object graph that is posted, together with metadata that goes with it (or even as metadata for other things). 2. The receiving side doesn't need to worry about the difference, all it gets is the graph of objects that was sent to it. Yep, I totally agree with this. All of the current solutions being discussed satisfy both of these in fact. It also raises questions when I see it. When I list an object there does it imply that all children are also transfered or do I have to list each of them explicitly as well?) None of the objects which allow transferring of ownership has children so this doesn't appear to be a problem at this time. If it indeed does turn into a problem, it would seem like a problem no matter what solution is used, no? Not if all objects are transferred. Define all objects. Consider something like: a = { x: myArrayBuffer1, y: myArrayBuffer2 }; worker.postMessage(a, { transfer: true }); In this case the 'a' object is obviously not transferred. Or are you proposing that it'd be transferred too somehow? Then I wonder what is the use case for this complexity. Why not something simpler like this? port.postMessage({frameBuffer: frame}, {transfer: true, ports: [port]}); where you can just say indicate that you want the message transfered. This means that you have to choose between transferring all arrays and transferring none of them. Yep, why add the complication of picking individual items to transfer over? (I'm wary of second system syndrome, which I've seen happen many times in various designs.) It also makes it much less explicit which objects ends up being mutated. It is all of them. Sure, but what all includes might not be obvious to the web developer in all cases. For example if you're receiving an object from some subsystem that you intend to do processing on. You later decide to do the processing in a thread and throw that object into an array alongside with some other data. Some of the other data you are throwing in includes some big arrays that you want to avoid copying and so you choose to use the transfer rather than copy mode. Now you all of a sudden start modifying the data that you got from the other subsystem. This is data that you might normally not be even looking at. The fact that it happened to contain some ArrayBuffers was just a side effect of that that was the data structures that the other subsystem uses and was easy for it to return to you. Here's a simple use case, suppose I create an array of arrays (a 2d array) which contains ArrayBuffers.Now I want to transfer this as fast as possible using postMessage. What does my code look like for each of these apis? Your proposal: w.postMessage(my2darray, {transfer: true}); vs. w.postMessage(my2darray, Array.concat.apply(Array, my2darray)); Hmm, if my2darray is large, the latter is pretty wasteful. Now show me the code needed to send a message which contains one big buffer from you that you want to transfer, along with some data that you got from some other piece of code and which you do not want to modify and which may or may not contain ArrayBuffers. I think this can be reversed: what if you got some data from some other piece of code that you do not really control and you want to transfer (i.e. transfer all the ArrayBuffers that are inside that data)? As in, the library gives you two methods, compute() that returns data and present(data) that presents it, and you want to pass data from worker to main thread without really caring what is inside that data. / Jonas So, to clarify, we have two competing requirements here: 1) exercise fine-grained control over which parts of the message are transfrerred 2) be able to transfer objects opaquely, for modularity or performance reasons. Here are two proposals that satisfy both: a) *Recursive transfer lists*. Allow arbitrary objects, not only ArrayBuffers, to appear in transfer lists. ArrayBuffers that are under objects in transfer lists are transferred, others are cloned. (This leaves the question, what happens to objects that are shared between objects in transfer lists and others. It looks like the sane answer
Re: What changes to Web Messaging spec are proposed? [Was: Re: Using ArrayBuffer as payload for binary data to/from Web Workers]
On Fri, Jun 3, 2011 at 2:15 PM, Andrew Wilson atwil...@google.com wrote: On Fri, Jun 3, 2011 at 1:02 PM, Jonas Sicking jo...@sicking.cc wrote: On Fri, Jun 3, 2011 at 11:37 AM, Kenneth Russell k...@google.com wrote: On Fri, Jun 3, 2011 at 9:46 AM, Glenn Maynard gl...@zewt.org wrote: On Fri, Jun 3, 2011 at 11:12 AM, Dmitry Lomov dslo...@google.com wrote: a) Recursive transfer lists. Allow arbitrary objects, not only ArrayBuffers, to appear in transfer lists. ArrayBuffers that are under objects in transfer lists are transferred, others are cloned. This again causes the same forwards-compatibility problem. If you do this: frame = { video: canvasPixelArrayInstance, histogram: arrayBuffer } } postMessage({ frame: frame }, { transfer: frame }); and you expect only histogram to be transferred (since that's all that supports it when you write this code), your code breaks when CanvasPixelArray later supports it. Right, but you should not write { transfer : frame }, you should write: postMessage( { frame: frame }, { transfer: frame.histogram }) in this case. The guidance for transferring objects should be: do not transfer objects you intend to use on this thread (on main thread, worker) - only transfer parts that you do not intend to use. This version of postMessage gives a high-fidelity tool for you to do so if desired, but handles the opaque case as well. b) Transfer lists + separate transferMessage method. We still equip postMessage with transfer lists, these transfer lists list ArrayBuffers, and we provide a separate method transferMessage with recursive transfer semantics. What do people think? Same problem. If you want a quicker way to transfer all messages of given types, see my previous mail: { transfer: ArrayBuffer }. (sorry I somehow missed your previous mail when replying) I do not think filtering by types solve much. Consider again a case of two library functions compute() returning some data and present(data) rendering some data, where library user treats the data as a black box. In first version, library author used ArrayBuffers as part of data internally, because it is the only data type that is transferrable. Now, CanvasPixelArray became transferrable as well, and library author changes the library to compute those as part of the data, to offload more of the computation to compute phase. With filtering by types, all library user will need to modify their code as well, to add CanvasPixelArray to a list of transferrable types in their postMessages. Again, I think having a transfer list (as some argument to postMessage) is great solution for high-fidelity control. Low-fidelity, opaque, don't show me the implementation use cases is what is missing. Agreed on these points. Using an object graph for the transfer list (which is what the recursive transfer list idea boils down to) also sounds overly complicated. It feels that but not solving this as part of messaging spec we just offload the complexity to the users. May I suggest to reconsider adding another optional array argument to postMessage for the transfer list, rather than using an object with special properties? Points in favor of adding another optional array argument: 1. Less typing, and less possibility that a typo will cause incorrect behavior: worker.postMessage(objectGraph, null, [ arrayBuffer1ToTransfer, arrayBuffer2ToTransfer ]); vs. worker.postMessage(objectGraph, { transfer: [ arrayBuffer1ToTransfer, arrayBuffer2ToTransfer] }); 2. Possibility of using Web IDL to specify the type of the optional array argument (i.e., Transferable[]?). Would be harder to do using an object -- requiring either specifying another interface type with the ports and transfer attributes, or using any plus ECMAScript-specific text. Points in favor of using an object: 1. Could potentially overload the meaning of the optional ports array argument, avoiding adding another argument to postMessage. 2. More extensible in the future. Thoughts? My first thought is that so far no implementer has stepped up and said that changing the meaning of the 'ports' argument would not be acceptable. Would be great if someone who is reading this thread and who works at Google/Apple/Opera could check with the relevant people to see if such a change would be possible. It's certainly possible - there's nothing intrinsically difficult with making this change from an implementor's point of view (I've had my fingers in both the WebKit and Chromium MessagePort implementations so I'm relatively confident that this would not be prohibitively hard). Is it desirable? My knee-jerk reaction is that we should stick with changes that are compatible with the existing API (like Ian's original suggestion, or adding a separate optional array of transferable objects) - I have no data on the number of sites that are using the current API but I