Skip to content

TrustedTypes: Worker()/SharedWorker() constructors#42073

Merged
wbamberg merged 5 commits intomdn:mainfrom
hamishwillee:tt_worker_constructor
Jan 21, 2026
Merged

TrustedTypes: Worker()/SharedWorker() constructors#42073
wbamberg merged 5 commits intomdn:mainfrom
hamishwillee:tt_worker_constructor

Conversation

@hamishwillee
Copy link
Collaborator

@hamishwillee hamishwillee commented Nov 24, 2025

The Worker() and SharedWorker() constructors takes a url parameter that that can be a TrustedScriptURL. This attempts to follow the pattern of our other trusted types docs and add the header warning and security considerations section.

SharedWorker appears to be the same as Worker but of course constructs a different object. To reduce duplication I have mostly linked from SharedWorker() rather than copy-pasting.

Related project: #41507

@github-actions github-actions bot added Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed labels Nov 24, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 24, 2025

Preview URLs

Flaws (2)

URL: /en-US/docs/Web/API/SharedWorker/SharedWorker
Title: SharedWorker: SharedWorker() constructor
Flaw count: 1

  • broken_links:
    • Link /en-US/docs/Web/Security/Same-origin_policy is a redirect

URL: /en-US/docs/Web/API/Worker/Worker
Title: Worker: Worker() constructor
Flaw count: 1

  • broken_links:
    • Link /en-US/docs/Web/Security/Same-origin_policy is a redirect
External URLs (3)

URL: /en-US/docs/Web/API/Worker/Worker
Title: Worker: Worker() constructor

(comment last updated: 2026-01-21 20:20:57)

- : Thrown if `url` cannot be parsed.
- `TypeError`
- : Thrown if the `url` parameter is set with a string when [Trusted Types](/en-US/docs/Web/API/Trusted_Types_API) are [enforced by a CSP](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) and no default policy is defined.
<!-- This is also thrown if the fetched URL cannot be successfully parsed as its indicated type. -->
Copy link
Collaborator Author

@hamishwillee hamishwillee Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was true in other case I copied the error from. Not sure yet if true here.

@github-actions
Copy link
Contributor

This pull request has merge conflicts that must be resolved before it can be merged.

@github-actions github-actions bot removed the merge conflicts 🚧 [PR only] label Dec 16, 2025
@hamishwillee hamishwillee marked this pull request as ready for review December 30, 2025 05:39
@hamishwillee hamishwillee requested a review from a team as a code owner December 30, 2025 05:39
@sideshowbarker sideshowbarker removed the request for review from a team December 31, 2025 03:21
@hamishwillee hamishwillee force-pushed the tt_worker_constructor branch from f4ca572 to 5fa24e4 Compare January 5, 2026 03:53

Classic workers are fetched in `no-cors` mode, executed as scripts, and import other scripts using {{domxref("WorkerGlobalScope.importScripts()")}} (not `import` statements).
Because they are fetched in `no-cors` mode, cross-origin requests are not blocked by CORS, and the response does not have to include the `Access-Control-Allow-Origin` header.
Unlike for modules, the document [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) is not affected by the `worker-src` directive, but it is still affected by sources listed in the `script-src` directive.
Copy link
Collaborator Author

@hamishwillee hamishwillee Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My testing shows that

  • module workers respect worker-src. So if you set a CSP: worker-src 'self' you can load a module (which is always same origin), then it can import further same origin modules, but NOT cross-origin modules.
  • classic/script* workers do not respect worker-src. So if you set a CSP: worker-src 'self', a wokrer script can import a cross origin script using importScript .
  • classic/script* workers do respect script-src, So if you set a CSP: script-src 'self' this will block a worker script loading a cross origin script.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like you're saying that worker-src controls the scripts that a worker can load? Not the source of the worker itself? In that case isn't the example here wrong: https://w3c.github.io/webappsec-csp/#directive-worker-src ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg I have updated my note above to state more clearly what testing shows.

But to summarize, I'm saying that

  1. worker-src affects the scripts that can be imported into a module worker, but not the scripts that can be imported into a classic worker.
  2. worker-src seems to be pointless, since if I specify Content-Security-Policy: worker-src https://example.com/ neither a module or classic script at that address will load. ONLY same-origin modules ever load.

Clearly the intent is that the worker-src allows control over where module scripts can be loaded from, and "perhaps" also where they may load. But that's not what my testing is showing. Of course my testing might be wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hoping to get some clarity from Jake in https://github.com/mdn/content/pull/42073/files#r2666249206

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in me? Which part? I think GitHub is being unhelpful and hiding something

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hamish is out for the next two weeks but I promised to try to get this PR merged if I could :).

The question we have is, when a page loads a worker script, does the worker have to be same origin with the loading document? The page for the Worker() constructor says:

This script must obey the same-origin policy.

...which seems to me a curious thing to say (I mean, everything has to obey the SOP, it's a browser policy, so what is the substantive force of this statement?). We weren't sure whether this means "the script must be same origin with the loading document". Hamish's tests indicate that it does have to be same origin.

But that raises further questions, such as, what is the worker-src CSP directive for, then? The spec for worker-src even has an example of how you would use the directive to block workers from a different source, and why would you need that if the worker needs to be same origin?

So we thought we could ask you since you probably know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the root worker script needs to be same origin. However, scripts from other origins can be loaded via ES imports and importScripts. I guess that's what worker-src is for

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, thank you! Then it looks like Hamish's tests are all right.

I guess that's what worker-src is for

That is still odd, because it's not what the example from the spec is showing: https://w3c.github.io/webappsec-csp/#example-fd6c5849. But I'm going to let it go now :).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jakearchibald and @wbamberg . It is a relief to know that all the testing was not just "wrong". I'm OK to let this all go for now - as you say it is a bit odd but it is what I saw.

@hamishwillee hamishwillee changed the title Worker() constructor - update for TT Worker()/SharedWorker() constructors - update for TT Jan 5, 2026
@hamishwillee hamishwillee changed the title Worker()/SharedWorker() constructors - update for TT TrustedTypes: Worker()/SharedWorker() constructors Jan 5, 2026
Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't finished but I got so many questions about SOP that I thought I should give you them.

- : A string specifying the type of worker to create. The value can be `classic` or `module`. If not specified, the default used is `classic`.
- : An object containing option properties that can be set when creating the object instance.
Available properties are as follows:
- `credentials`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this has the same meaning (and constraints?) as https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#credentials, where it is all described more fully (including saying what credentials are). Maybe link there and say this?

Copy link
Collaborator Author

@hamishwillee hamishwillee Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done in 1837809

This controls whether a module worker sends credentials when it imports a cross-origin module.

This is ignored for classic modules. They send credentials for same-origin imports. They generally won't send them for cross-origin imports - it isn't under the control of the interface. They might if the remote server allows it.

Comment on lines +95 to +101
All same-origin workers can load same-origin scripts by default.
However because module scripts are fetched using CORS, cross-origin scripts must be served with the {{httpheader("Access-Control-Allow-Origin")}} header.
In addition, the if the document has a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP), it must allow the origins of imported worker scripts in `worker-src` (with fallback to `script-src` and `default-src` directives).

Classic workers are fetched in `no-cors` mode, executed as scripts, and import other scripts using {{domxref("WorkerGlobalScope.importScripts()")}} (not `import` statements).
Because they are fetched in `no-cors` mode, cross-origin requests are not blocked by CORS, and the response does not have to include the `Access-Control-Allow-Origin` header.
Unlike for modules, the document [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) is not affected by the `worker-src` directive, but it is still affected by sources listed in the `script-src` directive.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right to understand that these 2 paras are exclusively about scripts that the worker script itself loads? i.e. they're not about loading the worker script itself (since that's always same-origin)?

If so then I think you ought to have a heading like "Loading scripts from a worker" or something, to make this very clear.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

In addition, the if the document has a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP), it must allow the origins of imported worker scripts in `worker-src` (with fallback to `script-src` and `default-src` directives).

Classic workers are fetched in `no-cors` mode, executed as scripts, and import other scripts using {{domxref("WorkerGlobalScope.importScripts()")}} (not `import` statements).
Because they are fetched in `no-cors` mode, cross-origin requests are not blocked by CORS, and the response does not have to include the `Access-Control-Allow-Origin` header.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this. It seems to be saying: because classic workers are fetched in no-cors, then they can make cross-origin requests without CORS headers (btw "not blocked by CORS" is not a good way to put it, CORS doesn't block anything, it only unblocks things).

But is that right? Why does the mode a script was fetched in affect the mode of the requests it makes? I don't think this is true. If a resource is fetched in no-cors then the fetching of it has certain attributes, but I don't think that "carries over" to the requests that it makes, does it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg So yes, I am saying that classic workers are fetched in no-cors mode. My understanding was that this mean't that any requirements of CORS are ignored - the request will succeed as if CORS headers were not set.

I did not mean to make any statement about what mode a classic script would import its scripts using - I just tried to make clear that a classic script uses importScripts() to load further scripts, and not import.

You're right that we can't draw any assumptions about this. That said, that is what my tests showed. I'll try check again on Friday.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg So yes, I am saying that classic workers are fetched in no-cors mode. My understanding was that this mean't that any requirements of CORS are ignored - the request will succeed as if CORS headers were not set.

But that's only relevant for cross-origin fetches, right? So if fetching a worker has to be same-origin, it's irrelevant. Put another way, the doc says:

Because they are fetched in no-cors mode, cross-origin requests are not blocked by CORS,

...what are these "cross-origin requests", if fetching a worker has to be same-origin?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross origin import requests made by the worker.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Classic workers are fetched in `no-cors` mode, executed as scripts, and import other scripts using {{domxref("WorkerGlobalScope.importScripts()")}} (not `import` statements).
Because they are fetched in `no-cors` mode, cross-origin requests are not blocked by CORS, and the response does not have to include the `Access-Control-Allow-Origin` header.
Unlike for modules, the document [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) is not affected by the `worker-src` directive, but it is still affected by sources listed in the `script-src` directive.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like you're saying that worker-src controls the scripts that a worker can load? Not the source of the worker itself? In that case isn't the example here wrong: https://w3c.github.io/webappsec-csp/#directive-worker-src ?

@hamishwillee hamishwillee force-pushed the tt_worker_constructor branch from 40c7414 to 01ce5b6 Compare January 9, 2026 03:15
@hamishwillee hamishwillee force-pushed the tt_worker_constructor branch from 2add044 to 1837809 Compare January 9, 2026 04:29
@hamishwillee hamishwillee force-pushed the tt_worker_constructor branch from 403d673 to 047a381 Compare January 9, 2026 04:34
Comment on lines +106 to +107
- Dependencies are imported using the {{domxref("WorkerGlobalScope.importScripts()")}} method
- Fetched synchonously in `no-cors` mode
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would expand this with other differences if we knew them.

Comment on lines +119 to +120
In addition, if the document has a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP), it must allow the origins of imported scripts or modules.
For modules the allowed sources are specified in `worker-src` (with fallback to `script-src` and `default-src` directives), while for classic scripts the sources are specified in `script-src` (with fallback to the `default-src` directives).
Copy link
Collaborator Author

@hamishwillee hamishwillee Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg I think this is ready for another look.

Unfortunately I ate the commits while trying to create a version where it would be easy to be determine the changes.

Since last you looked I have addressed your comments, including adding this section on importing scripts and the credentials.

Essentially I think my testing provides a consistent story (or or less) - workers must be constructed with a same origin script/module. The credentials apply to the modules that are imported by a module worker (but not a script worker, over which you have no control via the API).

The remaining inconsitency is this section - why would you use worker-src if you can't import the main script cross-origin, and why is it ignored for scripts. However this is what my testing shows about CSP. If we question this too much, we could drop this line. Or just put it in, and hope to tidy later.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for all your work on this Hamish.

@wbamberg wbamberg merged commit d6f264d into mdn:main Jan 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants