Skip to content

Commit

Permalink
Merge pull request #39 from WICG/sync
Browse files Browse the repository at this point in the history
Update spec & explainer to add sync method and clarify behavior
  • Loading branch information
rakina authored Oct 17, 2018
2 parents 8cbe2f4 + 7de153e commit f01227b
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 46 deletions.
51 changes: 41 additions & 10 deletions explainer.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
Expand All @@ -17,9 +18,9 @@

A web page may contain tens of thousands of web components. The styles for these components will be specified in a small number of style sheets, perhaps one for each component library.

Each web component uses Shadow DOM. For a style sheet to take effect within the Shadow DOM, it currently must be specified using a style element within each shadow root.
Most web component uses Shadow DOM. For a style sheet to take effect within the Shadow DOM, it currently must be specified using a `<style>` element within each shadow root. This can easily have a large time and memory cost if user agents force the style sheet rules to be parsed and stored once for every style element.

This can easily have a large time and memory cost if user agents force the style sheet rules to be parsed and stored once for every style element. Some user agents might attempt to optimize by sharing internal style sheet representations across different instances of the style element. However, component libraries may use JavaScript to modify the style sheet rules, this will thwart style sheet sharing and have large costs in performance and memory.
Some user agents might attempt to optimize by sharing internal style sheet representations across different instances of the style element. However, component libraries may use JavaScript to modify the style sheet rules, which will thwart style sheet sharing and have large costs in performance and memory.

Often, the main functionality inside the shadow root is provided by a single HTML element that may not itself have children. For a style sheet to also appear inside the shadow root, the shadow root can't be the single HTML element, or a style element, instead it must be a third element that simply contains the style element and the main HTML element that provides the functionality. Thus the shadow root contains three elements when one would otherwise suffice.

Expand All @@ -29,20 +30,50 @@ Early versions of the Web Component specifications allowed shadow piercing (/dee

### Proposed Solution

We can provide an API for creating stylesheet objects from script, without needing style elements. Script can optionally add or remove rules from a stylesheet object. Each stylesheet object can be added directly to any number of shadow roots (and/or the top level documemt).
We can provide an API for creating stylesheet objects from script, without needing style elements. Script can optionally add or remove rules from a stylesheet object. Each stylesheet object can be added directly to any number of shadow roots (and/or the top level document), which are in the same document tree where it is constructed on.

### Example Usage

```
```js
// Create style sheet when registering components.
let styleSheetList = new StyleSheetList([
await document.createCSSStyleSheet("hr { color: green; }")
]);
// ...
let someStyleSheet = document.createCSSStyleSheetSync("hr { color: green}");
let anotherStyleSheet = await document.createCSSStyleSheet("@import fancystyle.css")

// Apply style sheet in custom element constructor.
shadowRoot.adoptedStyleSheets = styleSheetList;
shadowRoot.adoptedStyleSheets = new StyleSheetList([someStyleSheet, anotherStyleSheet]);

// Apply style sheet in top level document.
document.adoptedStyleSheets = new StyleSheetList([someStyleSheet]);
```

### Behavior
* Each constructed `CSSStyleSheet` is "tied" to the `Document` it is constructed on, meaning that it can only be used in that document tree (whether in a top-level document or shadow trees).
* Example:
```html
<body>
<iframe id="someFrame">some frame</iframe>
<div id="someDiv">some div</div>
</body>
<script>
let sheetList = new StyleSheetList([
document.createCSSStyleSheetSync("* { color: red; })")
]);
// this will fail
someFrame.contentDocument.adoptedStyleSheets = sheetList;
// this will work
let shadowRoot = someDiv.attachShadow({mode: "open"});
shadowRoot.adoptedStyleSheets = sheetList;
</script>
```
* After a stylesheet is added to `DocumentOrShadowRoot`s, changes made to the stylesheet will also reflect in those `DocumentOrShadowRoot`s.
* Example:
```js
let sheet = document.createCSSStyleSheetSync("* { color: red; })");
document.adoptedStyleSheets = new StyleSheetList([sheet]);
sheet.insertRule("* { background-color: blue; }");
// Now document will have blue background color as well.
```
* Stylesheets added to `adoptedStyleSheets` are part of the `DocumentOrShadowRoot`'s style sheets, and they are ordered after the`DocumentOrShadowRoot`'s `styleSheets`.
* This means when there are conflicting rules in the `adoptedStyleSheets` and `styleSheets` and the resolution will consider the order of stylesheets, they treat the sheets in `adoptedStyleSheets` as ordered later.


34 changes: 32 additions & 2 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Constructing Stylesheets {#constructing-stylesheets}
<pre class='idl'>
partial interface Document {
[NewObject] Promise&lt;CSSStyleSheet> createCSSStyleSheet(DOMString text, optional CSSStyleSheetInit options);
[NewObject] CSSStyleSheet createCSSStyleSheetSync(DOMString text, optional CSSStyleSheetInit options);
[NewObject] CSSStyleSheet createEmptyCSSStyleSheet(optional CSSStyleSheetInit options);
};

Expand Down Expand Up @@ -65,6 +66,34 @@ dictionary CSSStyleSheetInit {
8. Return <var>promise</var>.
</dd>

<dt><dfn method for=Document lt="createCSSStyleSheetSync(text)|createCSSStyleSheetSync(text, options)">createCSSStyleSheetSync(text, options)</dfn></dt>
<dd>
When called, execute these steps:

1. Construct a new {{CSSStyleSheet}} object <var>sheet</var>,
with location set to the {{Document}}'s <a spec=html>base URL</a>,
no parent CSS style sheet,
no owner node,
no owner CSS rule,
and a title set to the {{CSSStyleSheetInit/title}} attribute of <var>options</var>.
Set <var>sheet’s</var> origin-clean flag.
2. If the {{CSSStyleSheetInit/media}} attribute of <var>options</var> is a string,
<a>create a MediaList object</a> from the string
and assign it as <var>sheet’s</var> media.
Otherwise, assign a copy of the value of the attribute as <var>sheet’s</var> media.
3. If the {{CSSStyleSheetInit/alternate}} attribute of <var>options</var> is true,
set <var>sheet’s</var> alternate flag.
4. If the {{CSSStyleSheetInit/disabled}} attribute of <var>options</var> is true,
set <var>sheet’s</var> disabled flag.
5. <a spec=css-syntax-3>Parse a stylesheet</a> from {{text}}.
If it returned a list of rules,
assign the list as <var>sheet’s</var> CSS rules;
otherwise,
set <var>sheet’s</var> CSS rules to an empty list.
6. If <var>sheet</var> contains one or more <a spec=css-cascade-4>@import</a> rules, throw a {{SyntaxError}}.
7. Return <var>sheet</var>.
</dd>

<dt><dfn method for=Document lt="createEmptyCSSStyleSheet()|createEmptyCSSStyleSheet(options)">createEmptyCSSStyleSheet(options)</dfn></dt>
<dd>
Synchronously creates an empty CSSStyleSheet object and returns it.
Expand Down Expand Up @@ -93,8 +122,9 @@ dictionary CSSStyleSheetInit {
Using Constructed Stylesheets {#using-constructed-stylesheets}
=============================

A {{CSSStyleSheet}} can only be applied to the {{Document}} it is constructed on, or any {{ShadowRoot}} in the {{Document}} it is constructed on,
by adding a {{StyleSheetList}} containing the sheet to their {{adoptedStyleSheets}}. A stylesheet can be used in multiple {{DocumentOrShadowRoot}}s.
A {{CSSStyleSheet}} can only be applied to the {{Document}} it is constructed on (e.g. the document on which the factory function is called on),
and any {{ShadowRoot}} in the {{Document}} it is constructed on, by adding a {{StyleSheetList}} containing the sheet to their {{adoptedStyleSheets}}.
So, a stylesheet can be used in multiple {{DocumentOrShadowRoot}}s within the document it is constructed on.

Non-explicitly constructed stylesheets cannot be added to {{adoptedStyleSheets}}.
If {{adoptedStyleSheets}} got assigned a {{StyleSheetList}} that contains style sheets not made by {{createCSSStyleSheet(text)}} or {{createEmptyCSSStyleSheet}},
Expand Down
Loading

0 comments on commit f01227b

Please sign in to comment.