Hi Maciej! First off, I really appreciate your willingness to get into the mix of things. It's a hard problem and I welcome any help we can get to solve it.
I also very much liked your outline of encapsulation and I would like to start using the terminology you introduced. I am even flattered to see the proposal you outlined, because it's similar to the one we originally considered as part of the first iteration of the API (https://raw.github.com/dglazkov/component-model/cbb28714ada37ddbaf49b3b2b24569b5b5e4ccb9/dom.html) or even earlier versions (https://github.com/dglazkov/component-model/blob/ed6011596a0213fc1eb9f4a12544bb7ddd4f4894/api-idl.txt) We did remove them however, and opted for the simplest possible API, which effectively only exposes the shadow DOM part of the component model (see my breakdown here http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/1345.html). One of the things to keep in mind is that the proposal outlined in http://dglazkov.github.com/component-model/dom.html is by no means a complete component model API. It's just the smallest subset that can already be useful in addressing some of the use cases listed in http://wiki.whatwg.org/wiki/Component_Model_Use_Cases. It seem obvious that it is better to have few small, closely related useful bits that could be combined into a bigger picture rather than one large monolithic feature that can't be teased apart. As for addressing encapsulation concerns, one of the simplest things we could is to introduce a flag on the ShadowRoot (we can discuss the default value), which if set, prohibits access to it with the element.shadow property. :DG< On Wed, Jun 29, 2011 at 9:03 PM, Maciej Stachowiak <m...@apple.com> wrote: > > > I am not a fan of this API because I don't think it provides sufficient > encapsulation. The words "encapsulation" and "isolation" have been used in > different ways in this discussion, so I will start with an outline of > different possible senses of "encapsulation" that could apply here. > > == Different kinds of "encapsulation" == > > 1) Encapsulation against accidental exposure - DOM Nodes from the shadow tree > are not leaked via pre-existing generic APIs - for example, events flowing > out of a shadow tree don't expose shadow nodes as the event target, > > 2) Encapsulation against deliberate access - no API is provided which lets > code outside the component poke at the shadow DOM. Only internals that the > component chooses to expose are exposed. > > 3) Inverse encapsulation - no API is provided which lets code inside the > component see content from the page embedding it (this would have the effect > of something like sandboxed iframes or Caja). > > 4) Isolation for security purposes - it is strongly guaranteed that there is > no way for code outside the component to violate its confidentiality or > integrity. > > 5) Inverse isolation for security purposes - it is strongly guaranteed that > there is no way for code inside the component to violate the confidentiality > or integrity of the embedding page. > > > I believe the proposed API has property 1, but not properties 2, 3 or 4. The > webkitShadow IDL attribute violates property #2, I assume it is obvious why > the others do not hold. > > I am not greatly interested in 3 or 4, but I believe #2 is important for a > component model. > > > == Why is encapsulation (type 2) important for components? == > > I believe type 2 encapsulation is important, because it allows components to > be more maintainable, reusable and robust. Type 1 encapsulation keeps > components from breaking the containing page accidentally, and can keep the > containing page from breaking the component. If the shadow DOM is exposed, > then you have the following risks: > > (1) A page using the component starts poking at the shadow DOM because it can > - perhaps in a rarely used code path. > (2) The component is updated, unaware that the page is poking at its guts. > (3) Page adopts new version of component. > (4) Page breaks. > (5) Page author blames component author or rolls back to old version. > > This is not good. Information hiding and hiding of implementation details are > key aspects of encapsulation, and are good software engineering practice. > Dmitri has argued that pages today do a version of components with no > encapsulation whatsoever, because many are written by monolithic teams that > control the whole stack. This does not strike me as a good argument. > Encapsulation can help teams maintain internal interfaces as they grow, and > can improve reusability of components to the point where maybe sites aren't > quite so monolithically developed. > > Furthermore, consider what has happened with JavaScript. While the DOM has no > good mechanism for encapsulation, JavaScript offers a choice. Object > properties are not very encapsulated at all, by default anyone can read or > write. But local variables in a closure are fully encapsulated. It's more and > more consider a good practice in JavaScript to build objects based on > closures to hide implementation details. This is the case even though the > closure approach is more awkward to code, and may hurt performance. For > ECMAScript Harmony, a form of object that provides true encapsulation is > being considered. > > I don't want us to make the same mistake with DOM components that in > retrospect I think was made with JavaScript objects. Let's provide good > encapsulation out of the gate. > > And it's important to keep in mind here that this form of encapsulation is > *not* meant as a security measure; it is meant as a technique for robustness > and good software engineering. > > > == Are there use cases for breaking type 2 encapsulation against the will of > the component? == > > I'm not aware of any. I asked Dmitri to explain these uses cases on IRC and > he didn't have any specific ones in mind, just said that exposing the shadow > DOM directly is the simplest thing that could possibly work and so is easy to > prototype and implement. I think there are other starter approaches that are > easy to implement but provide stronger encapsulation. > > > == Are there other limitations created by the lack of encapsulation? == > > My understanding is yes, there are some serious limitations: > > (1) It won't be possible (according to Dmitri) to attach a binding to an > object that has a native shadow DOM in the implementation (e.g. form > controls). That's because there can only be one shadow root, and form > controls have already used it internally and made it private. This seems like > a huge limitation. The ability to attach bindings/components to form elements > is potentially a huge win - authors can use the correct semantic element > instead of div soup, but still have the total control over look and feel from > a custom script-based implementation. > > (2) Attaching more than one binding with this approach is a huge hazard. > You'll either inadvertently blow away the previous, or won't be able to > attach more than one, or if your coding is sloppy, may end up mangling both > of them. > > I think these two limitations are intrinsic to the approach, not incidental. > > > == OK Mr. Fancypants, do you have a better proposal? == > > I haven't thought deeply about this, but here's a sketch of a component model > that is just as trivial to use and implement as what is proposed, yet > provides true encapsulation. It sticks with the idea that the only way to > create a component DOM is programmatically. But it immediately provides some > advantages that I'll expand on after throwing out the IDL: > > interface Component { > // deliberately left blank > } > > interface TreeScope : Node > { > readonly TreeScope parentTreeScope; > Element getElementById (in DOMString elementId); > } > > interface BindingRoot : TreeScope { > attribute bool applyAuthorSheets; > readonly attribute Element bindingHost; > }; > > [Callback] > interface ComponentInitializer { > void initialize(in BindingRoot binding); > }; > > partial interface Document { > Component createComponent(in Node template, in ComponentInitializer > initializer); > }; > > partial interface Element { > bindComponent(in Component component); > unbindComponent(in Component component); > } > > The way this works is as follows: > > (1) The component provider creates a DOM tree that provides templates for > bindings that instantiate that component. > (2) The component provider also makes an init function (represented above as > a callback interface) which is called whenever an instance of the component > is bound (see below). > (3) The component provider calls createComponent with these things and gets > back a Component object, which is completely opaque and exposes no > implementation details (though if we wanted we could add some visible > metadata attributes like a name or what have you) but can be bound to an > element. > (4) The component provider hands back this token to its client. > (5) The client can instantiate the component as many times as it wants by > binding it to one or more elements. > (6) When Element.bindComponent is called, it creates the BindingRoot, clones > the template into it as children (it can be a DocumentFragment if desired), > attaches it to the element, and then calls the initializer function on the > newly created binding root, so that it can set up event listeners and such. > > > Notice that this scheme is not significantly more complex to use, spec or > implement than the shadow/shadowHost proposal. And it provides a number of > advantages: > > A) True encapsulation is possible, indeed it is the easy default path. The > component provider has to go out of its way to deliberately break > encapsulation, though of course it can if it wants to. > B) Binding is atomic - the shadow root is not built up incrementally so you > can't have a half-built binding. > C) Has a natural extension to allowing more than one binding on an element, > having a stacking behavior like XBL. > D) Can readily support custom components bound to form controls, with either > semantics of stacking on top of the native binding or replacing it. > E) Avoids use of the jargon-ish, mysterious and potentially confusing term > "shadow" in favor of "binding" and "component" which are much more clear IMO. > F) The code to build up the DOM for the binding with raw DOM calls only has > to run once. After that it just gets cloned, which is likely faster than > creating a whole fresh one with raw DOM calls. > G) Naturally extensible to other ways of creating components, perhaps on a > declarative template a la XBL2. > > > I hope this comprehensively expresses my concerns, and I believe I have shown > at least one way in which my concerns about encapsulation can be addressed > without adding unwarranted complexity. > > > Regards, > Maciej > >