Re: [webcomponents] Proposal for Cross Origin Use Case and Declarative Syntax

Hi Dimitri, Dominic,

Ryosuke is here in Shezhen at WebApps' f2f meeting. We would like to 
have one or both of you join us (via voice conference) on Tuesday 
morning to talk about Web Components and his comments below.

Please look at the agenda page and let us know your availability for the 
one of the open slots before lunch (all times are local to Shenzhen):

<http://www.w3.org/wiki/Webapps/November2013Meeting#Agenda_Tuesday_November_12>

-Thanks, ArtB

On 11/9/13 3:24 AM, ext Ryosuke Niwa wrote:
> Hi all,
>
> We have been discussing cross-orign use case and declarative syntax of 
> web components internally at Apple, and here are our straw man 
> proposal to amend the existing Web Components specifications to 
> support it.
>
> *1. Modify HTML Imports to run scripts in the imported document itself*
> This allows the importee and the importer to not share the same script 
> context, etc…
>
> *2. Add “importcomponents" content attribute on link element*
> It defines the list of custom element tag names to be imported from 
> the imported HTML document.
> e.g. <link rel="import" href="~" importcomponents="tag-1 tag-2"> will 
> export custom elements of tag names "tag-1" and "tag-2" from ~. Any 
> name that didn't have a definition in the import document is ignored 
> (i.e. if "tag-2" was not defined in ~, it would be skipped but "tag-1" 
> will be still imported).
>
> This mechanism prevents the imported document from defining arbitrary 
> components in the host document.
>
> *3. Support "static" (write-once) binding of a HTML template*
> e.g.
> <template id=cardTemplate>Name: {{name}}<br>Email:{{email}}</template>
> <script>
> document.body.appendChild(cardTemplate.instantiate({name: "Ryosuke 
> Niwa", email:"rniwa@webkit.org <mailto:rniwa@webkit.org>"}));
> </script>
>
> *4. Add “interface" content attribute to template element*
> This content attribute specifies the name of the JavaScript 
> constructor function to be created in the global scope. The UA creates 
> one and will be used to instantiate a given custom element. The author 
> can then setup the prototype chain as needed:
>
> <template defines="name-card" interface="NameCardElement">
> Name: {{name}}<br>Email:{{email}}
> </template>
> <script>
> NameCardElement.prototype.name = function () {...}
> NameCardElement.prototype.email = function () {...}
> </script>
>
> This is similar to doing:
> var NameCardElement = document.register(’name-card');
>
> *5. Add "defines" content attribute on HTML template element to define 
> a custom element*
> This new attribute defines a custom element of the given name for the 
> template content.
> e.g. <template defines="nestedDiv"><div><div></div></div></template> 
> will let you use <nestedDiv></nestedDiv>
>
> We didn’t think having a separate custom element was useful because we 
> couldn’t think of a use case where you wanted to define a custom 
> element declaratively and not use template by default, and having to 
> associate the first template element with the custom element seemed 
> unnecessary complexity.
>
> *5.1. When a custom element is instantiated, automatically instantiate 
> template inside a shadow root after statically binding the template 
> with dataset*
> This allows statically declaring arguments to a component.
> e.g.
> <template defines="name-card">Name: {{name}}<br>Email:{{email}}</template>
> <name-card data-name="Ryosuke Niwa" data-email="rniwa@webkit.org 
> <mailto:rniwa@webkit.org>”>
>
> *5.2. When a new custom element object is constructed, "created" 
> callback is called with a shadow root*
> Unfortunately, we can't let the author define a constructor because 
> the element hadn't been properly initialized with the right JS wrapper 
> at the time of its construction. So just like we can't do "new 
> HTMLTitleElement", we're not going to let the author do an interesting 
> things inside a custom element's constructor. Instead, we're going to 
> call "created" function on its prototype chain:
>
> <template defines="name-card" interface="NameCardElement">
> Name: {{name}}<br>Email:{{email}}
> </template>
> <script>
> NameCardElement.prototype.name = function () {...}
> NameCardElement.prototype.email = function () {...}
> NameCardElement.prototype.created = function (shadowRoot) {
> ... // Initialize the shadowRoot here.
> }
> </script>
>
> This is similar to the way document.register works in that 
> document.register creates a constructor automatically.
>
> *6. The cross-origin component does not have access to the shadow host 
> element, and the host document doesn’t have access to the element object.*
> When member functions of the element is called, “this” object will be 
> undefined. This is necessary because exposing the object to a 
> cross-origin content will result in tricky security issues, forcing us 
> to have proxy objects, etc…
>
> Inside the document that imported a component, the element doesn’t use 
> the prototype defined by the component as that exposes JS objects 
> cross-origin. e.g. even if LikeButtonElement was defined in 
> facebook.com/~/like-button.html 
> <http://facebook.com/%7E/like-button.html>, the document that uses 
> this component wouldn’t see the prototype or the constructor. It’ll be 
> HTMLUnknownElement. (We could create a new custom element type such as 
> HTMLCrossOriginCustomElement if think that’s necessary).
>
> *7. Expose shadow host’s dataset on shadow root*
> This allows the component to communicate with the host document in a 
> limited fashion without exposing the element directly.
>
> This design allows us to have an iframe-like boundary between the 
> shadow host (custom element itself) and the shadow root 
> (implementation details), and address our cross-origin use case 
> elegantly as follows:
>
> rniwa.com/webkit.html <http://rniwa.com/webkit.html>
> ---------------------------------
> <!DOCTYPE html>
> <html>
> <head>
> <link rel=import href="https://webkit.org/components.html" 
> defines="share-button like-button">
> </head>
> <body>
> <like-button data-url="https://build.webkit.org/">Like 
> build.webkit.org <http://build.webkit.org></like-button>
> </body>
> </html>
>
> webkit.org/components.html <http://webkit.org/components.html>
> ---------------------------------
> <template defines="like-button" interface="LikeButtonElement">
> <!-- implicitly does 
> shadowRoot.appendChild(myTemplate.instantiate(shadowHost.dataset)); -->
> <form ...>
> <input type=hidden value="{{url}}">
> <button type=submit>Like!</button>
> </form>
> <script>
> LikeButtonElement.prototype.created = function (shadowRoot) {
> shadowRoot.query('form').onsubmit = function () {
> // ...
> }
> }
> </script>
> </template>
>
> - R. Niwa

Received on Monday, 11 November 2013 02:50:37 UTC