<pre class='metadata'>
Title:  Worklets Level 1
Status: ED
Group: houdini
ED: https://drafts.css-houdini.org/worklets/
TR: http://www.w3.org/TR/worklets-1/
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, ikilpatrick@chromium.org, w3cid 73001
</pre>

<style>
/* Put nice boxes around each algorithm. */
[data-algorithm]:not(.heading) {
    padding: .5em;
    border: thin solid #ddd; border-radius: .5em;
    margin: .5em calc(-0.5em - 1px);
}
[data-algorithm]:not(.heading) > :first-child {
    margin-top: 0;
}
[data-algorithm]:not(.heading) > :last-child {
    margin-bottom: 0;
}
</style>

<pre class=link-defaults>
spec:fetch; type:dfn; for:/; text:fetch
spec:html; type:dfn; for:/; text:browsing context
spec:html; type:interface; for:/; text:Document
spec:html; type:dfn; for:environment settings object;
    text: global object
    text: https state
    text: referrer policy
spec:html; type:dfn; for:environment; text:id
spec:webidl; type:dfn; for:interface; text:inherit
</pre>

<pre class="anchors">
urlPrefix: https://fetch.spec.whatwg.org/; type: dfn;
    urlPrefix: #concept-;
        text: fetch
urlPrefix: https://html.spec.whatwg.org/multipage/browsers.html; type: dfn;
    text: effective script origin
    url: #origin-2; text: origin
urlPrefix: http://heycam.github.io/webidl/; type: dfn;
    urlPrefix: #es-;
        text: invoking callback functions
urlPrefix: https://html.spec.whatwg.org/multipage/workers.html; type: dfn;
    urlPrefix: #dom-workerglobalscope-;
        text: self
urlPrefix: https://html.spec.whatwg.org/multipage/webappapis.html; type: dfn;
    text: document environment
    text: event loop processing model
    text: microtask queue
    text: task queues
    text: discarded; url: a-browsing-context-is-discarded
urlPrefix: https://w3c.github.io/webappsec-csp/#; type: dfn;
    text: initialize a global object's CSP list; url: initialize-global-object-csp
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn;
    text: Construct
    text: InitializeHostDefinedRealm
    text: Invoke
    text: strict mode code
</pre>
<pre class="link-defaults">
spec:url; type:dfn; text:url; for:url
spec:webidl; type:dfn; for:interface; text:inherit
</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 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}
------------------------------------

Some specifications which use worklets ([[css-paint-api-1]]), allow user agents to parallelize work
over multiple threads, or to move work between threads as required.

In these specifications user agents might invoke methods 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 using these APIs, 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 are 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 by referencing shared objects on the global scope.

 - These specifications must require user agents 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. These specifications can provide an opt-out under memory constraints.

 - User agents can create and destroy {{WorkletGlobalScope}}s at any time for these specifications.

Speculative Evaluation {#speculative-evaluation}
------------------------------------------------

Some specifications which use worklets ([[css-paint-api-1]]) may invoke methods on a class based on
the state of the user agent. To increase the concurrency between threads, a user agent may invoke a
method speculatively, based on potential future states.

In these specifications user agents might invoke methods on a class at any time, and with any
arguments, not just ones corresponding to the current state of the user agent. The results of such
speculative evaluations are not displayed immediately, but may be cached for use if the user agent
state matches the speculated state.  This may increase the concurrency between the user agent and
worklet threads.

As a result of this, to prevent this compatibility risk between user agents, authors who register
classes on the global scope using these APIs, should make their code stateless. That is, the only
effect of invoking a method should be its result, not any side-effects such as updating mutable
state.

The same techniques which encourage code idempotence also encourage authors to write stateless code.

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 {
};
</pre>

Each {{WorkletGlobalScope}} has an assocated <dfn export for=WorkletGlobalScope>owner
document</dfn>.
It is initially null and set inside the <a>create a WorkletGlobalScope</a> algorithm.

Whenever a {{Document}} object is <a>discarded</a>, each {{WorkletGlobalScope}} whose <a>owner
document</a> is that {{Document}} object, should clear its <a>owner document</a>.

Each {{WorkletGlobalScope}} has an associated <a>environment settings object</a>.

Each {{WorkletGlobalScope}} has an associated <dfn for=WorkletGlobalScope>module map</dfn>. It is a
<a>module map</a>, initially empty.

Each {{WorkletGlobalScope}} has a <dfn>worklet global scope execution environment</dfn>. This
execution environment may be parallel (i.e. it may be on a separate thread, process, or other
equivalent construct), or it may live on the same thread or process as the {{Worklet}} object it
belongs to. Which thread or process it lives on is decided by the user agent.

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.

When asked to <a>report an exception</a>, do nothing instead, or optionally report the exception to
a developer console.

Issue(whatwg/html#2611): HTML's <a>report an exception</a> needs updating to work with
    non-EventTarget global objects.

### 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>. The <a>event loop</a> is created by the <a>create a
WorkletGlobalScope</a> algorithm.

The <a>event loop</a> is run on the <a>worklet global scope execution environment</a> defined above.

It is expected that only tasks associated {{Worklet/addModule()}}, the user agent invoking author
defined callbacks, and <a>microtasks</a> will use this <a>event loop</a>.

Note:
    Even through the <a>event loop processing model</a> specifies that it loops continually,
    practically implementations aren't expected to do this. The <a>microtask queue</a> is emptied
    while <a>invoking callback functions</a> provided by the author.

### Creating a WorkletGlobalScope ### {#creating-a-workletglobalscope}

<div algorithm>
When a user agent is to <dfn export>create a WorkletGlobalScope</dfn>, given |workletGlobalScopeType|,
|moduleResponsesMap|, and |outsideSettings|, it <em>must</em> run the following steps:

    1. Create the <a>worklet global scope execution environment</a> and run the rest of these steps
        in that context.

    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.

    3. Let |insideSettings| be the result of <a>set up a worklet environment settings object</a>
        given |realmExecutionContext|, and |outsideSettings|.

    4. Associate the |insideSettings| with |workletGlobalScope|.

    5. Set |workletGlobalScope|'s <a>owner document</a> to |outsideSettings|'s <a>responsible
        document</a>.

    6. Invoke the <a>initialize a global object's CSP list</a> algorithm given |workletGlobalScope|.

    7. For each |entry| in the given |moduleResponsesMap| (in insertion order), run the following
        substeps:

        1. Let |moduleURLRecord| be |entry|'s key.

        2. Let |script| be the result of <a>fetch a worklet script</a> given |moduleURLRecord|,
            |moduleResponsesMap|, |outsideSettings|, and |insideSettings| when
            it asynchronously completes.

        3. <a>Run a module script</a> given |script|.

        Note: <a>Fetch a worklet script</a> won't actually perform a network request as it will hit
            the worklet's <a>module responses map</a>. It also won't have a parsing error as at this
            point it should have successfully been parsed by another worklet global scope. I.e.
            |script| should never be null here.

    6. Run the <a>responsible event loop</a> specified by |insideSettings|.
</div>

### Script settings for worklets ### {#script-settings-for-worklets}

<div algorithm>
When a user agent is to <dfn export>set up a worklet environment settings object</dfn>, given a
|executionContext|, and |outsideSettings|, it must run the following steps:
    1. Let |inheritedResponsibleBrowsingContext| be |outsideSettings|'s <a>responsible browsing
        context</a>.

    2. Let |inheritedAPIBaseURL| be |outsideSettings|'s <a>API base URL</a>.

    3. Let |origin| be a unique <a>opaque origin</a>.

    4. Let |inheritedHTTPSState| be |outsideSettings|'s <a>HTTPS state</a>.

    5. Let |inheritedReferrerPolicy| be |outsideSettings|'s <a>referrer policy</a>.

    6. Let |workletEventLoop| be a newly created <a>event loop</a>.

    7. Let |realm| be the value of |executionContext|'s Realm component.

    8. Let |workletGlobalScope| be |realm|'s <a>global object</a>.

    9. 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>module map</a>.
        :: Return |workletGlobalScope|'s <a for=WorkletGlobalScope>module map</a>.

        : 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 <a>UTF-8</a>.

        : The <a>API base URL</a>
        :: Return |inheritedAPIBaseURL|.

        : The <a for="environment settings object">origin</a>
        :: Return |origin|.

        : The <a>HTTPS state</a>
        :: Return |inheritedHTTPSState|.

        : The <a>referrer policy</a>
        :: Return |inheritedReferrerPolicy|.

    10. Set |settingsObject|'s <a>id</a> to a new unique opaque string, |settingsObject|'s
        <a>creation URL</a> to |inheritedAPIBaseURL|, |settingsObject|'s <a>target browsing
        context</a> to null, and |settingsObject|'s <a>active service worker</a> to null.

    11. Set |realm|'s \[[HostDefined]] field to |settingsObject|.

    12. Return |settingsObject|.
</div>

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 add 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'>
[Exposed=Window]
interface Worklet {
    [NewObject] Promise&lt;void> addModule(USVString moduleURL, optional WorkletOptions options);
};

dictionary WorkletOptions {
    RequestCredentials credentials = "same-origin";
};
</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 <dfn>module responses map</dfn>. This is a ordered map of module URLs to values
that are a <a>fetch</a> responses. The map's entries are ordered based on their insertion order.
Access to this map should be thread-safe.

The <a>module responses map</a> exists to ensure that {{WorkletGlobalScope}}s created at different
times contain the same set of script source text and have the same behaviour. The creation of
additional {{WorkletGlobalScope}}s should be transparent to the author.

<div class='note'>
    Practically user agents aren't expected to implement the following algorithm using a
    thread-safe map. Instead when {{Worklet/addModule()}} is called user agents can fetch the module
    graph on the main thread, and send the fetched sources (the data contained in the <a>module
    responses map</a>) to each thread which has a {{WorkletGlobalScope}}.

    If the user agent wishes to create a new {{WorkletGlobalScope}} it can simply sent the list of
    all fetched sources from the main thread to the thread which owns the {{WorkletGlobalScope}}.
</div>

A <dfn>pending tasks struct</dfn> is a <a>struct</a> consisting of:
    - A <dfn for="pending tasks struct">counter</dfn>.
This is used by the algorithms below.

<div algorithm>
When the <dfn method for=Worklet>addModule(|moduleURL|, |options|)</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. Let |worklet| be this {{Worklet}}.

    3. Let |outsideSettings| be the <a>relevant settings object</a> of <b>this</b>.

    4. Let |moduleURLRecord| be the result of <a>parsing</a> the |moduleURL| argument relative to
        |outsideSettings|.

    5. If |moduleURLRecord| is failure, then reject promise with a "{{SyntaxError}}"
        {{DOMException}} and return |promise|.

    6. Return |promise|, and then continue running this algorithm <a>in parallel</a>.

    7. Let |credentialOptions| be the {{WorkletOptions/credentials}} member of |options|.

    8. Let |moduleResponsesMap| be |worklet|'s <a>module responses map</a>.

    9. Let |workletGlobalScopeType| be |worklet|'s <a>worklet global scope type</a>.

    10. If the <a>worklet's WorkletGlobalScopes</a> is empty, run the following steps:

        1. <a>Create a WorkletGlobalScope</a> given |workletGlobalScopeType|, |moduleResponsesMap|,
            and |outsideSettings|.

        2. Add the {{WorkletGlobalScope}} to <a>worklet's WorkletGlobalScopes</a>.

        Depending on the type of worklet the user agent may create additional
        {{WorkletGlobalScope}}s at this time.

        Note: Specifically the [[css-paint-api-1]] allows for multiple global scopes, while the
            [[webaudio]] API does not.

        Wait for this step to complete before continuing.

    11. Let |pendingTaskStruct| be a new <a>pending tasks struct</a> with <a
        for="pending tasks struct">counter</a> initialized to the length of <a>worklet's
        WorkletGlobalScopes</a>.

    12. For each |workletGlobalScope| in the <a>worklet's WorkletGlobalScopes</a>, <a>queue a
        task</a> on the |workletGlobalScope| to <a>fetch and invoke a worklet script</a> given
        |workletGlobalScope|, |moduleURLRecord|, |moduleResponsesMap|, |credentialOptions|,
        |outsideSettings|, |pendingTaskStruct|, and |promise|.

    Note: The rejecting and resolving of the |promise| occurs within the <a>fetch and invoke a
        worklet script</a> algorithm.
</div>

<div algorithm>
When the user agent is to <dfn>fetch and invoke a worklet script</dfn> given |workletGlobalScope|,
|moduleURLRecord|, |moduleResponsesMap|, |credentialOptions|, |outsideSettings|,
|pendingTaskStruct|, and |promise|, the user agent <em>must</em> run the following steps:

    Note: This algorithm is to be run within the <a>worklet global scope execution environment</a>.

    1. Let |insideSettings| be the |workletGlobalScope|'s associated <a>environment settings
        object</a>.

    2. Let |script| by the result of <a>fetch a worklet script</a> given |moduleURLRecord|,
        |moduleResponsesMap|, |credentialOptions|, |outsideSettings|, and |insideSettings| when it
        asynchronously completes.

    3. If |script| is null, then <a>queue a task</a> on |outsideSettings|'s <a>responsible event
        loop</a> to run these steps:

        1. If |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> is not <b>-1</b>, then
            run these steps:

            1. Set |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> to <b>-1</b>.

            2. Reject |promise| with an "{{AbortError}}" {{DOMException}}.

    4. If |script|'s <a>error to rethrow</a> is not null, then <a>queue a task</a> on
        |outsideSettings|'s <a>responsible event loop</a> given |script|'s <a>error to rethrow</a>
        to run these steps:

        1. If |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> is not <b>-1</b>, then
            run these steps:

            1. Set |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> to <b>-1</b>.

            2. Reject |promise| with <a>error to rethrow</a>.

    5. <a>Run a module script</a> given |script|.

    6. <a>Queue a task</a> on |outsideSettings|'s <a>responsible event loop</a> to run these steps:

        1. If |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> is not <b>-1</b>, then
            run these steps:

            1. Decrement |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> by
                <b>1</b>.

            2. If |pendingTaskStruct|'s <a for="pending tasks struct">counter</a> is <b>0</b>, then
                resolve |promise|.
</div>

<div algorithm>
When the user agent is to <dfn>fetch a worklet script</dfn> given |moduleURLRecord|,
|moduleResponsesMap|, |credentialOptions|, |outsideSettings|, and |insideSettings|, the user agent
<em>must</em> run the following steps:

Note: This algorithm is to be run within the <a>worklet global scope execution environment</a>.

1. <a>Fetch a module worker script graph</a> given |moduleURLRecord|, |outsideSettings|, "script",
    |credentialOptions|, and |insideSettings|.

    To <a>perform the fetch</a> given |request|, perform the following steps:

        1. Let |cache| be the |moduleResponsesMap|.

        2. Let |url| be |request|'s <a for=request>url</a>.

        3. If |cache| contains an entry with key |url| whose value is "fetching", wait until that
            entry's value changes, then proceed to the next step.

        4. If |cache| contains an entry with key |url|, asynchronously complete this algorithm with
            that entry's value, and abort these steps.

        5. Create an entry in |cache| with key |url| and value "fetching".

        6. <a>Fetch</a> |request|.

        7. Let |response| be the result of <a>fetch</a> when it asynchronously completes.

        8. Set the value of the entry in |cache| whose key is |url| to |response|, and
            asynchronously complete this algorithm with |response|.

2. Return the result of <a>fetch a module worker script graph</a> when it asynchronously completes.

Note: Specifically, if a script fails to parse or fails to load over the network, it will reject the
    promise. If the script throws an error while first evaluating the promise it will resolve as a
    classes may have been registered correctly.

<div class=example>
    When an author adds code into a {{Worklet}} the code may run against multiple
    {{WorkletGlobalScope}}s, for example:
    <pre class='lang-javascript'>
        // script.js
        console.log('Hello from a WorkletGlobalScope!');
    </pre>

    <pre class='lang-javascript'>
        // main.js
        await CSS.paintWorklet.addModule('script.js');
    </pre>

    Behind the scenes the user agent may load the <code class='lang-javascript'>script.js</code>
    into 4 global scopes, in which case the debugging tools for the user agent would print:
    <pre class='lang-javascript'>
        [paintWorklet#1] Hello from a WorkletGlobalScope!
        [paintWorklet#4] Hello from a WorkletGlobalScope!
        [paintWorklet#2] Hello from a WorkletGlobalScope!
        [paintWorklet#3] Hello from a WorkletGlobalScope!
    </pre>

    If the user agent decided to kill and restart a {{WorkletGlobalScope}} number 3 in this example,
    it would print <code class='lang-javascript'>[paintWorklet#3] Hello from a
    WorkletGlobalScope!</code> again in the debugging tools when this occurs.
</div>
</div>

Issue(w3c/css-houdini-drafts#47): Need ability to load code into a {{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.

<div class=example>
    <pre class='idl'>
    partial interface Window {
      [SameObject] readonly attribute Worklet fakeWorklet1;
      [SameObject] readonly attribute Worklet fakeWorklet2;
    };
    </pre>

    <pre class='idl'>
    [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>.
</div>

Loading scripts into a worklet. {#example-single}
-------------------------------------------------

<pre class='lang-javascript'>
window.fakeWorklet1.addModule('script1.js');
window.fakeWorklet1.addModule('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.addModule('script1.js'),
    window.fakeWorklet2.addModule('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|.

