On Nov 12, 2013, at 8:12 AM, Dimitri Glazkov <dglaz...@chromium.org> wrote:
> On Sun, Nov 10, 2013 at 6:49 PM, Arthur Barstow <art.bars...@nokia.com> wrote:
> 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…
> 
> This could be an option and shouldn’t be the default.

I don’t think we want to add a yet another flag to control the behavior.

> By running scripts in a different context, we are ejecting the primary use 
> case of enabling frameworks/libraries to better manage their assets and 
> dependencies (aka the Bootstrap use case).

That would force same-origin and cross-origin use cases to have different 
behaviors because we can’t run the untrusted script in the host document.

In particular, it’s important for the global (window) object to have a 
consistent behavior between untrusted cross-origin and trusted same-origin use 
cases.

> Rob Dodson’s article has a nice progression explaining the use case: 
> http://robdodson.me/blog/2013/08/20/exploring-html-imports/

I don’t think this blog post contains any argument for either behavior.

> Also, check out newly minted Eric Bidelman's article on imports (especially 
> the use cases section at the bottom): 
> http://www.html5rocks.com/en/tutorials/webcomponents/imports/

I see the following argument for the current behavior:
Script in the import is executed in the context of the window that contains the 
importingdocument. So window.document refers to the main page document. This 
has two useful corollaries:
functions defined in an import end up on window.
you don't have to do anything crazy like append the import's <script> blocks to 
the main page. Again, script gets executed.
What we’re proposing is to execute the script in the imported document so the 
only real argument is the point that “functions defined in an imported end up 
on window” (of the host document).

I think that’s a bad thing.  We don’t want imported documents to start 
polluting global scope without the user explicitly importing them.  e.g. 
"import X" in Python doesn’t automatically import stuff inside the module into 
your global scope.  To do that, you explicitly say “import * from X”.  
Similarly, “using std” is discouraged in C++.

I don’t think the argument that this is how external script and stylesheet fly 
either because the whole point of web components is about improving the 
modularity and reusability of the Web.

> Re: Custom Elements LC, this is an issue to handle in HTML Imports 
> specification, not related to Custom Elements. 
> 
> 
> *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.
> 
> Re: Custom Elements LC, this should be handled in HTML Imports specification. 
> HTML Imports can rely on Custom Elements specification. Any additional hooks 
> that could be needed to facilitate this feature could be added in Custom 
> Elements Level 2 specification.
>  
> 
> *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:"rn...@webkit.org <mailto:rn...@webkit.org>"}));
> </script>
> 
> This seems very similar to the Rafael Weinstein's MDV work. You guys should 
> collaborate :)

Yes, we had an informal discussion about it a couple of weeks ago, and we’re 
interested in continuing our discussion with him on public-webapps.

> Re: Custom Elements LC, this is unrelated to specification.
>  
> *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');
> 
> This is another take on the declarative custom elements (a variant of [1]). 
> This particular approach has four problems that the WG was able to resolve 
> (at least the first three) in previous iterations:
> 
> 1) It is not friendly to ES6 classes. In fact, you can't use class syntax and 
> this syntax together.

We’re open to changing the proposed syntax to address this problem.

> 2) It couples templates, shadow DOM, and custom elements in a way that's 
> highly opinionated and inflexible. Throughout this year, we've tried many 
> various ways to get this right, and failed [2]. I highly recommend that we 
> avoid putting this into a specification now. Instead, we should let the best 
> practices evolve and build on the cowpaths.
> 
> 3) The approach pollutes global name space with constructors. This had been 
> voiced many times as unacceptable by developers.
> 
> 4) How does build a custom element that uses <name-card> as its base element? 
> What about <div> or any other HTML element?
> 
> The last one remains to be the hardest. The tortured inheritance support is 
> what killed <element> in the first place. We can't ignore the inheritance, 
> since it is clearly present, in both DOM and JS. If we attempt to punt on 
> supporting it, our decisions cut off the opportunities to evolve this right 
> in the future, and will likely leave us with boogers like multiple syntaxes 
> for inheritance vs. non-inheritance use cases.
> 
> I recommend studying the work the WG had already done here to avoid stepping 
> on the same rakes.
> 
> Re: Custom Elements LC, this functionality should be entirely defined as part 
> of <template>, relying on the Custom Elements specification. No changes to 
> Custom Elements specification are needed.
> 
> [1]: 
> http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/thread.html#msg75
> [2]: 
> http://lists.w3.org/Archives/Public/public-webapps/2013JulSep/thread.html#msg287

We will review the said discussions and get back to you.

> *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.
> 
> Re: Custom Elements LC, this functionality should be entirely defined as part 
> of <template>, relying on the Custom Elements specification. No changes to 
> Custom Elements specification are needed.
> 
> As an aside, there were several Polymer users who had live code that used 
> multiple templates and runtime logic to manage them, and various other 
> scenarios [1], [2], [3].
> 
> [1]: https://github.com/Polymer/polymer/issues/222
> [2]: https://github.com/Polymer/polymer/issues/156
> [3]: https://github.com/Polymer/polymer/issues/157

Ditto.

> *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="rn...@webkit.org 
> <mailto:rn...@webkit.org>”>
> 
> This is a cool idea, though coupling moustaches, templates, custom elements, 
> and data sets brings sadness. There are several library/framework authors who 
> will cry foul, since they rely on {{}} as their placeholders. Rafael 
> Weinstein have been working on this problem for several years now, it might 
> be good to work with him on something more flexible.

Yes, we’re interested in both static and dynamic value bindings for the 
template element in the long term.  However, just providing a mechanism to 
stamp out values statically goes a long way to address many use cases 
non-compositing components, such as a like button, have.

> Re: Custom Elements LC, this functionality should be entirely defined as part 
> of <template>, relying on the Custom Elements specification. No changes to 
> Custom Elements specification are needed.
>  
> *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.
> 
> We can't assume that Shadow DOM and Custom Elements are only used together. 
> There is already one framework already in existence that uses Custom 
> Elements, but not Shadow DOM [1], and we've already travelled this path [2], 
> [3].
> 
> Re: Custom Elements LC, we should not add this argument to "created" 
> callback, because this will lead to coupling Custom Elements with Shadow DOM 
> specification. Your template proposal could easily add a new 
> "shadowTreeCreated" callback that is invoked with this argument. The timing 
> of this invocation would be specified in the respective specification (which 
> I think is the HTML spec, since <template> had just moved there).
> 
> [1]: http://mozilla.github.io/brick/
> [2]: http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0521.html
> [3]: https://www.w3.org/Bugs/Public/show_bug.cgi?id=18748

Adding the optional 'template' argument to document.register and a new 
shadowTreeCreated callback would probably do the job on a technical level. But 
when you use it, you'd almost certainly want to use it in place of 
createdCallback instead of in addition, as it would be confusing to split your 
initialization. So it would be simpler to make it one callback with an optional 
argument, if there's a desire to use custom elements without shadow.

The point is not to make it impossible to use the technologies separately, but 
rather to make it convenient to use shadow dom + templates + custom elements 
together.

(Response credit: Maciej)

> *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).
> 
> I suspect that the boundary you're drawing at the custom element layer is 
> going to give you more trouble than it's worth. There are already notions of 
> proxy elements/HTMLKnownElements, which is just pure magic.
> 
> Fortunately, there is already a boundary that we built that might be just the 
> right fit for this problem: the shadow DOM boundary. A while back, we had 
> lunch with Mozilla security researchers who were interested in harnessing the 
> power of Shadow DOM, and Elliott (cc'd) came up with a pretty nifty proposal 
> called the DOMWorker. I nagged him and he is hopefully going to post it on 
> public-webapps. I am pretty sure that his proposal can address your use case 
> and not cripple the rest of the spec in the process.

Assuming you’re referring to 
https://docs.google.com/document/d/1V7ci1-lBTY6AJxgN99aCMwjZKCjKv1v3y_7WLtcgM00/edit,
 the security model of our proposal is very similar.  All we’re doing is using 
a HTML-imported document instead of a worker to isolate the cross-origin 
component.

Since we don’t want to run the cross-origin component on a separate thread, I 
don’t think worker is a good model for cross-origin components.

> Re: Custom Elements LC, if still necessary, this proposal is something that 
> should be investigated in Level 2 of the specification, as an additional 
> ability of custom elements.
>  
> *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>
> 
> Re: Custom Elements LC, this seems like something that would be part of the 
> Shadow DOM specification.

Yes.  It would be a very small addition to the existing specification but it 
would be extremely useful when coupled with an automatic binding of a template.

- R. Niwa

Reply via email to