On Nov 26, 2013, at 10:15 PM, Dominic Cooney <domin...@google.com> wrote:

> On Wed, Nov 27, 2013 at 2:19 PM, Ryosuke Niwa <rn...@apple.com> wrote:
> 
> On Nov 27, 2013, at 8:57 AM, Dominic Cooney <domin...@google.com> wrote:
> 
>> On Tue, Nov 26, 2013 at 2:03 PM, Ryosuke Niwa <rn...@apple.com> wrote:
>> Hi,
>> 
>> I have been having informal discussions of our earlier proposal for 
>> cross-orign use cases and declarative syntax for web components, and I 
>> realized there was a lot of confusion about our motivations and decision 
>> decisions.  So I wanted to explain why/how we came up that proposal in this 
>> email.
>> 
>> 
>> Problem: A lot of websites embed SNS widgets, increasing the security 
>> surface of embedders.  The old version of techcrunch.com, for example, had 
>> 5+ social share buttons on each article.  If any one of those SNS websites 
>> got compromised, then the embedder will also get compromised.
>> 
>> This is a valid problem. Does anyone have related use cases that might be 
>> in-scope for this discussion?
> 
> Comment forms (e.g. DISQUS) is another important use case.
> 
>> What if we used iframe?
>> What if we replaced each such instance with an iframe?  That would give us a 
>> security boundary.
>> 
>> On the other hand, using an iframe for each social button is very expensive 
>> because each iframe loads a document, creates its own security origin, JS 
>> global object, and so forth. Initializing new script context (a.k.a. "VM", 
>> "world", "isolate", etc…) for every single SNS widget on a page is quite 
>> expensive.  If we had 10 articles, and each article had 5 social buttons, 
>> we'll have 50 iframes, each of which needs to load megabytes of JavaScript.
>> 
>> iframe is also heavily restricted in terms of its ability to layout itself. 
>> Comment widgets (e.g. DISQUS) for example need to stretch themselves to the 
>> height of its content.
>> 
>> We also need a better mechanism to pass arguments and communicate with 
>> cross-origin frames than postMessage.
>> 
>> 
>> What if we made iframe lighter & used seamless iframe?
>> The cost of iframe could be reduced substantially if we cached and 
>> internally shared each page's JavaScript.  However, we still have to 
>> instantiate its own script context, document, and window objects.
>> 
>> We can also use seamless iframe to address the comment widget use case.
>> 
>> 
>> What if we let each iframe create multiple "views"?
>> The problem with using an iframe for a cross-origin widget is that each 
>> iframe creates its own document, window, etc… even if there are multiple 
>> widgets from the same origin.  e.g. if we had a tweet button on 10 different 
>> articles, we have to create its own document ,window, etc… for each tweet 
>> button.
>> 
>> We can reduce this cost if we could share the single frame, and have it 
>> render multiple "views".  Naturally, each such view will be represented as a 
>> separate DOM tree.  In this model, a single iframe owns multiple DOM trees, 
>> each of which will be displayed at different locations in the host document. 
>>  Each such a DOM tree is inaccessible from the host document, and the host 
>> document is inaccessible from the iframe.
>> 
>> This model dramatically reduces the cost of having multiple widgets from the 
>> same origin.  e.g. if we have 10 instances of widgets from 5 different 
>> social networks, then we'll have only 5 iframes (each of which will have 10 
>> "views") as opposed to 50 of them.
>> 
>> 
>> What if we provided a declarative syntax to create such a view?
>> Providing a better API proved to be challenging.  We could have let page 
>> authors register a custom element for each cross-origin widget but that 
>> would mean that page authors have to write a lot of script just to embed 
>> some third-party widgets.  We need some declarative syntax to let authors 
>> wrap an iframe.
>> 
>> Furthermore, if we wanted to use the multiple-views-per-iframe, then we'll 
>> need a mechanism to declare where each instance of such a view is placed in 
>> the host document with arguments/configuration options for each view.
>> 
>> A custom element seemed like a natural fit for this task but the 
>> prototype/element object cannot be instantiated in the host document since 
>> the cross-origin widgets' script can't run in the host document and 
>> prototype objects, etc… cannot be shared between the host document and the 
>> shared iframes.  So we'll need some mechanism for the shared iframe to 
>> define custom element names, and have the host document explicitly import 
>> them as needed.
>> 
>> 
>> At this point, the set of features we needed looked very similar to the 
>> existing custom element and shadow DOM.  Each "view" of the shared iframe 
>> was basically a shadow DOM with a security boundary sitting between the host 
>> element and the shadow root.  The declarative syntax for the "view" was 
>> basically a declarative syntax of a custom element that happens to 
>> instantiate a shadow DOM with a caveat that the shadow host is inaccessible 
>> form the component, and the shadow DOM is inaccessible from the host 
>> document.  It also seemed natural for such an "shared iframe" to be loaded 
>> using HTML imports.
>> 
>> 
>> You can think of our proposal as breaking iframe down into two pieces:
>> Creating a new document/window
>> Creating a new view
>> I think decomposing the problem this way is a good step.
>> 
>> Re: creating a new document/window, purely in terms of *mechanics*, IFRAME 
>> does this already. Is anything else required?
> 
> The problem is that iframe does both 1 and 2 but I agree that iframe already 
> provides this mechanism if we set style=display:none.  But it would be really 
> ugly and cumbersome if we had to import various SNS widgets with iframe with 
> style set to display:none.
> 
> Right. I think it will help us divide and conquer the problem if we can work 
> on (a) API aesthetics, and separately (b) mechanics in terms of as much 
> existing stuff as possible (IFRAME, viewport, etc.) I don't necessarily mean 
> taking that existing stuff as-is, but maybe pulling chunks out of the 
> existing stuff and specing it so it will explain the legacy stuff and work in 
> these new combinations for this new use case.

Yeah, that makes sense.

>> Re: creating a new view, this is really interesting to me. It seems there 
>> are a few different parts, I think most of these are needed for the use case 
>> above; I've also noted where we might break out and "explain" some existing 
>> part of the platform.
>> 
>> - Arranging the rendering of a DOM (sub)tree into a "view". IFRAME, 
>> ShadowRoot and indeed just "rendering in general" do this.
>> - Arranging the rendering of something else into a "view". Replaced elements 
>> like OBJECT and IMG do this. Maybe this is just trivially "arrange the 
>> rendering of a DOM containing CANVAS" though.
>> - Communicating or blocking layout across the "view" boundary. Cases where 
>> information flows outside-in: the viewport-document relationship; IFRAME. 
>> Cases where information flows two ways: seamless IFRAME, Shadow DOM, layout 
>> in general.
>> - Something about laying things out/rendering outside the bounds of the 
>> "view". Shadow DOM and does this (you can rel/abs/fixed position stuff 
>> outside of the host element bounds.) This is a tricky one... in scope or 
>> does Shadow DOM remain a special case? Would some embedders trust a 
>> component enough to let them clickjack them, just not steal their cookies, 
>> etc.?
> 
> Right.  We need to add something like overflow: clip by default to prevent 
> click hijacking.
> 
> Is overflow: clip sufficient?
> 
> If we're trying to map this to primitives, does this mean that the UA 
> stylesheet has a high specificity rule which says "if you're one of these 
> elements entangled with a viewport, overflow: clip"?
> 
> I note that fb:like has a "flyout". Do you think it is a reasonable use case? 
> Should the component author be allowed to detect when their view-thing will 
> clip them or not?
>> and providing a mechanism to do 2 without doing 1 (or that doing 2 multiple 
>> times after doing 1 once), and making it usable with a declarative syntax.
>> 
>> This definitely deserves to be bullet 3--usable with declarative syntax.
>> 
>> To clarify that I understand--the importance of succinct declarative syntax 
>> is so that the embedder doesn't end up including the "shim" script for Foo's 
>> widget from foo.com, which means trusting foo.com which was the whole point! 
>> Right?
> 
> Right.  Using Foo widget from foo.com should NOT involve running scripts from 
> foo.com in the host document.
> 
>> It would be nice if we could solve this problem in a layered way. For 
>> example, I think the "view" stuff above is a lower-level primitive, and the 
>> declarative syntax should be explained in terms of (something for getting a 
>> window+document--IFRAME?) plus "view" plus (extremely small alpha that 
>> explains how the stuff is wired up.)
> 
> That makes sense although we haven't come up with use cases where we just 
> want to use the multiple "views" cross-origin without the declarative syntax.
> 
> What about the status quo, where the embedder trusts the component being 
> embedded, but the component doesn't trust the embedder?

Authors can keep using script elements for that use case.  Is there some 
existing problem we want to solve in that use case?

> The component will be running scripts in the embedder's context (perhaps the 
> component has script API built on postMessage to the IFRAME) but for 
> efficiency its desirable to have one IFRAME for the multiple like buttons, 
> etc.
>> I guess it is OK if the API is not declarative on the widget side? If we 
>> assume the widget enjoys the isolation of an IFRAME, is performance the 
>> primary motivator on this side?
> 
> Being declarative will definitely benefit the performance because preload 
> scanner, etc… could detect what kind of "views" are exposed/implemented in a 
> given "slave" (or "widget") document without running scripts.
> 
> Also, I'd imagine a lot of widgets would end up using templates so having to 
> manually instantiate those templates would be annoyance.
> 
> Having written some basic apps with Polymer, it's evidently feasible to wrap 
> the template stamping up in a library. 
>> It would be nice if the widget author could get something rendered very 
>> quickly.
> 
> Right.
> 
>> I think this "declarative" part of the problem breaks down this way:
>> 
>> - How the page author "invokes" something in the embedded component. How is 
>> it named and how does the author mention the name?
> 
> So I think a custom element is the natural mechanism. e.g.
> 
> <import src="http://foo.com/widget.html"; customelements="foo-button">
> <foo-button>Foo this</foo-button>
> 
> I see the appeal of Custom Elements, because it has a way to define a name 
> (document.register), mention a name (createElement, write markup, etc.) and 
> has a model of instantiating elements. But it has baggage you don't want, 
> like prototypes and constructors (on the embedder side.) There's also all the 
> details of this viewport entangling. Likewise with HTML Imports, they've got 
> some things you want (new document) but some things you don't (shared window, 
> shared globals) and some things I'm unsure about (synchronous versus 
> asynchronous).

Right.  Perhaps we could either extract the common base of the custom elements.

Alternatively, we can provide a mechanism to auto-create custom elements as a 
wrapper for cross-origin widgets.  i.e. we want to have the imported document 
create a DOM tree given a name of tag/element, and then securely insert it 
somewhere in the host document as a custom element.

The downside of this approach is that now authors have to deal with two ways of 
defining widgets/components for same origin and cross origin use cases.

> I don't immediately have any better ideas so this is the straw man for now. 
> As we work through the details we might come up with some tweaks or 
> alternatives.
> 
> I guess that's another reason to sweat the small stuff--if we had prototype 
> implementations of element-view entangling and so on we could polyfill some 
> of the high level declarative syntax ideas and bounce prototypes off real web 
> developers and use cases.
>  
> Note that we can't let the imported "slave" document define an arbitrary set 
> of custom elements by default.
> 
> is=blah syntax isn't as useful/interesting here because it's unusual to use a 
> cross-origin widget to replace an existing built in HTML element.
> 
>> - How does the embedding page understand that there's an "instance" of their 
>> stuff contributing to the main page now?
> 
> Again, the custom element's created callback is a very nice mechanism for 
> that.
> 
>> - How does the author configure an instance from the embedded component? 
>> Presumably the button needs to know something things from its embedder, like 
>> API keys, etc.
> 
> 
> If we decided that each "view" is a custom element, then a very natural way 
> for it to communicate the information is via data attributes.
> 
> I note that fb:like already uses data- so there's precedent for that.

Right.

> Are there problems with data-?

Not that I know of.

> Where would the component access them? Can they see updates? Are updates one 
> way or bi-directional?

So if we had used shadow DOM as the security boundary, we can expose dataset on 
the shadow root, and have it sync'ed with data attributes on the shadow host.

- R. Niwa

Reply via email to