1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScriptLoader.h"
10 #include <type_traits>
12 #include "nsIChannel.h"
13 #include "nsIContentPolicy.h"
14 #include "nsIContentSecurityPolicy.h"
15 #include "nsICookieJarSettings.h"
16 #include "nsIDocShell.h"
17 #include "nsIHttpChannel.h"
18 #include "nsIHttpChannelInternal.h"
19 #include "nsIIOService.h"
20 #include "nsIOService.h"
21 #include "nsIPrincipal.h"
22 #include "nsIProtocolHandler.h"
23 #include "nsIScriptError.h"
24 #include "nsIScriptSecurityManager.h"
25 #include "nsIStreamListenerTee.h"
26 #include "nsIThreadRetargetableRequest.h"
28 #include "nsIXPConnect.h"
31 #include "jsfriendapi.h"
32 #include "js/CompilationAndEvaluation.h"
33 #include "js/Exception.h"
34 #include "js/SourceText.h"
35 #include "js/TypeDecls.h"
37 #include "nsComponentManagerUtils.h"
38 #include "nsContentSecurityManager.h"
39 #include "nsContentPolicyUtils.h"
40 #include "nsContentUtils.h"
41 #include "nsDocShellCID.h"
42 #include "nsJSEnvironment.h"
43 #include "nsNetUtil.h"
45 #include "nsIOutputStream.h"
46 #include "nsPrintfCString.h"
49 #include "nsThreadUtils.h"
51 #include "xpcpublic.h"
53 #include "mozilla/AntiTrackingUtils.h"
54 #include "mozilla/ArrayAlgorithm.h"
55 #include "mozilla/Assertions.h"
56 #include "mozilla/Encoding.h"
57 #include "mozilla/LoadContext.h"
58 #include "mozilla/Maybe.h"
59 #include "mozilla/ipc/BackgroundUtils.h"
60 #include "mozilla/dom/ClientChannelHelper.h"
61 #include "mozilla/dom/ClientInfo.h"
62 #include "mozilla/dom/Exceptions.h"
63 #include "mozilla/dom/nsCSPService.h"
64 #include "mozilla/dom/nsCSPUtils.h"
65 #include "mozilla/dom/PerformanceStorage.h"
66 #include "mozilla/dom/Response.h"
67 #include "mozilla/dom/ReferrerInfo.h"
68 #include "mozilla/dom/ScriptSettings.h"
69 #include "mozilla/dom/SerializedStackHolder.h"
70 #include "mozilla/dom/workerinternals/CacheLoadHandler.h"
71 #include "mozilla/dom/workerinternals/NetworkLoadHandler.h"
72 #include "mozilla/dom/workerinternals/ScriptResponseHeaderProcessor.h"
73 #include "mozilla/Result.h"
74 #include "mozilla/ResultExtensions.h"
75 #include "mozilla/StaticPrefs_browser.h"
76 #include "mozilla/UniquePtr.h"
77 #include "WorkerRunnable.h"
78 #include "WorkerScope.h"
80 #define MAX_CONCURRENT_SCRIPTS 1000
82 using JS::loader::ParserMetadata
;
83 using JS::loader::ScriptKind
;
84 using JS::loader::ScriptLoadRequest
;
85 using mozilla::ipc::PrincipalInfo
;
87 namespace mozilla::dom::workerinternals
{
90 nsresult
ConstructURI(const nsAString
& aScriptURL
, nsIURI
* baseURI
,
91 const mozilla::Encoding
* aDocumentEncoding
,
94 // Only top level workers' main script use the document charset for the
95 // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
96 if (aDocumentEncoding
) {
97 nsAutoCString charset
;
98 aDocumentEncoding
->Name(charset
);
99 rv
= NS_NewURI(aResult
, aScriptURL
, charset
.get(), baseURI
);
101 rv
= NS_NewURI(aResult
, aScriptURL
, nullptr, baseURI
);
105 return NS_ERROR_DOM_SYNTAX_ERR
;
110 nsresult
ChannelFromScriptURL(
111 nsIPrincipal
* principal
, Document
* parentDoc
, WorkerPrivate
* aWorkerPrivate
,
112 nsILoadGroup
* loadGroup
, nsIIOService
* ios
,
113 nsIScriptSecurityManager
* secMan
, nsIURI
* aScriptURL
,
114 const Maybe
<ClientInfo
>& aClientInfo
,
115 const Maybe
<ServiceWorkerDescriptor
>& aController
, bool aIsMainScript
,
116 WorkerScriptType aWorkerScriptType
, nsContentPolicyType aContentPolicyType
,
117 nsLoadFlags aLoadFlags
, uint32_t aSecFlags
,
118 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
119 nsIChannel
** aChannel
) {
120 AssertIsOnMainThread();
123 nsCOMPtr
<nsIURI
> uri
= aScriptURL
;
125 // Only use the document when its principal matches the principal of the
126 // current request. This means scripts fetched using the Workers' own
127 // principal won't inherit properties of the document, in particular the CSP.
128 if (parentDoc
&& parentDoc
->NodePrincipal() != principal
) {
132 // The main service worker script should never be loaded over the network
133 // in this path. It should always be offlined by ServiceWorkerScriptCache.
134 // We assert here since this error should also be caught by the runtime
135 // check in CacheLoadHandler.
137 // Note, if we ever allow service worker scripts to be loaded from network
138 // here we need to configure the channel properly. For example, it must
139 // not allow redirects.
140 MOZ_DIAGNOSTIC_ASSERT(aContentPolicyType
!=
141 nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
);
143 nsCOMPtr
<nsIChannel
> channel
;
145 // This is the path for top level dedicated worker scripts with a document
146 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, parentDoc
, aSecFlags
,
148 nullptr, // aPerformanceStorage
150 nullptr, // aCallbacks
152 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
154 // This branch is used in the following cases:
155 // * Shared and ServiceWorkers (who do not have a doc)
156 // * Static Module Imports
159 // We must have a loadGroup with a load context for the principal to
160 // traverse the channel correctly.
162 MOZ_ASSERT(loadGroup
);
163 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
));
165 RefPtr
<PerformanceStorage
> performanceStorage
;
166 nsCOMPtr
<nsICSPEventListener
> cspEventListener
;
167 if (aWorkerPrivate
&& !aIsMainScript
) {
168 performanceStorage
= aWorkerPrivate
->GetPerformanceStorage();
169 cspEventListener
= aWorkerPrivate
->CSPEventListener();
172 if (aClientInfo
.isSome()) {
173 // If we have an existing clientInfo (true for all modules and
174 // importScripts), we will use this branch
175 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
,
176 aClientInfo
.ref(), aController
, aSecFlags
,
177 aContentPolicyType
, aCookieJarSettings
,
178 performanceStorage
, loadGroup
, nullptr, // aCallbacks
181 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
, aSecFlags
,
182 aContentPolicyType
, aCookieJarSettings
,
183 performanceStorage
, loadGroup
, nullptr, // aCallbacks
187 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
189 if (cspEventListener
) {
190 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
191 rv
= loadInfo
->SetCspEventListener(cspEventListener
);
192 NS_ENSURE_SUCCESS(rv
, rv
);
197 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
);
199 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
200 if (NS_WARN_IF(NS_FAILED(rv
))) {
206 channel
.forget(aChannel
);
210 void LoadAllScripts(WorkerPrivate
* aWorkerPrivate
,
211 UniquePtr
<SerializedStackHolder
> aOriginStack
,
212 const nsTArray
<nsString
>& aScriptURLs
, bool aIsMainScript
,
213 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
,
214 const mozilla::Encoding
* aDocumentEncoding
= nullptr) {
215 aWorkerPrivate
->AssertIsOnWorkerThread();
216 NS_ASSERTION(!aScriptURLs
.IsEmpty(), "Bad arguments!");
218 AutoSyncLoopHolder
syncLoop(aWorkerPrivate
, Canceling
);
219 nsCOMPtr
<nsISerialEventTarget
> syncLoopTarget
=
220 syncLoop
.GetSerialEventTarget();
221 if (!syncLoopTarget
) {
222 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
226 RefPtr
<loader::WorkerScriptLoader
> loader
=
227 new loader::WorkerScriptLoader(aWorkerPrivate
, std::move(aOriginStack
),
228 syncLoopTarget
, aWorkerScriptType
, aRv
);
230 if (NS_WARN_IF(aRv
.Failed())) {
234 bool ok
= loader
->CreateScriptRequests(aScriptURLs
, aDocumentEncoding
,
240 // Bug 1817259 - For now, we force loading the debugger script as Classic,
241 // even if the debugged worker is a Module.
242 if (aWorkerPrivate
->WorkerType() == WorkerType::Module
&&
243 aWorkerScriptType
!= DebuggerScript
) {
244 if (!StaticPrefs::dom_workers_modules_enabled()) {
245 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
248 MOZ_ASSERT(aIsMainScript
);
250 RefPtr
<JS::loader::ScriptLoadRequest
> mainScript
= loader
->GetMainScript();
251 if (mainScript
&& mainScript
->IsModuleRequest()) {
252 if (NS_FAILED(mainScript
->AsModuleRequest()->StartModuleLoad())) {
260 if (loader
->DispatchLoadScripts()) {
265 class ChannelGetterRunnable final
: public WorkerMainThreadRunnable
{
266 const nsAString
& mScriptURL
;
267 const WorkerType
& mWorkerType
;
268 const RequestCredentials
& mCredentials
;
269 const ClientInfo mClientInfo
;
270 WorkerLoadInfo
& mLoadInfo
;
274 ChannelGetterRunnable(WorkerPrivate
* aParentWorker
,
275 const nsAString
& aScriptURL
,
276 const WorkerType
& aWorkerType
,
277 const RequestCredentials
& aCredentials
,
278 WorkerLoadInfo
& aLoadInfo
)
279 : WorkerMainThreadRunnable(aParentWorker
,
280 "ScriptLoader :: ChannelGetter"_ns
),
281 mScriptURL(aScriptURL
)
282 // ClientInfo should always be present since this should not be called
283 // if parent's status is greater than Running.
285 mWorkerType(aWorkerType
),
286 mCredentials(aCredentials
),
287 mClientInfo(aParentWorker
->GlobalScope()->GetClientInfo().ref()),
288 mLoadInfo(aLoadInfo
),
289 mResult(NS_ERROR_FAILURE
) {
290 MOZ_ASSERT(aParentWorker
);
291 aParentWorker
->AssertIsOnWorkerThread();
294 virtual bool MainThreadRun() override
{
295 AssertIsOnMainThread();
297 // Initialize the WorkerLoadInfo principal to our triggering principal
298 // before doing anything else. Normally we do this in the WorkerPrivate
299 // Constructor, but we can't do so off the main thread when creating
300 // a nested worker. So do it here instead.
301 mLoadInfo
.mLoadingPrincipal
= mWorkerPrivate
->GetPrincipal();
302 MOZ_DIAGNOSTIC_ASSERT(mLoadInfo
.mLoadingPrincipal
);
304 mLoadInfo
.mPrincipal
= mLoadInfo
.mLoadingPrincipal
;
306 // Figure out our base URI.
307 nsCOMPtr
<nsIURI
> baseURI
= mWorkerPrivate
->GetBaseURI();
311 nsCOMPtr
<Document
> parentDoc
= mWorkerPrivate
->GetDocument();
313 mLoadInfo
.mLoadGroup
= mWorkerPrivate
->GetLoadGroup();
314 mLoadInfo
.mCookieJarSettings
= mWorkerPrivate
->CookieJarSettings();
316 // Nested workers use default uri encoding.
317 nsCOMPtr
<nsIURI
> url
;
318 mResult
= ConstructURI(mScriptURL
, baseURI
, nullptr, getter_AddRefs(url
));
319 NS_ENSURE_SUCCESS(mResult
, true);
321 Maybe
<ClientInfo
> clientInfo
;
322 clientInfo
.emplace(mClientInfo
);
324 nsCOMPtr
<nsIChannel
> channel
;
325 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
326 ReferrerInfo::CreateForFetch(mLoadInfo
.mLoadingPrincipal
, nullptr);
327 mLoadInfo
.mReferrerInfo
=
328 static_cast<ReferrerInfo
*>(referrerInfo
.get())
329 ->CloneWithNewPolicy(mWorkerPrivate
->GetReferrerPolicy());
331 mResult
= workerinternals::ChannelFromScriptURLMainThread(
332 mLoadInfo
.mLoadingPrincipal
, parentDoc
, mLoadInfo
.mLoadGroup
, url
,
333 mWorkerType
, mCredentials
, clientInfo
,
334 // Nested workers are always dedicated.
335 nsIContentPolicy::TYPE_INTERNAL_WORKER
, mLoadInfo
.mCookieJarSettings
,
336 mLoadInfo
.mReferrerInfo
, getter_AddRefs(channel
));
337 NS_ENSURE_SUCCESS(mResult
, true);
339 mResult
= mLoadInfo
.SetPrincipalsAndCSPFromChannel(channel
);
340 NS_ENSURE_SUCCESS(mResult
, true);
342 mLoadInfo
.mChannel
= std::move(channel
);
346 nsresult
GetResult() const { return mResult
; }
349 virtual ~ChannelGetterRunnable() = default;
352 nsresult
GetCommonSecFlags(bool aIsMainScript
, nsIURI
* uri
,
353 nsIPrincipal
* principal
,
354 WorkerScriptType aWorkerScriptType
,
355 uint32_t& secFlags
) {
356 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
357 principal
, uri
, true /* aInheritForAboutBlank */,
358 false /* aForceInherit */);
360 bool isData
= uri
->SchemeIs("data");
361 if (inheritAttrs
&& !isData
) {
362 secFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
365 if (aWorkerScriptType
== DebuggerScript
) {
366 // A DebuggerScript needs to be a local resource like chrome: or resource:
367 bool isUIResource
= false;
368 nsresult rv
= NS_URIChainHasFlags(
369 uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
, &isUIResource
);
370 if (NS_WARN_IF(NS_FAILED(rv
))) {
375 return NS_ERROR_DOM_SECURITY_ERR
;
378 secFlags
|= nsILoadInfo::SEC_ALLOW_CHROME
;
381 // Note: this is for backwards compatibility and goes against spec.
382 // We should find a better solution.
383 if (aIsMainScript
&& isData
) {
384 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
390 nsresult
GetModuleSecFlags(bool aIsTopLevel
, nsIPrincipal
* principal
,
391 WorkerScriptType aWorkerScriptType
, nsIURI
* aURI
,
392 RequestCredentials aCredentials
,
393 uint32_t& secFlags
) {
394 // Implements "To fetch a single module script,"
395 // Step 9. If destination is "worker", "sharedworker", or "serviceworker",
396 // and the top-level module fetch flag is set, then set request's
397 // mode to "same-origin".
398 secFlags
= aIsTopLevel
399 ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
400 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
402 // Step 8. Let request be a new request whose [...] mode is "cors" [...]
403 // This implements the same Cookie settings as nsContentSecurityManager's
404 // ComputeSecurityFlags. The main difference is the line above, Step 9,
405 // setting to same origin.
406 if (aCredentials
== RequestCredentials::Include
) {
407 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_INCLUDE
;
408 } else if (aCredentials
== RequestCredentials::Same_origin
) {
409 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
410 } else if (aCredentials
== RequestCredentials::Omit
) {
411 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_OMIT
;
414 return GetCommonSecFlags(aIsTopLevel
, aURI
, principal
, aWorkerScriptType
,
418 nsresult
GetClassicSecFlags(bool aIsMainScript
, nsIURI
* uri
,
419 nsIPrincipal
* principal
,
420 WorkerScriptType aWorkerScriptType
,
421 uint32_t& secFlags
) {
422 secFlags
= aIsMainScript
423 ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
424 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
426 return GetCommonSecFlags(aIsMainScript
, uri
, principal
, aWorkerScriptType
,
430 } // anonymous namespace
434 class ScriptExecutorRunnable final
: public MainThreadWorkerSyncRunnable
{
435 RefPtr
<WorkerScriptLoader
> mScriptLoader
;
436 const Span
<RefPtr
<ThreadSafeRequestHandle
>> mLoadedRequests
;
439 ScriptExecutorRunnable(WorkerScriptLoader
* aScriptLoader
,
440 WorkerPrivate
* aWorkerPrivate
,
441 nsISerialEventTarget
* aSyncLoopTarget
,
442 Span
<RefPtr
<ThreadSafeRequestHandle
>> aLoadedRequests
);
445 ~ScriptExecutorRunnable() = default;
447 virtual bool IsDebuggerRunnable() const override
;
449 virtual bool PreRun(WorkerPrivate
* aWorkerPrivate
) override
;
451 bool ProcessModuleScript(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
453 bool ProcessClassicScripts(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
455 virtual bool WorkerRun(JSContext
* aCx
,
456 WorkerPrivate
* aWorkerPrivate
) override
;
458 nsresult
Cancel() override
;
461 template <typename Unit
>
462 static bool EvaluateSourceBuffer(JSContext
* aCx
,
463 const JS::CompileOptions
& aOptions
,
464 JS::loader::ClassicScript
* aClassicScript
,
465 JS::SourceText
<Unit
>& aSourceBuffer
) {
466 static_assert(std::is_same
<Unit
, char16_t
>::value
||
467 std::is_same
<Unit
, Utf8Unit
>::value
,
468 "inferred units must be UTF-8 or UTF-16");
470 JS::Rooted
<JSScript
*> script(aCx
, JS::Compile(aCx
, aOptions
, aSourceBuffer
));
476 if (aClassicScript
) {
477 aClassicScript
->AssociateWithScript(script
);
480 JS::Rooted
<JS::Value
> unused(aCx
);
481 return JS_ExecuteScript(aCx
, script
, &unused
);
484 WorkerScriptLoader::WorkerScriptLoader(
485 WorkerPrivate
* aWorkerPrivate
,
486 UniquePtr
<SerializedStackHolder
> aOriginStack
,
487 nsISerialEventTarget
* aSyncLoopTarget
, WorkerScriptType aWorkerScriptType
,
489 : mOriginStack(std::move(aOriginStack
)),
490 mSyncLoopTarget(aSyncLoopTarget
),
491 mWorkerScriptType(aWorkerScriptType
),
493 mLoadingModuleRequestCount(0),
495 mCleanUpLock("cleanUpLock") {
496 aWorkerPrivate
->AssertIsOnWorkerThread();
498 RefPtr
<StrongWorkerRef
> workerRef
=
499 StrongWorkerRef::Create(aWorkerPrivate
, "ScriptLoader");
502 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
504 mRv
.Throw(NS_ERROR_FAILURE
);
508 nsIGlobalObject
* global
= GetGlobal();
509 mController
= global
->GetController();
511 if (!StaticPrefs::dom_workers_modules_enabled()) {
515 // Set up the module loader, if it has not been initialzied yet.
516 if (!aWorkerPrivate
->IsServiceWorker()) {
521 ScriptLoadRequest
* WorkerScriptLoader::GetMainScript() {
522 mWorkerRef
->Private()->AssertIsOnWorkerThread();
523 ScriptLoadRequest
* request
= mLoadingRequests
.getFirst();
524 if (request
->GetWorkerLoadContext()->IsTopLevel()) {
530 void WorkerScriptLoader::InitModuleLoader() {
531 mWorkerRef
->Private()->AssertIsOnWorkerThread();
532 if (GetGlobal()->GetModuleLoader(nullptr)) {
535 RefPtr
<WorkerModuleLoader
> moduleLoader
=
536 new WorkerModuleLoader(this, GetGlobal(), mSyncLoopTarget
.get());
537 if (mWorkerScriptType
== WorkerScript
) {
538 mWorkerRef
->Private()->GlobalScope()->InitModuleLoader(moduleLoader
);
541 mWorkerRef
->Private()->DebuggerGlobalScope()->InitModuleLoader(moduleLoader
);
544 bool WorkerScriptLoader::CreateScriptRequests(
545 const nsTArray
<nsString
>& aScriptURLs
,
546 const mozilla::Encoding
* aDocumentEncoding
, bool aIsMainScript
) {
547 mWorkerRef
->Private()->AssertIsOnWorkerThread();
548 // If a worker has been loaded as a module worker, ImportScripts calls are
549 // disallowed -- then the operation is invalid.
551 // 10.3.1 Importing scripts and libraries.
552 // Step 1. If worker global scope's type is "module", throw a TypeError
555 // Also, for now, the debugger script is always loaded as Classic,
556 // even if the debugged worker is a Module. We still want to allow
557 // it to use importScripts.
558 if (mWorkerRef
->Private()->WorkerType() == WorkerType::Module
&&
559 !aIsMainScript
&& !IsDebuggerScript()) {
560 // This should only run for non-main scripts, as only these are
563 "Using `ImportScripts` inside a Module Worker is "
567 for (const nsString
& scriptURL
: aScriptURLs
) {
568 RefPtr
<ScriptLoadRequest
> request
=
569 CreateScriptLoadRequest(scriptURL
, aDocumentEncoding
, aIsMainScript
);
573 mLoadingRequests
.AppendElement(request
);
579 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> WorkerScriptLoader::GetLoadingList() {
580 mWorkerRef
->Private()->AssertIsOnWorkerThread();
581 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> list
;
582 for (ScriptLoadRequest
* req
= mLoadingRequests
.getFirst(); req
;
583 req
= req
->getNext()) {
584 RefPtr
<ThreadSafeRequestHandle
> handle
=
585 new ThreadSafeRequestHandle(req
, mSyncLoopTarget
.get());
586 list
.AppendElement(handle
.forget());
591 bool WorkerScriptLoader::IsDynamicImport(ScriptLoadRequest
* aRequest
) {
592 return aRequest
->IsModuleRequest() &&
593 aRequest
->AsModuleRequest()->IsDynamicImport();
596 nsContentPolicyType
WorkerScriptLoader::GetContentPolicyType(
597 ScriptLoadRequest
* aRequest
) {
598 if (aRequest
->GetWorkerLoadContext()->IsTopLevel()) {
599 // Implements https://html.spec.whatwg.org/#worker-processing-model
600 // Step 13: Let destination be "sharedworker" if is shared is true, and
601 // "worker" otherwise.
602 return mWorkerRef
->Private()->ContentPolicyType();
604 if (aRequest
->IsModuleRequest()) {
605 // Implements the destination for Step 14 in
606 // https://html.spec.whatwg.org/#worker-processing-model
608 // We need a special subresource type in order to correctly implement
609 // the graph fetch, where the destination is set to "worker" or
611 return nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE
;
613 return nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS
;
616 already_AddRefed
<ScriptLoadRequest
> WorkerScriptLoader::CreateScriptLoadRequest(
617 const nsString
& aScriptURL
, const mozilla::Encoding
* aDocumentEncoding
,
618 bool aIsMainScript
) {
619 mWorkerRef
->Private()->AssertIsOnWorkerThread();
620 WorkerLoadContext::Kind kind
=
621 WorkerLoadContext::GetKind(aIsMainScript
, IsDebuggerScript());
623 Maybe
<ClientInfo
> clientInfo
= GetGlobal()->GetClientInfo();
625 RefPtr
<WorkerLoadContext
> loadContext
=
626 new WorkerLoadContext(kind
, clientInfo
, this);
628 // Create ScriptLoadRequests for this WorkerScriptLoader
629 ReferrerPolicy referrerPolicy
= mWorkerRef
->Private()->GetReferrerPolicy();
631 // Only top level workers' main script use the document charset for the
632 // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
633 MOZ_ASSERT_IF(bool(aDocumentEncoding
),
634 aIsMainScript
&& !mWorkerRef
->Private()->GetParent());
635 nsCOMPtr
<nsIURI
> baseURI
= aIsMainScript
? GetInitialBaseURI() : GetBaseURI();
636 nsCOMPtr
<nsIURI
> uri
;
638 ConstructURI(aScriptURL
, baseURI
, aDocumentEncoding
, getter_AddRefs(uri
));
639 // If we failed to construct the URI, handle it in the LoadContext so it is
640 // thrown in the right order.
641 if (NS_WARN_IF(NS_FAILED(rv
))) {
642 loadContext
->mLoadResult
= rv
;
645 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-worker-script
646 // Step 2.5. Let script be the result [...] and the default classic script
649 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
650 // Step 1. Let options be a script fetch options whose cryptographic nonce is
651 // the empty string, integrity metadata is the empty string, parser metadata
652 // is "not-parser-inserted", credentials mode is credentials mode, referrer
653 // policy is the empty string, and fetch priority is "auto".
654 RefPtr
<ScriptFetchOptions
> fetchOptions
= new ScriptFetchOptions(
655 CORSMode::CORS_NONE
, referrerPolicy
, /* aNonce = */ u
""_ns
,
656 ParserMetadata::NotParserInserted
, nullptr);
658 RefPtr
<ScriptLoadRequest
> request
= nullptr;
659 // Bug 1817259 - For now the debugger scripts are always loaded a Classic.
660 if (mWorkerRef
->Private()->WorkerType() == WorkerType::Classic
||
661 IsDebuggerScript()) {
662 request
= new ScriptLoadRequest(ScriptKind::eClassic
, uri
, fetchOptions
,
663 SRIMetadata(), nullptr, // mReferrer
666 // Implements part of "To fetch a worklet/module worker script graph"
667 // including, setting up the request with a credentials mode,
670 // Step 1. Let options be a script fetch options.
671 // We currently don't track credentials in our ScriptFetchOptions
672 // implementation, so we are defaulting the fetchOptions object defined
673 // above. This behavior is handled fully in GetModuleSecFlags.
675 if (!StaticPrefs::dom_workers_modules_enabled()) {
676 mRv
.ThrowTypeError("Modules in workers are currently disallowed.");
679 RefPtr
<WorkerModuleLoader::ModuleLoaderBase
> moduleLoader
=
680 GetGlobal()->GetModuleLoader(nullptr);
682 // Implements the referrer for "To fetch a single module script"
683 // Our implementation does not have a "client" as a referrer.
684 // However, when client is resolved (per 8.3. Determine request’s
686 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer)
687 // This should result in the referrer source being the creation URL.
689 // In subresource modules, the referrer is the importing script.
690 nsCOMPtr
<nsIURI
> referrer
=
691 mWorkerRef
->Private()->GetReferrerInfo()->GetOriginalReferrer();
693 // Part of Step 2. This sets the Top-level flag to true
694 request
= new ModuleLoadRequest(
695 uri
, fetchOptions
, SRIMetadata(), referrer
, loadContext
,
696 true, /* is top level */
697 false, /* is dynamic import */
698 moduleLoader
, ModuleLoadRequest::NewVisitedSetForTopLevelImport(uri
),
702 // Set the mURL, it will be used for error handling and debugging.
703 request
->mURL
= NS_ConvertUTF16toUTF8(aScriptURL
);
705 return request
.forget();
708 bool WorkerScriptLoader::DispatchLoadScript(ScriptLoadRequest
* aRequest
) {
709 mWorkerRef
->Private()->AssertIsOnWorkerThread();
711 IncreaseLoadingModuleRequestCount();
713 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> scriptLoadList
;
714 RefPtr
<ThreadSafeRequestHandle
> handle
=
715 new ThreadSafeRequestHandle(aRequest
, mSyncLoopTarget
.get());
716 scriptLoadList
.AppendElement(handle
.forget());
718 RefPtr
<ScriptLoaderRunnable
> runnable
=
719 new ScriptLoaderRunnable(this, std::move(scriptLoadList
));
721 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
722 mWorkerRef
->Private(), "ScriptLoader", [runnable
]() {
723 NS_DispatchToMainThread(NewRunnableMethod(
724 "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
726 &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted
));
729 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
730 NS_ERROR("Failed to dispatch!");
731 mRv
.Throw(NS_ERROR_FAILURE
);
737 bool WorkerScriptLoader::DispatchLoadScripts() {
738 mWorkerRef
->Private()->AssertIsOnWorkerThread();
740 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> scriptLoadList
= GetLoadingList();
742 RefPtr
<ScriptLoaderRunnable
> runnable
=
743 new ScriptLoaderRunnable(this, std::move(scriptLoadList
));
745 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
746 mWorkerRef
->Private(), "ScriptLoader", [runnable
]() {
747 NS_DispatchToMainThread(NewRunnableMethod(
748 "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
750 &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted
));
753 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
754 NS_ERROR("Failed to dispatch!");
755 mRv
.Throw(NS_ERROR_FAILURE
);
761 nsIURI
* WorkerScriptLoader::GetInitialBaseURI() {
762 MOZ_ASSERT(mWorkerRef
->Private());
764 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
766 baseURI
= parentWorker
->GetBaseURI();
769 baseURI
= mWorkerRef
->Private()->GetBaseURI();
775 nsIURI
* WorkerScriptLoader::GetBaseURI() const {
776 MOZ_ASSERT(mWorkerRef
);
778 baseURI
= mWorkerRef
->Private()->GetBaseURI();
779 NS_ASSERTION(baseURI
, "Should have been set already!");
784 nsIGlobalObject
* WorkerScriptLoader::GetGlobal() {
785 mWorkerRef
->Private()->AssertIsOnWorkerThread();
786 return mWorkerScriptType
== WorkerScript
787 ? static_cast<nsIGlobalObject
*>(
788 mWorkerRef
->Private()->GlobalScope())
789 : mWorkerRef
->Private()->DebuggerGlobalScope();
792 void WorkerScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest
* aRequest
) {
793 mWorkerRef
->Private()->AssertIsOnWorkerThread();
794 // Only set to ready for regular scripts. Module loader will set the script to
795 // ready if it is a Module Request.
796 if (!aRequest
->IsModuleRequest()) {
797 aRequest
->SetReady();
800 // If the request is not in a list, we are in an illegal state.
801 MOZ_RELEASE_ASSERT(aRequest
->isInList());
803 while (!mLoadingRequests
.isEmpty()) {
804 ScriptLoadRequest
* request
= mLoadingRequests
.getFirst();
805 // We need to move requests in post order. If prior requests have not
806 // completed, delay execution.
807 if (!request
->IsFinished()) {
811 RefPtr
<ScriptLoadRequest
> req
= mLoadingRequests
.Steal(request
);
812 mLoadedRequests
.AppendElement(req
);
816 bool WorkerScriptLoader::StoreCSP() {
817 // We must be on the same worker as we started on.
818 mWorkerRef
->Private()->AssertIsOnWorkerThread();
820 if (!mWorkerRef
->Private()->GetJSContext()) {
824 MOZ_ASSERT(!mRv
.Failed());
826 // Move the CSP from the workerLoadInfo in the corresponding Client
827 // where the CSP code expects it!
828 mWorkerRef
->Private()->StoreCSPOnClient();
832 bool WorkerScriptLoader::ProcessPendingRequests(JSContext
* aCx
) {
833 mWorkerRef
->Private()->AssertIsOnWorkerThread();
834 // Don't run if something else has already failed.
835 if (mExecutionAborted
) {
836 mLoadedRequests
.CancelRequestsAndClear();
841 // If nothing else has failed, our ErrorResult better not be a failure
843 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
845 // Slightly icky action at a distance, but there's no better place to stash
846 // this value, really.
847 JS::Rooted
<JSObject
*> global(aCx
, JS::CurrentGlobalOrNull(aCx
));
850 while (!mLoadedRequests
.isEmpty()) {
851 // Take a reference, but do not remove it from the list yet. There is a
852 // possibility that this will need to be cancelled.
853 RefPtr
<ScriptLoadRequest
> req
= mLoadedRequests
.getFirst();
854 // We don't have a ProcessRequest method (like we do on the DOM), as there
855 // isn't much processing that we need to do per request that isn't related
856 // to evaluation (the processsing done for the DOM is handled in
857 // DataRecievedFrom{Cache,Network} for workers.
858 // So, this inner loop calls EvaluateScript directly. This will change
859 // once modules are introduced as we will have some extra work to do.
860 if (!EvaluateScript(aCx
, req
)) {
861 mExecutionAborted
= true;
862 WorkerLoadContext
* loadContext
= req
->GetWorkerLoadContext();
863 mMutedErrorFlag
= loadContext
->mMutedErrorFlag
.valueOr(true);
864 mLoadedRequests
.CancelRequestsAndClear();
867 // remove the element from the list.
868 mLoadedRequests
.Remove(req
);
875 nsresult
WorkerScriptLoader::LoadScript(
876 ThreadSafeRequestHandle
* aRequestHandle
) {
877 AssertIsOnMainThread();
879 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
880 ScriptLoadRequest
* request
= aRequestHandle
->GetRequest();
881 MOZ_ASSERT_IF(loadContext
->IsTopLevel(), !IsDebuggerScript());
883 // The URL passed to us for loading was invalid, stop loading at this point.
884 if (loadContext
->mLoadResult
!= NS_ERROR_NOT_INITIALIZED
) {
885 return loadContext
->mLoadResult
;
888 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
890 // For JavaScript debugging, the devtools server must run on the same
891 // thread as the debuggee, indicating the worker uses content principal.
892 // However, in Bug 863246, web content will no longer be able to load
893 // resource:// URIs by default, so we need system principal to load
895 nsIPrincipal
* principal
= (IsDebuggerScript())
896 ? nsContentUtils::GetSystemPrincipal()
897 : mWorkerRef
->Private()->GetPrincipal();
899 nsCOMPtr
<nsILoadGroup
> loadGroup
= mWorkerRef
->Private()->GetLoadGroup();
900 MOZ_DIAGNOSTIC_ASSERT(principal
);
902 NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
),
906 nsCOMPtr
<Document
> parentDoc
= mWorkerRef
->Private()->GetDocument();
908 nsCOMPtr
<nsIChannel
> channel
;
909 if (loadContext
->IsTopLevel()) {
911 channel
= mWorkerRef
->Private()->ForgetWorkerChannel();
914 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
916 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
917 NS_ASSERTION(secMan
, "This should never be null!");
919 nsresult
& rv
= loadContext
->mLoadResult
;
921 nsLoadFlags loadFlags
= mWorkerRef
->Private()->GetLoadFlags();
923 // Get the top-level worker.
924 WorkerPrivate
* topWorkerPrivate
= mWorkerRef
->Private();
925 WorkerPrivate
* parent
= topWorkerPrivate
->GetParent();
927 topWorkerPrivate
= parent
;
928 parent
= topWorkerPrivate
->GetParent();
931 // If the top-level worker is a dedicated worker and has a window, and the
932 // window has a docshell, the caching behavior of this worker should match
933 // that of that docshell.
934 if (topWorkerPrivate
->IsDedicatedWorker()) {
935 nsCOMPtr
<nsPIDOMWindowInner
> window
= topWorkerPrivate
->GetWindow();
937 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
939 nsresult rv
= docShell
->GetDefaultLoadFlags(&loadFlags
);
940 NS_ENSURE_SUCCESS(rv
, rv
);
946 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
948 if (request
->IsModuleRequest()) {
949 // https://fetch.spec.whatwg.org/#concept-main-fetch
950 // Step 8. If request’s referrer policy is the empty string, then set
951 // request’s referrer policy to request’s policy container’s
953 ReferrerPolicy policy
=
954 request
->ReferrerPolicy() == ReferrerPolicy::_empty
955 ? mWorkerRef
->Private()->GetReferrerPolicy()
956 : request
->ReferrerPolicy();
958 referrerInfo
= new ReferrerInfo(request
->mReferrer
, policy
);
960 // https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
961 // The default classic script fetch options are a script fetch options
962 // whose ... credentials mode is "same-origin", ....
963 RequestCredentials credentials
=
964 mWorkerRef
->Private()->WorkerType() == WorkerType::Classic
965 ? RequestCredentials::Same_origin
966 : mWorkerRef
->Private()->WorkerCredentials();
968 rv
= GetModuleSecFlags(loadContext
->IsTopLevel(), principal
,
969 mWorkerScriptType
, request
->mURI
, credentials
,
972 referrerInfo
= ReferrerInfo::CreateForFetch(principal
, nullptr);
973 if (parentWorker
&& !loadContext
->IsTopLevel()) {
975 static_cast<ReferrerInfo
*>(referrerInfo
.get())
976 ->CloneWithNewPolicy(parentWorker
->GetReferrerPolicy());
978 rv
= GetClassicSecFlags(loadContext
->IsTopLevel(), request
->mURI
,
979 principal
, mWorkerScriptType
, secFlags
);
982 if (NS_WARN_IF(NS_FAILED(rv
))) {
986 nsContentPolicyType contentPolicyType
= GetContentPolicyType(request
);
988 rv
= ChannelFromScriptURL(
989 principal
, parentDoc
, mWorkerRef
->Private(), loadGroup
, ios
, secMan
,
990 request
->mURI
, loadContext
->mClientInfo
, mController
,
991 loadContext
->IsTopLevel(), mWorkerScriptType
, contentPolicyType
,
992 loadFlags
, secFlags
, mWorkerRef
->Private()->CookieJarSettings(),
993 referrerInfo
, getter_AddRefs(channel
));
994 if (NS_WARN_IF(NS_FAILED(rv
))) {
999 // Associate any originating stack with the channel.
1000 if (!mOriginStackJSON
.IsEmpty()) {
1001 NotifyNetworkMonitorAlternateStack(channel
, mOriginStackJSON
);
1004 // We need to know which index we're on in OnStreamComplete so we know
1005 // where to put the result.
1006 RefPtr
<NetworkLoadHandler
> listener
=
1007 new NetworkLoadHandler(this, aRequestHandle
);
1009 RefPtr
<ScriptResponseHeaderProcessor
> headerProcessor
= nullptr;
1011 // For each debugger script, a non-debugger script load of the same script
1012 // should have occured prior that processed the headers.
1013 if (!IsDebuggerScript()) {
1014 headerProcessor
= MakeRefPtr
<ScriptResponseHeaderProcessor
>(
1015 mWorkerRef
->Private(),
1016 loadContext
->IsTopLevel() && !IsDynamicImport(request
),
1017 GetContentPolicyType(request
) ==
1018 nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS
);
1021 nsCOMPtr
<nsIStreamLoader
> loader
;
1022 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), listener
, headerProcessor
);
1023 if (NS_WARN_IF(NS_FAILED(rv
))) {
1027 if (loadContext
->IsTopLevel()) {
1028 MOZ_DIAGNOSTIC_ASSERT(loadContext
->mClientInfo
.isSome());
1030 // In order to get the correct foreign partitioned prinicpal, we need to
1031 // set the `IsThirdPartyContextToTopWindow` to the channel's loadInfo.
1032 // This flag reflects the fact that if the worker is created under a
1033 // third-party context.
1034 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1035 loadInfo
->SetIsThirdPartyContextToTopWindow(
1036 mWorkerRef
->Private()->IsThirdPartyContextToTopWindow());
1038 Maybe
<ClientInfo
> clientInfo
;
1039 clientInfo
.emplace(loadContext
->mClientInfo
.ref());
1040 rv
= AddClientChannelHelper(channel
, std::move(clientInfo
),
1041 Maybe
<ClientInfo
>(),
1042 mWorkerRef
->Private()->HybridEventTarget());
1043 if (NS_WARN_IF(NS_FAILED(rv
))) {
1048 if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
1049 nsILoadInfo::CrossOriginEmbedderPolicy respectedCOEP
=
1050 mWorkerRef
->Private()->GetEmbedderPolicy();
1051 if (mWorkerRef
->Private()->IsDedicatedWorker() &&
1052 respectedCOEP
== nsILoadInfo::EMBEDDER_POLICY_NULL
) {
1053 respectedCOEP
= mWorkerRef
->Private()->GetOwnerEmbedderPolicy();
1056 nsCOMPtr
<nsILoadInfo
> channelLoadInfo
= channel
->LoadInfo();
1057 channelLoadInfo
->SetLoadingEmbedderPolicy(respectedCOEP
);
1060 if (loadContext
->mCacheStatus
!= WorkerLoadContext::ToBeCached
) {
1061 rv
= channel
->AsyncOpen(loader
);
1062 if (NS_WARN_IF(NS_FAILED(rv
))) {
1066 nsCOMPtr
<nsIOutputStream
> writer
;
1068 // In case we return early.
1069 loadContext
->mCacheStatus
= WorkerLoadContext::Cancel
;
1071 NS_NewPipe(getter_AddRefs(loadContext
->mCacheReadStream
),
1072 getter_AddRefs(writer
), 0,
1073 UINT32_MAX
, // unlimited size to avoid writer WOULD_BLOCK case
1074 true, false); // non-blocking reader, blocking writer
1076 nsCOMPtr
<nsIStreamListenerTee
> tee
=
1077 do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID
);
1078 rv
= tee
->Init(loader
, writer
, listener
);
1079 if (NS_WARN_IF(NS_FAILED(rv
))) {
1083 nsresult rv
= channel
->AsyncOpen(tee
);
1084 if (NS_WARN_IF(NS_FAILED(rv
))) {
1089 loadContext
->mChannel
.swap(channel
);
1094 nsresult
WorkerScriptLoader::FillCompileOptionsForRequest(
1095 JSContext
* cx
, ScriptLoadRequest
* aRequest
, JS::CompileOptions
* aOptions
,
1096 JS::MutableHandle
<JSScript
*> aIntroductionScript
) {
1097 // The full URL shouldn't be exposed to the debugger. See Bug 1634872
1098 aOptions
->setFileAndLine(aRequest
->mURL
.get(), 1);
1099 aOptions
->setNoScriptRval(true);
1101 aOptions
->setMutedErrors(
1102 aRequest
->GetWorkerLoadContext()->mMutedErrorFlag
.value());
1104 if (aRequest
->mSourceMapURL
) {
1105 aOptions
->setSourceMapURL(aRequest
->mSourceMapURL
->get());
1111 bool WorkerScriptLoader::EvaluateScript(JSContext
* aCx
,
1112 ScriptLoadRequest
* aRequest
) {
1113 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1114 MOZ_ASSERT(!IsDynamicImport(aRequest
));
1116 WorkerLoadContext
* loadContext
= aRequest
->GetWorkerLoadContext();
1118 NS_ASSERTION(!loadContext
->mChannel
, "Should no longer have a channel!");
1119 NS_ASSERTION(aRequest
->IsFinished(), "Should be scheduled!");
1121 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
1122 mRv
.MightThrowJSException();
1123 if (NS_FAILED(loadContext
->mLoadResult
)) {
1124 ReportErrorToConsole(aRequest
, loadContext
->mLoadResult
);
1128 // If this is a top level script that succeeded, then mark the
1129 // Client execution ready and possible controlled by a service worker.
1130 if (loadContext
->IsTopLevel()) {
1131 if (mController
.isSome()) {
1132 MOZ_ASSERT(mWorkerScriptType
== WorkerScript
,
1133 "Debugger clients can't be controlled.");
1134 mWorkerRef
->Private()->GlobalScope()->Control(mController
.ref());
1136 mWorkerRef
->Private()->ExecutionReady();
1139 if (aRequest
->IsModuleRequest()) {
1140 // Only the top level module of the module graph will be executed from here,
1141 // the rest will be executed from SpiderMonkey as part of the execution of
1142 // the module graph.
1143 MOZ_ASSERT(aRequest
->IsTopLevel());
1144 ModuleLoadRequest
* request
= aRequest
->AsModuleRequest();
1145 if (!request
->mModuleScript
) {
1149 // Implements To fetch a worklet/module worker script graph
1150 // Step 5. Fetch the descendants of and link result.
1151 if (!request
->InstantiateModuleGraph()) {
1155 nsresult rv
= request
->EvaluateModule();
1156 return NS_SUCCEEDED(rv
);
1159 JS::CompileOptions
options(aCx
);
1160 // The introduction script is used by the DOM script loader as a way
1161 // to fill the Debugger Metadata for the JS Execution context. We don't use
1162 // the JS Execution context as we are not making use of async compilation
1163 // (delegation to another worker to produce bytecode or compile a string to a
1164 // JSScript), so it is not used in this context.
1165 JS::Rooted
<JSScript
*> unusedIntroductionScript(aCx
);
1166 nsresult rv
= FillCompileOptionsForRequest(aCx
, aRequest
, &options
,
1167 &unusedIntroductionScript
);
1169 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Filling compile options should not fail");
1171 // Our ErrorResult still shouldn't be a failure.
1172 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
1174 // Get the source text.
1175 ScriptLoadRequest::MaybeSourceText maybeSource
;
1176 rv
= aRequest
->GetScriptSource(aCx
, &maybeSource
);
1177 if (NS_FAILED(rv
)) {
1178 mRv
.StealExceptionFromJSContext(aCx
);
1182 RefPtr
<JS::loader::ClassicScript
> classicScript
= nullptr;
1183 if (StaticPrefs::dom_workers_modules_enabled() &&
1184 !mWorkerRef
->Private()->IsServiceWorker()) {
1185 // We need a LoadedScript to be associated with the JSScript in order to
1186 // correctly resolve the referencing private for dynamic imports. In turn
1187 // this allows us to correctly resolve the BaseURL.
1189 // Dynamic import is disallowed on service workers. Additionally, causes
1190 // crashes because the life cycle isn't completed for service workers. To
1191 // keep things simple, we don't create a classic script for ServiceWorkers.
1192 // If this changes then we will need to ensure that the reference that is
1193 // held is released appropriately.
1194 nsCOMPtr
<nsIURI
> requestBaseURI
;
1195 if (loadContext
->mMutedErrorFlag
.valueOr(false)) {
1196 NS_NewURI(getter_AddRefs(requestBaseURI
), "about:blank"_ns
);
1198 requestBaseURI
= aRequest
->mBaseURL
;
1201 new JS::loader::ClassicScript(aRequest
->mFetchOptions
, requestBaseURI
);
1204 bool successfullyEvaluated
=
1205 aRequest
->IsUTF8Text()
1206 ? EvaluateSourceBuffer(aCx
, options
, classicScript
,
1207 maybeSource
.ref
<JS::SourceText
<Utf8Unit
>>())
1208 : EvaluateSourceBuffer(aCx
, options
, classicScript
,
1209 maybeSource
.ref
<JS::SourceText
<char16_t
>>());
1211 if (aRequest
->IsCanceled()) {
1214 if (!successfullyEvaluated
) {
1215 mRv
.StealExceptionFromJSContext(aCx
);
1218 // steal the loadContext so that the cycle is broken and cycle collector can
1219 // collect the scriptLoadRequest.
1223 void WorkerScriptLoader::TryShutdown() {
1225 MutexAutoLock
lock(CleanUpLock());
1231 if (AllScriptsExecuted() && AllModuleRequestsLoaded()) {
1232 ShutdownScriptLoader(!mExecutionAborted
, mMutedErrorFlag
);
1236 void WorkerScriptLoader::ShutdownScriptLoader(bool aResult
, bool aMutedError
) {
1237 MOZ_ASSERT(AllScriptsExecuted());
1238 MOZ_ASSERT(AllModuleRequestsLoaded());
1239 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1242 // At this point there are two possibilities:
1244 // 1) mRv.Failed(). In that case we just want to leave it
1245 // as-is, except if it has a JS exception and we need to mute JS
1246 // exceptions. In that case, we log the exception without firing any
1247 // events and then replace it on the ErrorResult with a NetworkError,
1250 // 2) mRv succeeded. As far as I can tell, this can only
1251 // happen when loading the main worker script and
1252 // GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
1253 // got called. Does it matter what we throw in this case? I'm not
1256 if (aMutedError
&& mRv
.IsJSException()) {
1257 LogExceptionToConsole(mWorkerRef
->Private()->GetJSContext(),
1258 mWorkerRef
->Private());
1259 mRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
1262 mRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1266 // Lock, shutdown, and cleanup state. After this the Loader is closed.
1268 MutexAutoLock
lock(CleanUpLock());
1274 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1275 // Module loader doesn't use sync loop for dynamic import
1276 if (mSyncLoopTarget
) {
1277 mWorkerRef
->Private()->StopSyncLoop(mSyncLoopTarget
,
1278 aResult
? NS_OK
: NS_ERROR_FAILURE
);
1284 // Allow worker shutdown.
1285 mWorkerRef
= nullptr;
1289 void WorkerScriptLoader::ReportErrorToConsole(ScriptLoadRequest
* aRequest
,
1290 nsresult aResult
) const {
1291 nsAutoString url
= NS_ConvertUTF8toUTF16(aRequest
->mURL
);
1292 workerinternals::ReportLoadError(mRv
, aResult
, url
);
1295 void WorkerScriptLoader::LogExceptionToConsole(JSContext
* aCx
,
1296 WorkerPrivate
* aWorkerPrivate
) {
1297 aWorkerPrivate
->AssertIsOnWorkerThread();
1299 MOZ_ASSERT(mRv
.IsJSException());
1301 JS::Rooted
<JS::Value
> exn(aCx
);
1302 if (!ToJSValue(aCx
, std::move(mRv
), &exn
)) {
1306 // Now the exception state should all be in exn.
1307 MOZ_ASSERT(!JS_IsExceptionPending(aCx
));
1308 MOZ_ASSERT(!mRv
.Failed());
1310 JS::ExceptionStack
exnStack(aCx
, exn
, nullptr);
1311 JS::ErrorReportBuilder
report(aCx
);
1312 if (!report
.init(aCx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
1313 JS_ClearPendingException(aCx
);
1317 RefPtr
<xpc::ErrorReport
> xpcReport
= new xpc::ErrorReport();
1318 xpcReport
->Init(report
.report(), report
.toStringResult().c_str(),
1319 aWorkerPrivate
->IsChromeWorker(), aWorkerPrivate
->WindowID());
1321 RefPtr
<AsyncErrorReporter
> r
= new AsyncErrorReporter(xpcReport
);
1322 NS_DispatchToMainThread(r
);
1325 bool WorkerScriptLoader::AllModuleRequestsLoaded() const {
1326 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1327 return mLoadingModuleRequestCount
== 0;
1330 void WorkerScriptLoader::IncreaseLoadingModuleRequestCount() {
1331 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1332 ++mLoadingModuleRequestCount
;
1335 void WorkerScriptLoader::DecreaseLoadingModuleRequestCount() {
1336 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1337 --mLoadingModuleRequestCount
;
1340 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable
, nsIRunnable
, nsINamed
)
1342 NS_IMPL_ISUPPORTS(WorkerScriptLoader
, nsINamed
)
1344 ScriptLoaderRunnable::ScriptLoaderRunnable(
1345 WorkerScriptLoader
* aScriptLoader
,
1346 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> aLoadingRequests
)
1347 : mScriptLoader(aScriptLoader
),
1348 mWorkerRef(aScriptLoader
->mWorkerRef
),
1349 mLoadingRequests(std::move(aLoadingRequests
)),
1350 mCancelMainThread(Nothing()) {
1351 MOZ_ASSERT(aScriptLoader
);
1354 nsresult
ScriptLoaderRunnable::Run() {
1355 AssertIsOnMainThread();
1357 // Convert the origin stack to JSON (which must be done on the main
1358 // thread) explicitly, so that we can use the stack to notify the net
1359 // monitor about every script we load. We do this, rather than pass
1360 // the stack directly to the netmonitor, in order to be able to use this
1361 // for all subsequent scripts.
1362 if (mScriptLoader
->mOriginStack
&&
1363 mScriptLoader
->mOriginStackJSON
.IsEmpty()) {
1364 ConvertSerializedStackToJSON(std::move(mScriptLoader
->mOriginStack
),
1365 mScriptLoader
->mOriginStackJSON
);
1368 if (!mWorkerRef
->Private()->IsServiceWorker() ||
1369 mScriptLoader
->IsDebuggerScript()) {
1370 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1371 handle
->mRunnable
= this;
1374 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1375 nsresult rv
= mScriptLoader
->LoadScript(handle
);
1376 if (NS_WARN_IF(NS_FAILED(rv
))) {
1377 LoadingFinished(handle
, rv
);
1378 CancelMainThread(rv
);
1386 MOZ_ASSERT(!mCacheCreator
);
1387 mCacheCreator
= new CacheCreator(mWorkerRef
->Private());
1389 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1390 handle
->mRunnable
= this;
1391 WorkerLoadContext
* loadContext
= handle
->GetContext();
1392 mCacheCreator
->AddLoader(MakeNotNull
<RefPtr
<CacheLoadHandler
>>(
1393 mWorkerRef
, handle
, loadContext
->IsTopLevel(), mScriptLoader
));
1396 // The worker may have a null principal on first load, but in that case its
1397 // parent definitely will have one.
1398 nsIPrincipal
* principal
= mWorkerRef
->Private()->GetPrincipal();
1400 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
1401 MOZ_ASSERT(parentWorker
, "Must have a parent!");
1402 principal
= parentWorker
->GetPrincipal();
1405 nsresult rv
= mCacheCreator
->Load(principal
);
1406 if (NS_WARN_IF(NS_FAILED(rv
))) {
1407 CancelMainThread(rv
);
1414 nsresult
ScriptLoaderRunnable::OnStreamComplete(
1415 ThreadSafeRequestHandle
* aRequestHandle
, nsresult aStatus
) {
1416 AssertIsOnMainThread();
1418 LoadingFinished(aRequestHandle
, aStatus
);
1422 void ScriptLoaderRunnable::LoadingFinished(
1423 ThreadSafeRequestHandle
* aRequestHandle
, nsresult aRv
) {
1424 AssertIsOnMainThread();
1426 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
1428 loadContext
->mLoadResult
= aRv
;
1429 MOZ_ASSERT(!loadContext
->mLoadingFinished
);
1430 loadContext
->mLoadingFinished
= true;
1432 if (loadContext
->IsTopLevel() && NS_SUCCEEDED(aRv
)) {
1433 MOZ_DIAGNOSTIC_ASSERT(
1434 mWorkerRef
->Private()->PrincipalURIMatchesScriptURL());
1437 MaybeExecuteFinishedScripts(aRequestHandle
);
1440 void ScriptLoaderRunnable::MaybeExecuteFinishedScripts(
1441 ThreadSafeRequestHandle
* aRequestHandle
) {
1442 AssertIsOnMainThread();
1444 // We execute the last step if we don't have a pending operation with the
1445 // cache and the loading is completed.
1446 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
1447 if (!loadContext
->IsAwaitingPromise()) {
1448 if (aRequestHandle
->GetContext()->IsTopLevel()) {
1449 mWorkerRef
->Private()->WorkerScriptLoaded();
1451 DispatchProcessPendingRequests();
1455 void ScriptLoaderRunnable::CancelMainThreadWithBindingAborted() {
1456 AssertIsOnMainThread();
1457 CancelMainThread(NS_BINDING_ABORTED
);
1460 void ScriptLoaderRunnable::CancelMainThread(nsresult aCancelResult
) {
1461 AssertIsOnMainThread();
1462 if (IsCancelled()) {
1467 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1469 // Check if we have already cancelled, or if the worker has been killed
1470 // before we cancel.
1471 if (mScriptLoader
->CleanedUp()) {
1475 mCancelMainThread
= Some(aCancelResult
);
1477 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1478 if (handle
->IsEmpty()) {
1482 bool callLoadingFinished
= true;
1484 WorkerLoadContext
* loadContext
= handle
->GetContext();
1489 if (loadContext
->IsAwaitingPromise()) {
1490 MOZ_ASSERT(mWorkerRef
->Private()->IsServiceWorker());
1491 loadContext
->mCachePromise
->MaybeReject(NS_BINDING_ABORTED
);
1492 loadContext
->mCachePromise
= nullptr;
1493 callLoadingFinished
= false;
1495 if (loadContext
->mChannel
) {
1496 if (NS_SUCCEEDED(loadContext
->mChannel
->Cancel(aCancelResult
))) {
1497 callLoadingFinished
= false;
1499 NS_WARNING("Failed to cancel channel!");
1502 if (callLoadingFinished
&& !loadContext
->mLoadingFinished
) {
1503 LoadingFinished(handle
, aCancelResult
);
1506 DispatchProcessPendingRequests();
1510 void ScriptLoaderRunnable::DispatchProcessPendingRequests() {
1511 AssertIsOnMainThread();
1513 const auto begin
= mLoadingRequests
.begin();
1514 const auto end
= mLoadingRequests
.end();
1515 using Iterator
= decltype(begin
);
1516 const auto maybeRangeToExecute
=
1517 [begin
, end
]() -> Maybe
<std::pair
<Iterator
, Iterator
>> {
1518 // firstItToExecute is the first loadInfo where mExecutionScheduled is
1520 auto firstItToExecute
= std::find_if(
1521 begin
, end
, [](const RefPtr
<ThreadSafeRequestHandle
>& requestHandle
) {
1522 return !requestHandle
->mExecutionScheduled
;
1525 if (firstItToExecute
== end
) {
1529 // firstItUnexecutable is the first loadInfo that is not yet finished.
1530 // Update mExecutionScheduled on the ones we're about to schedule for
1532 const auto firstItUnexecutable
=
1533 std::find_if(firstItToExecute
, end
,
1534 [](RefPtr
<ThreadSafeRequestHandle
>& requestHandle
) {
1535 MOZ_ASSERT(!requestHandle
->IsEmpty());
1536 if (!requestHandle
->Finished()) {
1540 // We can execute this one.
1541 requestHandle
->mExecutionScheduled
= true;
1546 return firstItUnexecutable
== firstItToExecute
1548 : Some(std::pair(firstItToExecute
, firstItUnexecutable
));
1551 // If there are no unexecutable load infos, we can unuse things before the
1552 // execution of the scripts and the stopping of the sync loop.
1553 if (maybeRangeToExecute
) {
1554 if (maybeRangeToExecute
->second
== end
) {
1555 mCacheCreator
= nullptr;
1558 RefPtr
<ScriptExecutorRunnable
> runnable
= new ScriptExecutorRunnable(
1559 mScriptLoader
, mWorkerRef
->Private(), mScriptLoader
->mSyncLoopTarget
,
1560 Span
<RefPtr
<ThreadSafeRequestHandle
>>{maybeRangeToExecute
->first
,
1561 maybeRangeToExecute
->second
});
1563 if (!runnable
->Dispatch() && mScriptLoader
->mSyncLoopTarget
) {
1564 MOZ_ASSERT(false, "This should never fail!");
1569 ScriptExecutorRunnable::ScriptExecutorRunnable(
1570 WorkerScriptLoader
* aScriptLoader
, WorkerPrivate
* aWorkerPrivate
,
1571 nsISerialEventTarget
* aSyncLoopTarget
,
1572 Span
<RefPtr
<ThreadSafeRequestHandle
>> aLoadedRequests
)
1573 : MainThreadWorkerSyncRunnable(aWorkerPrivate
, aSyncLoopTarget
),
1574 mScriptLoader(aScriptLoader
),
1575 mLoadedRequests(aLoadedRequests
) {}
1577 bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
1578 // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
1579 // In the latter case, the runnable needs to be dispatched to the debugger
1581 return mScriptLoader
->IsDebuggerScript();
1584 bool ScriptExecutorRunnable::PreRun(WorkerPrivate
* aWorkerPrivate
) {
1585 aWorkerPrivate
->AssertIsOnWorkerThread();
1587 // We must be on the same worker as we started on.
1589 mScriptLoader
->mSyncLoopTarget
== mSyncLoopTarget
,
1590 "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
1593 // There is a possibility that we cleaned up while this task was waiting to
1594 // run. If this has happened, return and exit.
1595 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1596 if (mScriptLoader
->CleanedUp()) {
1600 const auto& requestHandle
= mLoadedRequests
[0];
1601 // Check if the request is still valid.
1602 if (requestHandle
->IsEmpty() ||
1603 !requestHandle
->GetContext()->IsTopLevel()) {
1608 return mScriptLoader
->StoreCSP();
1611 bool ScriptExecutorRunnable::ProcessModuleScript(
1612 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1613 // We should only ever have one script when processing modules
1614 MOZ_ASSERT(mLoadedRequests
.Length() == 1);
1615 RefPtr
<ScriptLoadRequest
> request
;
1617 // There is a possibility that we cleaned up while this task was waiting to
1618 // run. If this has happened, return and exit.
1619 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1620 if (mScriptLoader
->CleanedUp()) {
1624 MOZ_ASSERT(mLoadedRequests
.Length() == 1);
1625 const auto& requestHandle
= mLoadedRequests
[0];
1626 // The request must be valid.
1627 MOZ_ASSERT(!requestHandle
->IsEmpty());
1629 // Release the request to the worker. From this point on, the Request Handle
1631 request
= requestHandle
->ReleaseRequest();
1633 // release lock. We will need it later if we cleanup.
1636 MOZ_ASSERT(request
->IsModuleRequest());
1638 WorkerLoadContext
* loadContext
= request
->GetWorkerLoadContext();
1639 ModuleLoadRequest
* moduleRequest
= request
->AsModuleRequest();
1641 // DecreaseLoadingModuleRequestCount must be called before OnFetchComplete.
1642 // OnFetchComplete will call ProcessPendingRequests, and in
1643 // ProcessPendingRequests it will try to shutdown if
1644 // AllModuleRequestsLoaded() returns true.
1645 mScriptLoader
->DecreaseLoadingModuleRequestCount();
1646 moduleRequest
->OnFetchComplete(loadContext
->mLoadResult
);
1648 if (NS_FAILED(loadContext
->mLoadResult
)) {
1649 if (moduleRequest
->IsDynamicImport()) {
1650 if (request
->isInList()) {
1651 moduleRequest
->CancelDynamicImport(loadContext
->mLoadResult
);
1652 mScriptLoader
->TryShutdown();
1654 } else if (!moduleRequest
->IsTopLevel()) {
1655 moduleRequest
->Cancel();
1656 mScriptLoader
->TryShutdown();
1658 moduleRequest
->LoadFailed();
1664 bool ScriptExecutorRunnable::ProcessClassicScripts(
1665 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1666 // There is a possibility that we cleaned up while this task was waiting to
1667 // run. If this has happened, return and exit.
1669 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1670 if (mScriptLoader
->CleanedUp()) {
1674 for (const auto& requestHandle
: mLoadedRequests
) {
1675 // The request must be valid.
1676 MOZ_ASSERT(!requestHandle
->IsEmpty());
1678 // Release the request to the worker. From this point on, the Request
1680 RefPtr
<ScriptLoadRequest
> request
= requestHandle
->ReleaseRequest();
1681 mScriptLoader
->MaybeMoveToLoadedList(request
);
1684 return mScriptLoader
->ProcessPendingRequests(aCx
);
1687 bool ScriptExecutorRunnable::WorkerRun(JSContext
* aCx
,
1688 WorkerPrivate
* aWorkerPrivate
) {
1689 aWorkerPrivate
->AssertIsOnWorkerThread();
1691 // We must be on the same worker as we started on.
1693 mScriptLoader
->mSyncLoopTarget
== mSyncLoopTarget
,
1694 "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
1696 if (mLoadedRequests
.begin()->get()->GetRequest()->IsModuleRequest()) {
1697 return ProcessModuleScript(aCx
, aWorkerPrivate
);
1700 return ProcessClassicScripts(aCx
, aWorkerPrivate
);
1703 nsresult
ScriptExecutorRunnable::Cancel() {
1704 if (mScriptLoader
->AllScriptsExecuted() &&
1705 mScriptLoader
->AllModuleRequestsLoaded()) {
1706 mScriptLoader
->ShutdownScriptLoader(false, false);
1711 } /* namespace loader */
1713 nsresult
ChannelFromScriptURLMainThread(
1714 nsIPrincipal
* aPrincipal
, Document
* aParentDoc
, nsILoadGroup
* aLoadGroup
,
1715 nsIURI
* aScriptURL
, const WorkerType
& aWorkerType
,
1716 const RequestCredentials
& aCredentials
,
1717 const Maybe
<ClientInfo
>& aClientInfo
,
1718 nsContentPolicyType aMainScriptContentPolicyType
,
1719 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
1720 nsIChannel
** aChannel
) {
1721 AssertIsOnMainThread();
1723 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
1725 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
1726 NS_ASSERTION(secMan
, "This should never be null!");
1730 if (aWorkerType
== WorkerType::Module
) {
1731 rv
= GetModuleSecFlags(true, aPrincipal
, WorkerScript
, aScriptURL
,
1732 aCredentials
, secFlags
);
1734 rv
= GetClassicSecFlags(true, aScriptURL
, aPrincipal
, WorkerScript
,
1737 if (NS_FAILED(rv
)) {
1741 return ChannelFromScriptURL(
1742 aPrincipal
, aParentDoc
, nullptr, aLoadGroup
, ios
, secMan
, aScriptURL
,
1743 aClientInfo
, Maybe
<ServiceWorkerDescriptor
>(), true, WorkerScript
,
1744 aMainScriptContentPolicyType
, nsIRequest::LOAD_NORMAL
, secFlags
,
1745 aCookieJarSettings
, aReferrerInfo
, aChannel
);
1748 nsresult
ChannelFromScriptURLWorkerThread(
1749 JSContext
* aCx
, WorkerPrivate
* aParent
, const nsAString
& aScriptURL
,
1750 const WorkerType
& aWorkerType
, const RequestCredentials
& aCredentials
,
1751 WorkerLoadInfo
& aLoadInfo
) {
1752 aParent
->AssertIsOnWorkerThread();
1754 RefPtr
<ChannelGetterRunnable
> getter
= new ChannelGetterRunnable(
1755 aParent
, aScriptURL
, aWorkerType
, aCredentials
, aLoadInfo
);
1758 getter
->Dispatch(Canceling
, rv
);
1760 NS_ERROR("Failed to dispatch!");
1761 return rv
.StealNSResult();
1764 return getter
->GetResult();
1767 void ReportLoadError(ErrorResult
& aRv
, nsresult aLoadResult
,
1768 const nsAString
& aScriptURL
) {
1769 MOZ_ASSERT(!aRv
.Failed());
1771 nsPrintfCString
err("Failed to load worker script at \"%s\"",
1772 NS_ConvertUTF16toUTF8(aScriptURL
).get());
1774 switch (aLoadResult
) {
1775 case NS_ERROR_FILE_NOT_FOUND
:
1776 case NS_ERROR_NOT_AVAILABLE
:
1777 case NS_ERROR_CORRUPTED_CONTENT
:
1778 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
1781 case NS_ERROR_MALFORMED_URI
:
1782 case NS_ERROR_DOM_SYNTAX_ERR
:
1783 aRv
.ThrowSyntaxError(err
);
1786 case NS_BINDING_ABORTED
:
1787 // Note: we used to pretend like we didn't set an exception for
1788 // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway. The
1789 // other callsite, in WorkerPrivate::Constructor, never passed in
1790 // NS_BINDING_ABORTED. So just throw it directly here. Consumers will
1791 // deal as needed. But note that we do NOT want to use one of the
1792 // Throw*Error() methods on ErrorResult for this case, because that will
1793 // make it impossible for consumers to realize that our error was
1794 // NS_BINDING_ABORTED.
1795 aRv
.Throw(aLoadResult
);
1798 case NS_ERROR_DOM_BAD_URI
:
1799 // This is actually a security error.
1800 case NS_ERROR_DOM_SECURITY_ERR
:
1801 aRv
.ThrowSecurityError(err
);
1805 // For lack of anything better, go ahead and throw a NetworkError here.
1806 // We don't want to throw a JS exception, because for toplevel script
1807 // loads that would get squelched.
1808 aRv
.ThrowNetworkError(nsPrintfCString(
1809 "Failed to load worker script at %s (nsresult = 0x%" PRIx32
")",
1810 NS_ConvertUTF16toUTF8(aScriptURL
).get(),
1811 static_cast<uint32_t>(aLoadResult
)));
1816 void LoadMainScript(WorkerPrivate
* aWorkerPrivate
,
1817 UniquePtr
<SerializedStackHolder
> aOriginStack
,
1818 const nsAString
& aScriptURL
,
1819 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
,
1820 const mozilla::Encoding
* aDocumentEncoding
) {
1821 nsTArray
<nsString
> scriptURLs
;
1823 scriptURLs
.AppendElement(aScriptURL
);
1825 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), scriptURLs
, true,
1826 aWorkerScriptType
, aRv
, aDocumentEncoding
);
1829 void Load(WorkerPrivate
* aWorkerPrivate
,
1830 UniquePtr
<SerializedStackHolder
> aOriginStack
,
1831 const nsTArray
<nsString
>& aScriptURLs
,
1832 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
) {
1833 const uint32_t urlCount
= aScriptURLs
.Length();
1839 if (urlCount
> MAX_CONCURRENT_SCRIPTS
) {
1840 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1844 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), aScriptURLs
, false,
1845 aWorkerScriptType
, aRv
);
1848 } // namespace mozilla::dom::workerinternals