A user account is required in order to edit this wiki, but we've had to disable public user registrations due to spam.
To request an account, ask an autoconfirmed user on Chat (such as one of these permanent autoconfirmed members).
Component Model Isolation Brainstorming
Overview
Depending on the source and the purpose of a component it may be desirable to limit access to the component from the containing document, or vice versa. E.g., a page may want to include components from a third party out of convenience, but limit the access those components have to other contents of the page. Conversely, a component with a complicated internal structure and scripting may want to disallow the containing page from (inadvertently, perhaps) meddling with its internal structure. There are several dimensions to this problem:
- DOM access via JavaScript
- event propagation
- CSS
- styling
- changing of display type, or setting display: none
- generated content
Note that since components may include other components, "containing document" may also refer to "containing component", or - to be more general - "containing tree scopes".
Component distrusts Document
JavaScript
getElementById()
,getElementsByTagName()
,getElementsByName()
andgetElementsByClassName()
(and their...NS
incarnations) do not reach into shadow trees.
Exception: none
querySelector()
andquerySelectorAll()
do not reach into shadow trees, even when using a shadow pseudo-ID (!).
Exception: none
- The host element's
.shadow
property is null (TODO: as an alternative, accessing it throws an exception?).
Exception: TODO: the template has the attribute allow-shadow-accessor
(?)
CSS
- Style rules of the containing document do not match nodes in the shadow tree.
Exception: Shadow tree elements that have explicit pseudoID
attributes may match, provided they are explicitly matched by an equivalent pseudo-element selector.
Exception: The template has the allow-selectors-through
attribute: outside selectors may cross into the shadow tree.
Exception: The template has the apply-author-sheets
attribute: outside selectors may be applied fully within the shadow tree. (TODO: is that really required on top of allow-selectors-through
?)
- Nodes in the shadow tree do not inherit styles from the host element
Exception: TODO: define a shadow tree to reset all CSS attributes to initial
, but allow a (scoped) style sheet to set them to inherit
?
- A child or descendant selector of a shadow pseudo-ID selector (e.g.,
host::pseudo div
) does not match, even if the shadow tree's element with pseudo-IDpseudo
does happen to have a<div>
descendant. In other words, the shadow pseudo-ID simple selector(s) must be a member of the last selector sequence.
Exception: none
- TODO: prevent setting
display
andcontent
?
Exception: TODO: see "CSS filtering" below.
- TODO: prevent generated content - i.e.,
::before
and::after
?
Exception: TODO: ?
- TODO: in general, add a way to specify specifically which CSS attributes are allowed?
Document distrusts Component
JavaScript
- A component does not have access to
document
, norwindows
.treeScope.document
returns null (TODO: throws?).
Exception: The document specifies that it's ok that bindings from that source access its DOM (TODO: how?).
- A component has access to its tree scope, but not to its parent tree scope.
treeScope.parentTreeScope
returns null (TODO: throws?).
Exception: The parent tree scope (document or template) specifies that it's ok that bindings from that source access its DOM (TODO: how?).
- A component does not have access to its host element.
element.treeScope.hostElement
is null (TODO: throws?).
Exception: The containing document or template specifies that it's ok that bindings from that source access the host element (TODO: how?)
- TODO: Communication to the containing page takes place via attribute-forwarding and events?
TODO: is it safe to allow the document to grant access to the document
or window
object to a component, even considering that that component might be included as part of a different component elsewhere? In this case the nested component could manipulate the document, and the document's DOM, but if the containing component distrusts the document (and by extension presumably the nested component as well), it would not get access to the containing component from the document.
CSS
- Style rules are not applied to nodes outside the shadow tree, even if declared in style sheets that are defined or imported via
<style>
that has no scoped attribute.<style>
elements within a shadow tree that don't have thescoped
attribute set must be treated as if it was set and every rule was prefixed with:root
.
Exception: The containing document or template does not prohibit that style sheets of bindings from that source may affect its nodes (default: allowed) (TODO: how?)
- Children of the host element do not inherit styles from a shadow tree's
<content>
element that they are rendered "under".
Exception: The <content>
element has the apply-binding-sheets
attribute AND the containing document or template does not prohibit that it's ok that bindings from that source act as inheritance parent (default: allowed) (TODO: how?)
Suggested Approach
Introduce attributes isolated
and confined
(and later perhaps sealed
). The element that has one or more of these attributes set is called the boundary element. Once set, these attributes cannot be unset. The meaning of the attributes is as follows:
isolated
- Events that bubble up to the boundary element are re-targeted to appear as to have originated from the boundary element
- CSS selectors will not cross the boundary element
- Style rules set outside the boundary will not apply to elements within
getElementById()
, etc., will not return descendants of the boundary element- the boundary element's
.innerHTML
will return an empty string,.outerHTML
will return no content between the boundary element tags - the boundary element appears as if it has no children
- the boundary element's
appendChild()
,insertBefore()
andremoveChild()
will throw
sealed
Disallow derived components (i.e., isolated
vs. derived components) - once we define how derived components work in the first place.
confined
- Events that bubble down to the boundary element are re-targeted to appear as to have originated from the boundary element
- CSS selectors will not cross the boundary element
- Style rules set within the boundary will not apply to elements outside
- Scripts within the boundary are run as if loaded by a separate document. They have no access to the containing document, nor its DOM.
- To scripts within the confinement, the boundary element's
parentNode
will be null. :root
will be equal to the boundary element, as will:scope
by default.
Discussion and Caveats
At first I'd suggest these attributes to only apply to specific elements, such as <decls>. However, AFAICT, this could be useful on arbitrary elements (?). Also, rather than being simply boolean, they could list a set of keywords 'javascript
', 'events
', 'css
'. e.g., isolated="javascript events"
to isolate with regard to scripts and events, but allow CSS through (this would imply that css selectors on style rules can cross the boundary, but the same selector used in querySelector()
couldn't). But I'd leave this for when there's an actual use case.
Isolation/confinement has to be contagious - i.e., a confined <template> cloned into a DocumentFragment must cause the DocumentFragment to be confined. That DocumentFragment appended into a ShadowRoot must cause the ShadowRoot to be confined.
One more caveat: <decls>
may need to be special in that isolated
and confined
are applied as if they'd be present on every child of <decls>
instead. Otherwise, <binding>
s within <decls isolated>
wouldn't be visible from the outside.
Now, the $1.000 question: how much of the above mirrors <iframe>
and how much could we solve by simply wrapping the shadow contents in an <iframe>
(isolation), or the host element of a component (confinement) - apart from it being awkward?