-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathindex.bs
221 lines (183 loc) · 11.1 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<pre class=metadata>
Title: Constructable Stylesheet Objects
Shortname: construct-stylesheets
Level: 1
Status: DREAM
ED: https://wicg.github.io/construct-stylesheets/
Editor: Tab Atkins Jr., Google, http://xanthir.com/contact/
Editor: Eric Willigers, Google, ericwilligers@google.com
Editor: Rakina Zata Amni, Google, rakina@google.com
Repository: https://github.com/WICG/construct-stylesheets/
Abstract: This draft defines additions to CSSOM to make {{/CSSStyleSheet}} objects directly constructable, along with a way to use them in {{DocumentOrShadowRoot}}s.
Ignored Terms: create a medialist object, add a css style sheet, document css style sheets
Markup Shorthands: markdown yes
</pre>
<pre class='link-defaults'>
spec:dom; type:interface; text:Document
spec:cssom-1; type:interface; for:/; text:CSSStyleSheet
spec:cssom-1; type:method; for:CSSStyleSheet; text:insertRule(rule)
spec:cssom-1; type:method; for:CSSStyleSheet; text:deleteRule(rule)
spec:cssom-1; type:dfn; for:CSSStyleSheet; text:parent css style sheet
spec:cssom-1; type:dfn; for:CSSStyleSheet; text:owner node
spec:fetch; type:dfn; for:fetch; text:terminated
</pre>
Motivation {#motivation}
============================
Most web components 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.
As a web page may contain tens of thousands of web components, 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. However, the duplications are actually not needed as the web components will most likely use the same styling, perhaps one for each component library.
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.
Proposed Solution {#proposed-solution}
============================
We are proposing to provide an API for creating stylesheet objects from script, without needing style elements, and also a way to reuse them in multiple places. Script can optionally add, remove, or replace rules from a stylesheet object. Each stylesheet object can be added directly to any number of shadow roots (and/or the top level document).
<pre class='lang-js'>
const myElementSheet = new CSSStyleSheet();
class MyElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.adoptedStyleSheets = [myElementSheet];
}
connectedCallback() {
// Only actually parse the stylesheet when the first instance is connected.
if (myElementSheet.cssRules.length == 0) {
myElementSheet.replaceSync(styleText);
}
}
}
</pre>
Constructing Stylesheets {#constructing-stylesheets}
=================================
<pre class='idl'>
partial interface CSSStyleSheet {
constructor(optional CSSStyleSheetInit options = {});
Promise<CSSStyleSheet> replace(USVString text);
undefined replaceSync(USVString text);
};
dictionary CSSStyleSheetInit {
(MediaList or DOMString) media = "";
DOMString title = "";
boolean alternate = false;
boolean disabled = false;
};
</pre>
<dl>
<dt><dfn constructor for=CSSStyleSheet>CSSStyleSheet(|options|)</dfn></dt>
<dd>
When called, execute these steps:
1. Construct a new {{/CSSStyleSheet}} object |sheet| with the following properties:
* <a spec=cssom>location</a> set to the <a spec=html>base URL</a> of the [=associated Document=] for the [=current global object=]
* No <a spec=cssom>parent CSS style sheet</a>.
* No <a spec=cssom>owner node</a>.
* No <a spec=cssom>owner CSS rule</a>.
* <a spec=cssom>title</a> set to the {{CSSStyleSheetInit/title}} attribute of |options|.
* Set <a spec=cssom>alternate flag</a> if the {{CSSStyleSheetInit/alternate}} attribute of |options| is true, otherwise unset the <a spec=cssom>alternate flag</a>.
* Set <a spec=cssom>origin-clean flag</a>.
* Set [=constructed flag=].
* [=Constructor document=] set to the [=associated Document=] for the [=current global object=].
2. If the {{CSSStyleSheetInit/media}} attribute of |options| is a string,
<a>create a MediaList object</a> from the string
and assign it as |sheet|'s <a spec=cssom>media</a>.
Otherwise, [=serialize a media query list=] from the attribute and then [=create a MediaList object=]
from the resulting string and set it as |sheet|'s <a spec=cssom>media</a>.
3. If the {{CSSStyleSheetInit/disabled}} attribute of |options| is true,
set |sheet|'s <a spec=cssom>disabled flag</a>.
4. Return |sheet|.
</dd>
</dl>
<a interface>CSSStyleSheet</a> instances have the following associated states:
<dl>
<dt><dfn for=CSSStyleSheet lt="constructed flag">constructed flag</dfn></dt>
<dd>
Specified when created. Either set or unset. Unset by default.
Signifies whether this stylesheet was created by invoking the IDL-defined constructor.
</dd>
<dt><dfn for=CSSStyleSheet lt="disallow modification flag">disallow modification flag</dfn></dt>
<dd>
Either set or unset. Unset by default. If set, modification of the stylesheet's rules is not allowed.
</dd>
<dt><dfn for=CSSStyleSheet lt="constructor document">constructor document</dfn></dt>
<dd>
Specified when created. The {{Document}} a constructed stylesheet is associated with. Null by default.
Only non-null for stylesheets that have [=constructed flag=] set.
</dd>
</dl>
Modifying Constructed Stylesheets {#modifying-constructed-stylesheets}
=============================
After construction, constructed stylesheets can be modified using rule modification methods like {{CSSStyleSheet/insertRule()}} or {{CSSStyleSheet/deleteRule()}}, or {{replace(text)}} and {{replaceSync(text)}}. The behavior of those methods are affected by the [=disallow modification flag=] and [=constructed flag=] as described below.
<p class="note">
Note that we're explicitly monkeypatching CSSOM. We are working on [merging this spec into CSSOM](https://github.com/w3c/csswg-drafts/issues/3433) soon.
</p>
<dl>
<dt><dfn method for=CSSStyleSheet>insertRule(|rule|, |index|)</dfn></dt>
<dd>
1. Let |sheet| be this {{/CSSStyleSheet}} object.
2. If |sheet|'s [=disallow modification flag=] is set, throw a "{{NotAllowedError}}" {{DOMException}}.
3. [=Parse a rule=] from |rule|. If the result is an <a>@import</a> rule and |sheet|'s [=constructed flag=] is set, or |sheet|'s [=parent CSS style sheet=]'s [=constructed flag=] is set, or that sheet's [=parent CSS style sheet=]'s [=constructed flag=] is set (and so on), throw a "{{NotAllowedError}}" {{DOMException}}.
4. (The rest of the algorithm remains as in CSSOM)
</dd>
<dt><dfn method for=CSSStyleSheet>deleteRule(|index|)</dfn></dt>
<dd>
1. Let |sheet| be this {{/CSSStyleSheet}} object.
2. If |sheet|'s [=disallow modification flag=] is set, throw a "{{NotAllowedError}}" {{DOMException}}.
3. (The rest of the algorithm remains as in CSSOM)
</dd>
<dt><dfn method for=CSSStyleSheet>replace(|text|)</dfn></dt>
<dd>
1. Let |sheet| be this {{/CSSStyleSheet}} object.
2. Let |promise| be a promise.
3. If |sheet|'s [=constructed flag=] is not set, or |sheet|'s [=disallow modification flag=] is set, reject |promise| with a "{{NotAllowedError}}" {{DOMException}} and return |promise|.
4. Set |sheet|'s [=disallow modification flag=].
5. [=In parallel=], do these steps:
1. Let |rules| be the result of running [=parse a list of rules=] from |text|. If |rules| is not a list of rules (i.e. an error occurred during parsing), set |rules| to an empty list.
2. Wait for loading of <a>@import</a> rules in |rules| and any nested <a>@import</a>s from those rules (and so on).
* If any of them failed to load, [=terminate=] fetching of the remaining <a>@import</a> rules, and [=queue a task=] on the [=networking task source=] to perform the following steps:
1. Unset |sheet|'s [=disallow modification flag=].
2. Reject |promise| with a "{{NetworkError}}" {{DOMException}}.
* Otherwise, once all of them have finished loading, [=queue a task=] on the [=networking task source=] to perform the following steps:
1. Set |sheet|'s [=CSS rules=] to |rules|.
2. Unset |sheet|'s [=disallow modification flag=].
3. Resolve |promise| with |sheet|.
<p class="note">
Note: Loading of <a>@import</a> rules should follow the rules used for fetching style sheets for <a>@import</a> rules of stylesheets from <link> elements, in regard to what counts as success, CSP, and Content-Type header checking.
</p>
<p class="note">
Note: We will use the [=fetch group=] of |sheet|'s [=constructor document=]'s [=relevant settings object=] for <a>@import</a> rules and other (fonts, etc) loads.
</p>
<p class="note">
Note: The rules regarding loading mentioned above are currently not specified rigorously anywhere.
</p>
6. Return |promise|.
</dd>
<dt><dfn method for=CSSStyleSheet>replaceSync(|text|)</dfn></dt>
<dd>
When called, execute these steps:
1. Let |sheet| be this {{/CSSStyleSheet}} object.
2. If |sheet|'s [=constructed flag=] is not set, or |sheet|'s [=disallow modification flag=] is set, throw a "{{NotAllowedError}}" {{DOMException}}.
3. Let |rules| be the result of running [=parse a list of rules=] from |text|. If |rules| is not a list of rules (i.e. an error occurred during parsing), set |rules| to an empty list.
4. If |rules| contains one or more <a>@import</a> rules, throw a "{{NotAllowedError}}" {{DOMException}}.
5. Set |sheet|'s [=CSS rules=] to |rules|.
</dd>
</dl>
Using Constructed Stylesheets {#using-constructed-stylesheets}
=============================
<pre class='idl'>
partial interface mixin DocumentOrShadowRoot {
attribute FrozenArray<CSSStyleSheet> adoptedStyleSheets;
};
</pre>
<dl>
<dt><dfn attribute for=DocumentOrShadowRoot lt="adoptedStyleSheets">adoptedStyleSheets</dfn></dt>
<dd>
On getting, {{adoptedStyleSheets}} returns this {{DocumentOrShadowRoot}}'s [=adopted stylesheets=].
On setting, {{adoptedStyleSheets}} performs the following steps:
1. Let |adopted| be the result of converting the given value to a FrozenArray<CSSStyleSheet>
2. If any entry of |adopted| has its [=constructed flag=] not set, or its [=constructor document=] is not equal to this {{DocumentOrShadowRoot}}'s [=node document=], throw a "{{NotAllowedError}}" {{DOMException}}.
3. Set this {{DocumentOrShadowRoot}}'s [=adopted stylesheets=] to |adopted|.
</dd>
</dl>
Every {{DocumentOrShadowRoot}} has <dfn>adopted stylesheets</dfn>.
The user agent must include all style sheets in the {{DocumentOrShadowRoot}}'s
[=adopted stylesheets=] inside its <a>document or shadow root CSS style sheets</a>.
These [=adopted stylesheets=] are ordered after all the other style sheets (i.e. those derived from {{DocumentOrShadowRoot/styleSheets}}).
This specification defines the following [=adopting steps=] for {{ShadowRoot}} nodes, given |node| and <var ignore>oldDocument</var>:
1. Set |node|'s {{adoptedStyleSheets}} to the empty list.