W3C home > Mailing lists > Public > public-webapps@w3.org > April to June 2015

RE: [components] Isolated Imports and Foreign Custom Elements

From: Travis Leithead <travis.leithead@microsoft.com>
Date: Mon, 4 May 2015 22:32:57 +0000
To: Maciej Stachowiak <mjs@apple.com>
CC: Anne van Kesteren <annevk@annevk.nl>, WebApps WG <public-webapps@w3.org>
Message-ID: <BLUPR03MB389FDF98FE3C55D8682B72FF8D20@BLUPR03MB389.namprd03.prod.outlook.com>
>> (1) All of the parameters to registerElement() should really be provided by the cross-origin element itself. It makes no sense for prototype and extends to come from the hosting environment.

It does makes sense for the cross-origin component to own the element name and extends that it defines, as this name may be important for its documentation and potentially sub-classing behavior. So +1 to that, though when handing that naming control to the host, the host can independently manage naming collisions. I'm just mentioning that--not suggesting that we should design for that particular use case.

On the API control:
One other approach I considered was to use ES6 proxies in some way, because the drawback with the property descriptor approach (if you consider it a drawback) is that you can't create cross-origin components whose elements act like DOM Collections (e.g. the behavior of 'window' or 'form' which have named property/indexed property getters). Perhaps these 'feature' of the current DOM should be discouraged in component creation, but if not, you'd need something with the flexibility of ES6 Proxies. I stuck with property descriptors in the proposal because Proxy use seems like overkill for the simple API definition scenarios (in other words, for a simple property, I don't want to have to define the proxy handler methods: has, get, deleteProperty, enumerate, ownKeys, etc. I'm not sure to what extent your two-way membrane proxy would handle this case...

-----Original Message-----
From: Maciej Stachowiak [mailto:mjs@apple.com] 
Sent: Friday, May 1, 2015 2:57 PM
To: Travis Leithead
Cc: Anne van Kesteren; WebApps WG
Subject: Re: [components] Isolated Imports and Foreign Custom Elements

Your proposal seems conceptually very similar to mine. I guess that’s a good sign!

It seems the biggest difference is the foreign registration hook - whether it’s done at class registration time (registerElement) or done at import time.

The reasons I did not go with class registration time are:

(1) All of the parameters to registerElement() should really be provided by the cross-origin element itself. It makes no sense for prototype and extends to come from the hosting environment.
(2) You need to define an elaborate protocol for the outside origin to do the “inside” of the registration operation.
(3) At least in the obvious way to do it, you need one external document per custom element type.
(4) It’s less aligned with regular HTML imports, which are the non-cross-origin non-isolated tool for importing a bunch of element definitions.
(5) Documents referenced by <link> can be preloaded aggressively, but loads initiated from a script parameter cannot.

On these grounds, I think doing the loading at import time, and explicit import/export lists, are a simpler and cleaner solution. It sounds like you agree with some of the reasons below.

I guess the other big difference is that your approach allows customizing what API is exposed, rather than proxying everything defined by the custom element instance in its own world. That is intriguing to me, but I’m not sure it’s necessary. The custom element can have truly private methods and slots by using ES6 symbols, and my proposed rule to do extended structured cloning on parameters and returns limits the risk of exposing everything. But I agree with you that if we need fine-grained API control, it’s better to do it with property descriptor-like structures than in a fully programmatic way.


> On May 1, 2015, at 10:46 AM, Travis Leithead <Travis.Leithead@microsoft.com> wrote:
> If you take a look at [1], we extend the custom elements registration mechanism so that the constructor is still available in the hosting global, yet the implementation is defined in the isolated environment.
> An approach to solving this might address another concern I have...
> I've been thinking about the way that the APIs are created with my proposal and the design wherein you have an explicit API to create the API signature on the prototype (and instances) leaves a lot of room for potential issues. For example:
> * Nothing requires the isolated component to create any APIs initially (leaving the custom element without any API until some random later time of the isolated component's choosing).
> * There is no way to know when the isolated component's APIs creation is "done"
> * The isolated component can remove APIs at any time; this is not a 
> pattern that user agents ever make use of and there's no use case for 
> it--doesn't seem appropriate to give this power to the isolated 
> component
> To address these problems, if you change the model to work more like what Maciej proposed where you can have N number of custom elements defined by one global, then in the creation of a particular custom element (specifically it's prototype) you can specify what APIs should be defined on it in one shot (creation time) and don't provide any other way to do it. This naturally satisfies my above concerns. So, a rough sketch might be something like:
>   void exportElement(DOMString customElementName, PropDescDictionary 
> definitions);
> usage example:
> ```js
> document.exportElement("element-name", {
>    api1: { enumerable: true, value: function() { return "hello, from the isolated component"; }},
>    api2: { /* etc... */ }
> });
> // returns void (or throws is "element-name" is already 
> defined/exported?) ```
> Once you divorce the isolated component in this way, you rightly point out the problem of how to get the custom element's constructor function exported outside of the isolated environment. One possible approach to solve this allows the host to ask for the custom element constructor function explicitly. Rough idea:
>    Function importConstructor("element-name");
> usage example:
> ```js
> window.MyElementName = document.importConstructor("element-name");
> // now new MyElementName(); returns an instance of "element-name" 
> element ```
> You can imagine this might be useful for any custom element (either those exported as shown above, or those defined using registerElement -- the non-isolated custom elements).
> Just some food for thought.
> [1] 
> https://github.com/w3c/webcomponents/wiki/Cross-Origin-Custom-Elements

> :-Concept-and-Proposal
> -----Original Message-----
> From: Anne van Kesteren [mailto:annevk@annevk.nl]
> Sent: Friday, May 1, 2015 9:48 AM
> To: Maciej Stachowiak
> Cc: WebApps WG
> Subject: Re: [components] Isolated Imports and Foreign Custom Elements
> On Thu, Apr 23, 2015 at 8:58 PM, Maciej Stachowiak <mjs@apple.com> wrote:
>> I wrote up a proposal (with input and advice from Ryosuke Niwa) on a 
>> possible way to extend Web Components to support fully isolated components:
>> https://github.com/w3c/webcomponents/wiki/Isolated-Imports-Proposal

>> I welcome comments on whether this approach makes sense.
> I don't get the bit where you create a node in one global, but run its constructor in another. That seems rather Frankenstein-esque. Would love to see more details overall, as the direction this is going in certainly seems like the kind of thing we want. Allowing a dozen Facebook Like buttons to appear on a page using only one additional global.
> --
> https://annevankesteren.nl/


Received on Monday, 4 May 2015 22:33:26 UTC

This archive was generated by hypermail 2.3.1 : Friday, 27 October 2017 07:27:31 UTC