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. > 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. > 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. > 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. > 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> 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. - R. Niwa