-
Notifications
You must be signed in to change notification settings - Fork 143
/
Copy pathOverview.bs
408 lines (305 loc) · 16.2 KB
/
Overview.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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
<pre class='metadata'>
Title: Worklets Level 1
Status: ED
Group: houdini
ED: https://drafts.css-houdini.org/worklets/
Previous Version: http://www.w3.org/TR/2016/WD-worklets-1-20160607/
Shortname: worklets
Level: 1
Abstract: This specification defines an API for running scripts in stages of the rendering pipeline independent of the main javascript execution environment.
Editor: Ian Kilpatrick, [email protected]
</pre>
<pre class="anchors">
urlPrefix: http://heycam.github.io/webidl/#dfn-; type: dfn;
text: inherit
urlPrefix: https://html.spec.whatwg.org/multipage/browsers.html; type: dfn;
text: effective script origin
url: #origin-2; text: origin
urlPrefix: https://html.spec.whatwg.org/multipage/workers.html; type: dfn;
text: web workers
urlPrefix: #dom-workerglobalscope-;
text: self
urlPrefix: https://html.spec.whatwg.org/multipage/webappapis.html; type: dfn;
text: api base url
text: api url character encoding
text: browsing context
text: code entry-point
text: creation url
text: document environment
text: entry settings object
text: environment settings object
text: event loop
text: fetch a module script tree
text: global object
text: https state
text: incumbent settings object
text: microtask queue
text: module script
text: realm execution context
text: responsible browsing context
text: responsible document
text: responsible event loop
text: run a module script
text: script execution environment
text: task queues
urlPrefix: https://html.spec.whatwg.org/multipage/infrastructure.html; type: dfn;
text: cors setting attribute
text: in parallel
text: javascript global environment
urlPrefix: #js-;
text: syntaxerror;
url: resolve-a-url; text: resolve;
urlPrefix: https://www.w3.org/2001/tag/doc/promises-guide; type: dfn;
text: a new promise
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn;
text: Construct
text: InitializeHostDefinedRealm
text: Invoke
text: strict mode code
</pre>
Introduction {#intro}
=====================
Motivations {#motivations}
--------------------------
<em>This section is not normative.</em>
Allowing extension points defined in the <a>document environment</a> is difficult, as rendering
engines would need to abandon previously held assumptions for what could happen in the middle of a
phase.
For example, during the layout phase the rendering engine assumes that no DOM will be modified.
Additionally defining extension points in the <a>document environment</a> would restrict rendering
engines to performing work in the same thread as the <a>document environment</a>. (Unless rendering
engines added complex, high-overhead infrastructure to allow thread-safe APIs in addition to thread
joining guarantees).
The worklet is designed to allow such extension points in rendering engines, while keeping
guarantees which rendering engines rely currently on.
Worklets are similar to <a>web workers</a> however they:
- Are thread-agnostic. That is, they are not defined to run on a particular thread. Rendering
engines may run them wherever they choose.
- Are able to have multiple duplicate instances of the global scope created for the purpose of
parallelism.
- Are not event API based. Instead classes are registered on the global scope, whose methods are to
be invoked by the user agent.
- Have a reduced API surface on the <a>javascript global environment</a> (global scope).
- Have a lifetime for the global scope which is defined by subsequent specifications or user
agents. They aren't tied to the lifetime of the document.
As worklets have a relatively high overhead, they should be used sparingly. Due to this worklets are
expected to be shared between separate scripts. This is similar to the <a>document environment</a>.
Code Idempotency {#code-idempotency}
------------------------------------
<em>This section is not normative.</em>
Multiple instances of {{WorkletGlobalScope}} can be created for each {{Worklet}} that they belong
to. User agents may choose to do this in order to parallelize work over multiple threads, or to move
work between threads as required.
Additionally different user agents may invoke a method on a class in a different order to other user
agents.
As a result of this, to prevent this compatibility risk between user agents, authors who register
classes on the global scope should make their code idempotent. That is, a method or set of methods
on a class should produce the same output given a particular input.
The following techniques should be used in order to encourage authors to write code in an idempotent
way:
- No reference to the global object, e.g. <a>self</a> on a {{DedicatedWorkerGlobalScope}}.
- Code is loaded as a <a>module script</a> which resulting in the code being executed in <a>strict
mode code</a> without a shared this. This prevents two different module scripts sharing
state be referencing shared objects on the global scope.
- User agents may choose to always have at least two {{WorkletGlobalScope}}s per {{Worklet}} and
randomly assign a method or set of methods on a class to a particular global scope.
- User agents may create and destroy {{WorkletGlobalScope}}s at any time.
Infrastructure {#infrastructure}
================================
The Global Scope {#the-global-scope}
------------------------------------
The {{WorkletGlobalScope}} object provides a <dfn>worklet global scope</dfn> which represents the
global execution context of a {{Worklet}}.
<pre class='idl'>
[Exposed=Worklet]
interface WorkletGlobalScope {
attribute Console console;
};
</pre>
A {{WorkletGlobalScope}} has an associated <a>environment settings object</a>.
Note:
The {{WorkletGlobalScope}} has a limited global scope when compared to a
{{DedicatedWorkerGlobalScope}}. It is expected that other specifications will extend
{{WorkletGlobalScope}} with <code class='lang-javascript'>registerAClass</code> methods which
will allow authors to register classes for the user agent create and invoke methods on.
### The event loop ### {#the-event-loop}
Each {{WorkletGlobalScope}} object has a distinct <a>event loop</a>. This <a>event loop</a> has no
associated <a>browsing context</a>, and only its <a>microtask queue</a> is used (all other <a>task
queues</a> are not used). The <a>event loop</a> is created by the <a>create a
WorkletGlobalScope</a> algorithm.
### Creating a WorkletGlobalScope ### {#creating-a-workletglobalscope}
When a user agent is to <dfn>create a WorkletGlobalScope</dfn>, for a given |worklet|, it
<em>must</em> run the following steps:
1. Let |workletGlobalScopeType| be the |worklet|'s <a>worklet global scope type</a>.
2. Call the JavaScript <a>InitializeHostDefinedRealm</a> abstract operation with the following
customizations:
- For the global object, create a new |workletGlobalScopeType| object. Let
|workletGlobalScope| be the created object.
- Let |realmExecutionContext| be the created JavaScript execution context.
- Do not obtain any source texts for scripts or modules.
3. Let |settingsObject| be the result of <a>set up a worklet environment settings object</a>
with |realmExecutionContext|.
4. Associate the |settingsObject| with |workletGlobalScope|.
5. For each |resolvedModuleURL| in the given |worklet|'s <a>worklet's resolved module URLs</a>,
run the following substeps:
1. Let |script| be the result of <a>fetch a module script tree</a> given
|resolvedModuleURL|, "anonymous" for the <a>CORS setting attribute</a>, and
|settingsObject|.
Note: Worklets follow <a>web workers</a> here in not allowing "use-credientials" for
fetching resources.
2. <a>Run a module script</a> given |script|.
### Script settings for worklets ### {#script-settings-for-worklets}
When a user agent is to <dfn>set up a worklet environment settings object</dfn>, given a
|executionContext|, it must run the following steps:
1. Let |inheritedResponsibleBrowsingContext| be the <a>responsible browsing context</a>
specified by the <a>incumbent settings object</a>.
2. Let |inheritedOrigin| be the <a>origin</a> specified by the <a>incumbent settings object</a>.
3. Let |inheritedAPIBaseURL| be the <a>API base URL</a> specified by the <a>incumbent settings object</a>.
4. Let |workletEventLoop| be a newly created <a>event loop</a>.
5. Let |workletGlobalScope| be |executionContext|'s <a>global object</a>.
6. Let |settingsObject| be a new <a>environment settings object</a> whose algorithms are
defined as follows:
: The <a>realm execution context</a>
:: Return |executionContext|.
: The <a>global object</a>
:: Return |workletGlobalScope|.
: The <a>responsible browsing context</a>
:: Return |inheritedResponsibleBrowsingContext|.
: The <a>responsible event loop</a>
:: Return |workletEventLoop|.
: The <a>responsible document</a>
:: Not applicable (the <a>responsible event loop</a> is not a <a>browsing context</a>
<a>event loop</a>).
: The <a>API URL character encoding</a>
:: Return UTF-8.
: The <a>API base URL</a>
:: Return |inheritedAPIBaseURL|.
: The <a>origin</a> and <a>effective script origin</a>
:: Return |inheritedOrigin|.
: The <a>creation URL</a>
:: Not applicable.
: The <a>HTTPS state</a>
:: Return |workletGlobalScope|'s <a>HTTPS state</a>.
7. Return |settingsObject|.
Issue: Merge this with https://html.spec.whatwg.org/multipage/workers.html#set-up-a-worker-environment-settings-object
Worklet {#worklet-section}
--------------------------
The {{Worklet}} object provides the capability to import module scripts into its associated
{{WorkletGlobalScope}}s. The user agent can then create classes registered on the
{{WorkletGlobalScope}}s and invoke their methods.
<pre class='idl'>
interface Worklet {
[NewObject] Promise<void> import(DOMString moduleURL);
};
</pre>
A {{Worklet}} has a <dfn export>worklet global scope type</dfn>. This is used for creating new
{{WorkletGlobalScope}} and the type must <a>inherit</a> from {{WorkletGlobalScope}}.
Note: As an example the <a>worklet global scope type</a> might be a {{PaintWorkletGlobalScope}}.
A {{Worklet}} has a list of the <dfn export>worklet's WorkletGlobalScopes</dfn>. Initially this list
is empty; it is populated when the user agent chooses to create its {{WorkletGlobalScope}}.
A {{Worklet}} has a list of the <dfn>worklet's resolved module URLs</dfn>. Initially this list is
empty; it is populated when module scripts resolved.
When the <dfn method for=Worklet>import(|moduleURL|)</dfn> method is called on a {{Worklet}} object,
the user agent <em>must</em> run the following steps:
1. Let |promise| be <a>a new promise</a>.
2. Run the following steps <a>in parallel</a>:
1. Let |resolvedModuleURL| be the result of <a>resolving</a> the |moduleURL| relative to the
<a>API base URL</a> specified by the <a>entry settings object</a> when the method was
invoked.
2. If this fails, reject |promise| with a <a>SyntaxError</a> exception and abort these
steps.
3. Add |resolvedModuleURL| to the list of <a>worklet's resolved module URLs</a>.
4. Ensure that there is at least one {{WorkletGlobalScope}} in the <a>worklet's
WorkletGlobalScopes</a>. If not <a>create a WorkletGlobalScope</a> given the current
{{Worklet}}.
5. For each {{WorkletGlobalScope}} in the <a>worklet's WorkletGlobalScopes</a>, run these
substeps:
1. Let |settings| be the {{WorkletGlobalScope}}'s associated <a>environment settings
object</a>.
2. Let |script| be the result of <a>fetch a module script tree</a> given
|resolvedModuleURL|, "anonymous" for the <a>CORS setting attribute</a>, and
|settings|.
Note: Worklets follow <a>web workers</a> here in not allowing "use-credientials" for
fetching resources.
3. <a>Run a module script</a> given |script|.
6. If <em>all</em> the steps above succeeded (in particular, if all of the scripts parsed
and loaded into the global scopes), resolve |promise|.
<br>Otherwise, reject |promise|.
Note: Specifically, if a script fails to parse or fails to load over the network, it should
reject the promise; if the script throws an error while first evaluating the promise
should resolve as a classes may have been registered correctly.
3. Return |promise|.
Issue(w3c/css-houdini-drafts#47): Need ability to load code into {{WorkletGlobalScope}} declaratively.
Lifetime of the Worklet {#lifetime-of-the-worklet}
--------------------------------------------------
The lifetime of a {{Worklet}} is tied to the object it belongs to, for example the {{Window}}.
The lifetime of a {{WorkletGlobalScope}} should be defined by subsequent specifications which
inherit from {{WorkletGlobalScope}}.
Subsequent specifications <em>may</em> define that a {{WorkletGlobalScope}} can be terminated at any
time particularly if there are no pending operations, or detects abnormal operation such as infinite
loops and callbacks exceeding imposed time limits.
Security Considerations {#security-considerations}
==================================================
Issue(w3c/css-houdini-drafts#92): Need to decide if to allow worklets for unsecure context, etc.
Examples {#examples}
====================
<em>This section is not normative.</em>
For these examples we'll use a fake worklet on window.
<pre class='idl'>
partial interface Window {
[SameObject] readonly attribute Worklet fakeWorklet1;
[SameObject] readonly attribute Worklet fakeWorklet2;
};
</pre>
<pre class='idl'>
callback Function = any (any... arguments);
[Global=(Worklet,FakeWorklet),Exposed=FakeWorklet]
interface FakeWorkletGlobalScope : WorkletGlobalScope {
void registerAnArbitaryClass(DOMString type, Function classConstructor);
};
</pre>
Each {{FakeWorkletGlobalScope}} has a map of the <dfn>registered class constructors map</dfn>.
When the <dfn method for=FakeWorkletGlobalScope>
registerAnArbitaryClass(|type|, |classConstructor|)</dfn> method is called, the user agent will add
the |classConstructor| of |type| to the map of <a>registered class constructors map</a>.
Loading scripts into a worklet. {#example-single}
-------------------------------------------------
<pre class='lang-javascript'>
window.fakeWorklet1.import('script1.js');
window.fakeWorklet1.import('script2.js');
// Assuming no other calls to fakeWorklet1 valid script loading orderings are:
// 1. 'script1.js', 'script2.js'
// 2. 'script2.js', 'script1.js'
</pre>
Loading scripts into multiple worklets. {#example-multiple}
-----------------------------------------------------------
<pre class='lang-javascript'>
Promise.all([
window.fakeWorklet1.import('script1.js'),
window.fakeWorklet2.import('script2.js')
]).then(function() {
// Both scripts now have loaded code, can do a task which relies on this.
});
</pre>
Create a registered class and invoke a method. {#example-class}
---------------------------------------------------------------
<pre class='lang-javascript'>
// Inside FakeWorkletGlobalScope
registerAnArbitaryClass('key', class FooClass {
process(arg) {
return !arg;
}
});
</pre>
As an example, if the user agent wants to invoke "process" on a new class instance, the user agent
could follow the following steps:
1. Let |workletGlobalScope| be a {{FakeWorkletGlobalScope}} from the list of <a>worklet's
WorkletGlobalScopes</a> from the fake {{Worklet}}.
The user agent <em>may</em> also <a>create a WorkletGlobalScope</a> given the fake
{{Worklet}} and use that.
2. Let |classCtor| be the result of performing a lookup in <a>registered class constructors
map</a> with "key" as the key.
3. Let |classInstance| be the result of <a>Construct</a>(|classCtor|).
4. Let |result| be the result of <a>Invoke</a>(O=|classInstance|, P="process",
Arguments=["true"]).
5. Return |result|.