W3C home > Mailing lists > Public > www-style@w3.org > February 2014

Re: Shadow DOM Encapsulation

From: Peter Linss <peter.linss@hp.com>
Date: Thu, 6 Feb 2014 18:54:01 -0800
Cc: www-style list <www-style@w3.org>
Message-Id: <B2A82337-1ADF-40F7-B21D-AC94B9161833@hp.com>
To: Tab Atkins Jr. <jackalmage@gmail.com>

On Feb 6, 2014, at 5:23 PM, Tab Atkins Jr. <jackalmage@gmail.com>
 wrote:

> On Thu, Feb 6, 2014 at 4:40 PM, Peter Linss <peter.linss@hp.com> wrote:
>> Please note that this was not the only proposal, and each of the problems you point out below were addressed by my proposal.
>> 
>> To reiterate for the new thread, I proposed two mechanisms for exporting pieces of a shadow component. Either or both could be implemented.
>> 
>> The first would be to allow exporting pieces by a simple flag, rather than a part='' attribute you could simple specify <piece export>...</piece>. The piece would be exported by its tag name and the export would also apply to all nested pieces, unless there was another flag to turn it off, such as: ''no-export' (or maybe 'public' and 'private' for the attribute names). If you want to set the exported name as something other than the tag name you could specify that as export='name'. This would allow the creation of a fully public component by simply adding a single attribute to the shadow root node and addresses your problem #1.
> 
> This could work, and it's similar to the simple notion of "hidden, and
> semi-closed" that shadow DOM considered for JS.
> 
> Note, though, that you can reverse the meaning too - rather than
> closing by default and opening with a flag, you could be open by
> default and hide things with a flag.  That's compatible with an
> evolution of our current approach, and compatible with the JS API,
> which is "hidden, and open" by default.

I personally prefer the opt-in, but yes, it could just as easily be opt-out by default.

> 
> Note also that this isn't quite as easy as it sounds.  When you expose
> things, where are they relative to everything else?  

They are in the same place as they are defined, the structure is preserved for everything exported.

For example, given the shadow DOM:
<component>
  <part-one export>
    <sub-part-one />
  </part-one>	
  <part-two>
    <sub-part-two export>
      <sub-sub-part-two />
    </sub-part-two>
  </part-two>
</component>

You'd see it as though it were defined as:
<part-one>
  <sub-part-one />
</part-one>
<sub-part-two>
  <sub-sub-part-two />
</sub-part-two>

> CSS syntax needs
> to see a tree, or at least trees of trees.  Are all the exported
> pieces independent islands that you can only reach by ::part(), and
> *then* you can use normal CSS syntax on the subtree rooted there?

No, you're conflating my proposal with Ted's. Under my proposal, each exported piece of a shadow dom is simply addressed as though it were a pseudo-element with the same name (unless given an explicit name with export='name'). There is no ::part() pseudo, which I agree is overloading the concept of a pseudo-element.

So you simply use "normal" (assuming the relaxed pseudo-element selector rules) CSS matching for addressing anything inside a component, you just address them as pseudo elements, e.g.:
component.foo:first-child ::sub-part-two { ... }

> 
>> The second proposal was to provide an optional style map for the component. We could create a simple syntax to define an arbitrary 'style node' tree and map it to component pieces via selectors. I don't have a specific syntax at hand, but if folks are interested I'm sure I could come up with something reasonable fairly quickly.
> 
> This sounds potentially cool, and a good thing to pursue for the
> future, but I can't help but get a huge "over-engineering" vibe from
> any though of doing this in V1.

I have no issue with deferring the style map to a future level, I just mentioned it as a level of abstraction that we can use to decouple the exported shadow structure from the actual shadow-dom structure.

Note that the style map approach would let you define an arbitrary pseudo-element structure to export and a mapping to the internal structure.

> 
>> The other part of my proposal was to simply use pseudo-elements to directly select the exported component pieces by name, and to relax the restrictions on pseudo-element selectors to allow pseudo-classes, attribute selectors, and descendants (with combinators).
>> 
>> With those restrictions relaxed, it's not necessary to flatten the pseudo-element space, so the internal structure of exported pieces could be maintained.
>> 
>> So a piece could be selected as:
>> component ::piece { ... }
>> it's child could be selected as:
>> component ::piece ::child-peice { ... }
>> etc, with the full expressiveness of selectors. This addresses your points #2 & #3.
> 
> Well, no, not quite.  You're using spaces where they're not allowing.
> It'd have to be at least:
> 
>  component::piece::child-piece
> 
> Or something else for the child-piece part that allowed combinators.

No, I really meant spaces, i.e. descendant combinators (see "loosening restrictions on use of pseudo-element selectors" in a previous message). You could use other combinators as needed to address specific nodes. There's no magic pseudo-element or combinator needed to pierce the shadow layer, the shadow component simply exports its parts as a structure of pseudo-elements.

The only interesting thing here is that there's an implied > between all pseudo-elements and the rest of the selector, so:
p::first-line
is morally equivalent to:
p > ::first-line
That's the way Gecko at-least used to do it internally...

We did this back in '98 or so in Gecko when building form controls out of "anonymous" boxes and hidden dom nodes (for content and event handling), essentially the 16 year-old precursor to today's shadow-dom. The interesting "anonymous" boxes each had a pseudo-element name and you could simply style them with normal CSS selectors.

> 
>> While allowing a component to simply opt-in to exposing it's internals to selectors, this also allows components to expose only a subset (or nothing). The style map solution would allow the component to describe an arbitrary structure to the selector system and map it to an arbitrary internal structure. Allowing the internals of the component to change without breaking external references.
> 
> Ultimately, there's a strong argument against *any* attempt to lock
> down by default and only expose some pieces: because the JS side is
> hidden-but-open, the page's JS can just walk down the tree and open
> everything up by itself.  Any *default* restrictions we put in place
> are thus mere suggestions; we're not actually helping anyone lock down
> their components, which is the entire argument for being more
> restrictive in the first place.  This isn't a weird hack, either -
> it's very trivial to do.
> 
> The current CSS mechanism of hidden-but-open with cat/hat combinators
> doesn't suffer from this problem, because it's an exact analog of the
> capabilities offered by JS.  It doesn't pretend to offer more safety
> than it really does.
> 
> When we *do* offer more restrictive CSS shadow-piercing, we'll
> probably want to do it in conjuction with more restrictive JS shadow
> piercing, when we actually provide a way to hide a shadow DOM.  (This
> doesn't have to be a strong security boundary, just something
> sufficiently difficult to remove that you're clearly attacking things
> rather than just hacking around.)

I see no reason why this approach can't also be used with querySelector to expose the exact same structure via JS.

I accept the symmetry argument between the structure exposed to selectors and the JS APIs, but this isn't the right place to debate the JS APIs.

Peter


Received on Friday, 7 February 2014 02:54:26 UTC

This archive was generated by hypermail 2.3.1 : Friday, 7 February 2014 02:54:27 UTC