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 "mozilla/dom/RequestBinding.h"
13 #include "nsIChannel.h"
14 #include "nsIContentPolicy.h"
15 #include "nsIContentSecurityPolicy.h"
16 #include "nsICookieJarSettings.h"
17 #include "nsIDocShell.h"
18 #include "nsIHttpChannel.h"
19 #include "nsIHttpChannelInternal.h"
20 #include "nsIIOService.h"
21 #include "nsIOService.h"
22 #include "nsIPrincipal.h"
23 #include "nsIProtocolHandler.h"
24 #include "nsIScriptError.h"
25 #include "nsIScriptSecurityManager.h"
26 #include "nsIStreamListenerTee.h"
27 #include "nsIThreadRetargetableRequest.h"
29 #include "nsIXPConnect.h"
32 #include "jsfriendapi.h"
33 #include "js/CompilationAndEvaluation.h"
34 #include "js/Exception.h"
35 #include "js/SourceText.h"
36 #include "js/TypeDecls.h"
38 #include "nsComponentManagerUtils.h"
39 #include "nsContentSecurityManager.h"
40 #include "nsContentPolicyUtils.h"
41 #include "nsContentUtils.h"
42 #include "nsDocShellCID.h"
43 #include "nsJSEnvironment.h"
44 #include "nsNetUtil.h"
46 #include "nsIOutputStream.h"
47 #include "nsPrintfCString.h"
50 #include "nsThreadUtils.h"
52 #include "xpcpublic.h"
54 #include "mozilla/AntiTrackingUtils.h"
55 #include "mozilla/ArrayAlgorithm.h"
56 #include "mozilla/Assertions.h"
57 #include "mozilla/Encoding.h"
58 #include "mozilla/LoadContext.h"
59 #include "mozilla/Maybe.h"
60 #include "mozilla/ipc/BackgroundUtils.h"
61 #include "mozilla/dom/ClientChannelHelper.h"
62 #include "mozilla/dom/ClientInfo.h"
63 #include "mozilla/dom/Exceptions.h"
64 #include "mozilla/dom/nsCSPService.h"
65 #include "mozilla/dom/nsCSPUtils.h"
66 #include "mozilla/dom/PerformanceStorage.h"
67 #include "mozilla/dom/Response.h"
68 #include "mozilla/dom/ReferrerInfo.h"
69 #include "mozilla/dom/ScriptSettings.h"
70 #include "mozilla/dom/SerializedStackHolder.h"
71 #include "mozilla/dom/workerinternals/CacheLoadHandler.h"
72 #include "mozilla/dom/workerinternals/NetworkLoadHandler.h"
73 #include "mozilla/dom/workerinternals/ScriptResponseHeaderProcessor.h"
74 #include "mozilla/Result.h"
75 #include "mozilla/ResultExtensions.h"
76 #include "mozilla/StaticPrefs_browser.h"
77 #include "mozilla/UniquePtr.h"
78 #include "WorkerRunnable.h"
79 #include "WorkerScope.h"
81 #define MAX_CONCURRENT_SCRIPTS 1000
83 using JS::loader::ParserMetadata
;
84 using JS::loader::ScriptKind
;
85 using JS::loader::ScriptLoadRequest
;
86 using mozilla::ipc::PrincipalInfo
;
88 namespace mozilla::dom::workerinternals
{
91 nsresult
ConstructURI(const nsAString
& aScriptURL
, nsIURI
* baseURI
,
92 const mozilla::Encoding
* aDocumentEncoding
,
95 // Only top level workers' main script use the document charset for the
96 // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
97 if (aDocumentEncoding
) {
98 nsAutoCString charset
;
99 aDocumentEncoding
->Name(charset
);
100 rv
= NS_NewURI(aResult
, aScriptURL
, charset
.get(), baseURI
);
102 rv
= NS_NewURI(aResult
, aScriptURL
, nullptr, baseURI
);
106 return NS_ERROR_DOM_SYNTAX_ERR
;
111 nsresult
ChannelFromScriptURL(
112 nsIPrincipal
* principal
, Document
* parentDoc
, WorkerPrivate
* aWorkerPrivate
,
113 nsILoadGroup
* loadGroup
, nsIIOService
* ios
,
114 nsIScriptSecurityManager
* secMan
, nsIURI
* aScriptURL
,
115 const Maybe
<ClientInfo
>& aClientInfo
,
116 const Maybe
<ServiceWorkerDescriptor
>& aController
, bool aIsMainScript
,
117 WorkerScriptType aWorkerScriptType
, nsContentPolicyType aContentPolicyType
,
118 nsLoadFlags aLoadFlags
, uint32_t aSecFlags
,
119 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
120 nsIChannel
** aChannel
) {
121 AssertIsOnMainThread();
124 nsCOMPtr
<nsIURI
> uri
= aScriptURL
;
126 // Only use the document when its principal matches the principal of the
127 // current request. This means scripts fetched using the Workers' own
128 // principal won't inherit properties of the document, in particular the CSP.
129 if (parentDoc
&& parentDoc
->NodePrincipal() != principal
) {
133 // The main service worker script should never be loaded over the network
134 // in this path. It should always be offlined by ServiceWorkerScriptCache.
135 // We assert here since this error should also be caught by the runtime
136 // check in CacheLoadHandler.
138 // Note, if we ever allow service worker scripts to be loaded from network
139 // here we need to configure the channel properly. For example, it must
140 // not allow redirects.
141 MOZ_DIAGNOSTIC_ASSERT(aContentPolicyType
!=
142 nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
);
144 nsCOMPtr
<nsIChannel
> channel
;
146 // This is the path for top level dedicated worker scripts with a document
147 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, parentDoc
, aSecFlags
,
149 nullptr, // aPerformanceStorage
151 nullptr, // aCallbacks
153 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
155 // This branch is used in the following cases:
156 // * Shared and ServiceWorkers (who do not have a doc)
157 // * Static Module Imports
160 // We must have a loadGroup with a load context for the principal to
161 // traverse the channel correctly.
163 MOZ_ASSERT(loadGroup
);
164 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
));
166 RefPtr
<PerformanceStorage
> performanceStorage
;
167 nsCOMPtr
<nsICSPEventListener
> cspEventListener
;
168 if (aWorkerPrivate
&& !aIsMainScript
) {
169 performanceStorage
= aWorkerPrivate
->GetPerformanceStorage();
170 cspEventListener
= aWorkerPrivate
->CSPEventListener();
173 if (aClientInfo
.isSome()) {
174 // If we have an existing clientInfo (true for all modules and
175 // importScripts), we will use this branch
176 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
,
177 aClientInfo
.ref(), aController
, aSecFlags
,
178 aContentPolicyType
, aCookieJarSettings
,
179 performanceStorage
, loadGroup
, nullptr, // aCallbacks
182 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
, aSecFlags
,
183 aContentPolicyType
, aCookieJarSettings
,
184 performanceStorage
, loadGroup
, nullptr, // aCallbacks
188 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
190 if (cspEventListener
) {
191 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
192 rv
= loadInfo
->SetCspEventListener(cspEventListener
);
193 NS_ENSURE_SUCCESS(rv
, rv
);
198 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
);
200 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
201 if (NS_WARN_IF(NS_FAILED(rv
))) {
207 channel
.forget(aChannel
);
211 void LoadAllScripts(WorkerPrivate
* aWorkerPrivate
,
212 UniquePtr
<SerializedStackHolder
> aOriginStack
,
213 const nsTArray
<nsString
>& aScriptURLs
, bool aIsMainScript
,
214 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
,
215 const mozilla::Encoding
* aDocumentEncoding
= nullptr) {
216 aWorkerPrivate
->AssertIsOnWorkerThread();
217 NS_ASSERTION(!aScriptURLs
.IsEmpty(), "Bad arguments!");
219 AutoSyncLoopHolder
syncLoop(aWorkerPrivate
, Canceling
);
220 nsCOMPtr
<nsISerialEventTarget
> syncLoopTarget
=
221 syncLoop
.GetSerialEventTarget();
222 if (!syncLoopTarget
) {
223 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
227 RefPtr
<loader::WorkerScriptLoader
> loader
=
228 loader::WorkerScriptLoader::Create(
229 aWorkerPrivate
, std::move(aOriginStack
), syncLoopTarget
,
230 aWorkerScriptType
, aRv
);
232 if (NS_WARN_IF(aRv
.Failed())) {
236 bool ok
= loader
->CreateScriptRequests(aScriptURLs
, aDocumentEncoding
,
242 // Bug 1817259 - For now, we force loading the debugger script as Classic,
243 // even if the debugged worker is a Module.
244 if (aWorkerPrivate
->WorkerType() == WorkerType::Module
&&
245 aWorkerScriptType
!= DebuggerScript
) {
246 if (!StaticPrefs::dom_workers_modules_enabled()) {
247 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
250 MOZ_ASSERT(aIsMainScript
);
252 RefPtr
<JS::loader::ScriptLoadRequest
> mainScript
= loader
->GetMainScript();
253 if (mainScript
&& mainScript
->IsModuleRequest()) {
254 if (NS_FAILED(mainScript
->AsModuleRequest()->StartModuleLoad())) {
262 if (loader
->DispatchLoadScripts()) {
267 class ChannelGetterRunnable final
: public WorkerMainThreadRunnable
{
268 const nsAString
& mScriptURL
;
269 const WorkerType
& mWorkerType
;
270 const RequestCredentials
& mCredentials
;
271 const ClientInfo mClientInfo
;
272 WorkerLoadInfo
& mLoadInfo
;
276 ChannelGetterRunnable(WorkerPrivate
* aParentWorker
,
277 const nsAString
& aScriptURL
,
278 const WorkerType
& aWorkerType
,
279 const RequestCredentials
& aCredentials
,
280 WorkerLoadInfo
& aLoadInfo
)
281 : WorkerMainThreadRunnable(aParentWorker
,
282 "ScriptLoader :: ChannelGetter"_ns
),
283 mScriptURL(aScriptURL
)
284 // ClientInfo should always be present since this should not be called
285 // if parent's status is greater than Running.
287 mWorkerType(aWorkerType
),
288 mCredentials(aCredentials
),
289 mClientInfo(aParentWorker
->GlobalScope()->GetClientInfo().ref()),
290 mLoadInfo(aLoadInfo
),
291 mResult(NS_ERROR_FAILURE
) {
292 MOZ_ASSERT(aParentWorker
);
293 aParentWorker
->AssertIsOnWorkerThread();
296 virtual bool MainThreadRun() override
{
297 AssertIsOnMainThread();
299 // Initialize the WorkerLoadInfo principal to our triggering principal
300 // before doing anything else. Normally we do this in the WorkerPrivate
301 // Constructor, but we can't do so off the main thread when creating
302 // a nested worker. So do it here instead.
303 mLoadInfo
.mLoadingPrincipal
= mWorkerPrivate
->GetPrincipal();
304 MOZ_DIAGNOSTIC_ASSERT(mLoadInfo
.mLoadingPrincipal
);
306 mLoadInfo
.mPrincipal
= mLoadInfo
.mLoadingPrincipal
;
308 // Figure out our base URI.
309 nsCOMPtr
<nsIURI
> baseURI
= mWorkerPrivate
->GetBaseURI();
313 nsCOMPtr
<Document
> parentDoc
= mWorkerPrivate
->GetDocument();
315 mLoadInfo
.mLoadGroup
= mWorkerPrivate
->GetLoadGroup();
316 mLoadInfo
.mCookieJarSettings
= mWorkerPrivate
->CookieJarSettings();
318 // Nested workers use default uri encoding.
319 nsCOMPtr
<nsIURI
> url
;
320 mResult
= ConstructURI(mScriptURL
, baseURI
, nullptr, getter_AddRefs(url
));
321 NS_ENSURE_SUCCESS(mResult
, true);
323 Maybe
<ClientInfo
> clientInfo
;
324 clientInfo
.emplace(mClientInfo
);
326 nsCOMPtr
<nsIChannel
> channel
;
327 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
328 ReferrerInfo::CreateForFetch(mLoadInfo
.mLoadingPrincipal
, nullptr);
329 mLoadInfo
.mReferrerInfo
=
330 static_cast<ReferrerInfo
*>(referrerInfo
.get())
331 ->CloneWithNewPolicy(mWorkerPrivate
->GetReferrerPolicy());
333 mResult
= workerinternals::ChannelFromScriptURLMainThread(
334 mLoadInfo
.mLoadingPrincipal
, parentDoc
, mLoadInfo
.mLoadGroup
, url
,
335 mWorkerType
, mCredentials
, clientInfo
,
336 // Nested workers are always dedicated.
337 nsIContentPolicy::TYPE_INTERNAL_WORKER
, mLoadInfo
.mCookieJarSettings
,
338 mLoadInfo
.mReferrerInfo
, getter_AddRefs(channel
));
339 NS_ENSURE_SUCCESS(mResult
, true);
341 mResult
= mLoadInfo
.SetPrincipalsAndCSPFromChannel(channel
);
342 NS_ENSURE_SUCCESS(mResult
, true);
344 mLoadInfo
.mChannel
= std::move(channel
);
348 nsresult
GetResult() const { return mResult
; }
351 virtual ~ChannelGetterRunnable() = default;
354 nsresult
GetCommonSecFlags(bool aIsMainScript
, nsIURI
* uri
,
355 nsIPrincipal
* principal
,
356 WorkerScriptType aWorkerScriptType
,
357 uint32_t& secFlags
) {
358 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
359 principal
, uri
, true /* aInheritForAboutBlank */,
360 false /* aForceInherit */);
362 bool isData
= uri
->SchemeIs("data");
363 if (inheritAttrs
&& !isData
) {
364 secFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
367 if (aWorkerScriptType
== DebuggerScript
) {
368 // A DebuggerScript needs to be a local resource like chrome: or resource:
369 bool isUIResource
= false;
370 nsresult rv
= NS_URIChainHasFlags(
371 uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
, &isUIResource
);
372 if (NS_WARN_IF(NS_FAILED(rv
))) {
377 return NS_ERROR_DOM_SECURITY_ERR
;
380 secFlags
|= nsILoadInfo::SEC_ALLOW_CHROME
;
383 // Note: this is for backwards compatibility and goes against spec.
384 // We should find a better solution.
385 if (aIsMainScript
&& isData
) {
386 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
392 nsresult
GetModuleSecFlags(bool aIsTopLevel
, nsIPrincipal
* principal
,
393 WorkerScriptType aWorkerScriptType
, nsIURI
* aURI
,
394 RequestCredentials aCredentials
,
395 uint32_t& secFlags
) {
396 // Implements "To fetch a single module script,"
397 // Step 9. If destination is "worker", "sharedworker", or "serviceworker",
398 // and the top-level module fetch flag is set, then set request's
399 // mode to "same-origin".
401 // Step 8. Let request be a new request whose [...] mode is "cors" [...]
402 secFlags
= aIsTopLevel
? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
403 : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
405 // This implements the same Cookie settings as nsContentSecurityManager's
406 // ComputeSecurityFlags. The main difference is the line above, Step 9,
407 // setting to same origin.
409 if (aCredentials
== RequestCredentials::Include
) {
410 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_INCLUDE
;
411 } else if (aCredentials
== RequestCredentials::Same_origin
) {
412 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
413 } else if (aCredentials
== RequestCredentials::Omit
) {
414 secFlags
|= nsILoadInfo::nsILoadInfo::SEC_COOKIES_OMIT
;
417 return GetCommonSecFlags(aIsTopLevel
, aURI
, principal
, aWorkerScriptType
,
421 nsresult
GetClassicSecFlags(bool aIsMainScript
, nsIURI
* uri
,
422 nsIPrincipal
* principal
,
423 WorkerScriptType aWorkerScriptType
,
424 uint32_t& secFlags
) {
425 secFlags
= aIsMainScript
426 ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
427 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
429 return GetCommonSecFlags(aIsMainScript
, uri
, principal
, aWorkerScriptType
,
433 } // anonymous namespace
437 class ScriptExecutorRunnable final
: public MainThreadWorkerSyncRunnable
{
438 RefPtr
<WorkerScriptLoader
> mScriptLoader
;
439 const Span
<RefPtr
<ThreadSafeRequestHandle
>> mLoadedRequests
;
442 ScriptExecutorRunnable(WorkerScriptLoader
* aScriptLoader
,
443 WorkerPrivate
* aWorkerPrivate
,
444 nsISerialEventTarget
* aSyncLoopTarget
,
445 Span
<RefPtr
<ThreadSafeRequestHandle
>> aLoadedRequests
);
448 ~ScriptExecutorRunnable() = default;
450 virtual bool IsDebuggerRunnable() const override
;
452 virtual bool PreRun(WorkerPrivate
* aWorkerPrivate
) override
;
454 bool ProcessModuleScript(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
456 bool ProcessClassicScripts(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
458 virtual bool WorkerRun(JSContext
* aCx
,
459 WorkerPrivate
* aWorkerPrivate
) override
;
461 nsresult
Cancel() override
;
464 static bool EvaluateSourceBuffer(JSContext
* aCx
, JS::Handle
<JSScript
*> aScript
,
465 JS::loader::ClassicScript
* aClassicScript
) {
466 if (aClassicScript
) {
467 aClassicScript
->AssociateWithScript(aScript
);
470 JS::Rooted
<JS::Value
> unused(aCx
);
471 return JS_ExecuteScript(aCx
, aScript
, &unused
);
474 WorkerScriptLoader::WorkerScriptLoader(
475 UniquePtr
<SerializedStackHolder
> aOriginStack
,
476 nsISerialEventTarget
* aSyncLoopTarget
, WorkerScriptType aWorkerScriptType
,
478 : mOriginStack(std::move(aOriginStack
)),
479 mSyncLoopTarget(aSyncLoopTarget
),
480 mWorkerScriptType(aWorkerScriptType
),
482 mLoadingModuleRequestCount(0),
484 mCleanUpLock("cleanUpLock") {}
486 already_AddRefed
<WorkerScriptLoader
> WorkerScriptLoader::Create(
487 WorkerPrivate
* aWorkerPrivate
,
488 UniquePtr
<SerializedStackHolder
> aOriginStack
,
489 nsISerialEventTarget
* aSyncLoopTarget
, WorkerScriptType aWorkerScriptType
,
491 aWorkerPrivate
->AssertIsOnWorkerThread();
493 RefPtr
<WorkerScriptLoader
> self
= new WorkerScriptLoader(
494 std::move(aOriginStack
), aSyncLoopTarget
, aWorkerScriptType
, aRv
);
496 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
497 aWorkerPrivate
, "WorkerScriptLoader::Create", [self
]() {
498 // Requests that are in flight are covered by the worker references
499 // in DispatchLoadScript(s), so we do not need to do additional
500 // cleanup, but just in case we are ready/aborted we can try to
501 // shutdown here, too.
506 self
->mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
508 self
->mRv
.Throw(NS_ERROR_FAILURE
);
512 nsIGlobalObject
* global
= self
->GetGlobal();
513 self
->mController
= global
->GetController();
515 if (!StaticPrefs::dom_workers_modules_enabled()) {
516 return self
.forget();
519 // Set up the module loader, if it has not been initialzied yet.
520 if (!aWorkerPrivate
->IsServiceWorker()) {
521 self
->InitModuleLoader();
524 return self
.forget();
527 ScriptLoadRequest
* WorkerScriptLoader::GetMainScript() {
528 mWorkerRef
->Private()->AssertIsOnWorkerThread();
529 ScriptLoadRequest
* request
= mLoadingRequests
.getFirst();
530 if (request
->GetWorkerLoadContext()->IsTopLevel()) {
536 void WorkerScriptLoader::InitModuleLoader() {
537 mWorkerRef
->Private()->AssertIsOnWorkerThread();
538 if (GetGlobal()->GetModuleLoader(nullptr)) {
541 RefPtr
<WorkerModuleLoader
> moduleLoader
=
542 new WorkerModuleLoader(this, GetGlobal());
543 if (mWorkerScriptType
== WorkerScript
) {
544 mWorkerRef
->Private()->GlobalScope()->InitModuleLoader(moduleLoader
);
547 mWorkerRef
->Private()->DebuggerGlobalScope()->InitModuleLoader(moduleLoader
);
550 bool WorkerScriptLoader::CreateScriptRequests(
551 const nsTArray
<nsString
>& aScriptURLs
,
552 const mozilla::Encoding
* aDocumentEncoding
, bool aIsMainScript
) {
553 mWorkerRef
->Private()->AssertIsOnWorkerThread();
554 // If a worker has been loaded as a module worker, ImportScripts calls are
555 // disallowed -- then the operation is invalid.
557 // 10.3.1 Importing scripts and libraries.
558 // Step 1. If worker global scope's type is "module", throw a TypeError
561 // Also, for now, the debugger script is always loaded as Classic,
562 // even if the debugged worker is a Module. We still want to allow
563 // it to use importScripts.
564 if (mWorkerRef
->Private()->WorkerType() == WorkerType::Module
&&
565 !aIsMainScript
&& !IsDebuggerScript()) {
566 // This should only run for non-main scripts, as only these are
569 "Using `ImportScripts` inside a Module Worker is "
573 for (const nsString
& scriptURL
: aScriptURLs
) {
574 RefPtr
<ScriptLoadRequest
> request
=
575 CreateScriptLoadRequest(scriptURL
, aDocumentEncoding
, aIsMainScript
);
579 mLoadingRequests
.AppendElement(request
);
585 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> WorkerScriptLoader::GetLoadingList() {
586 mWorkerRef
->Private()->AssertIsOnWorkerThread();
587 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> list
;
588 for (ScriptLoadRequest
* req
= mLoadingRequests
.getFirst(); req
;
589 req
= req
->getNext()) {
590 RefPtr
<ThreadSafeRequestHandle
> handle
=
591 new ThreadSafeRequestHandle(req
, mSyncLoopTarget
.get());
592 list
.AppendElement(handle
.forget());
597 bool WorkerScriptLoader::IsDynamicImport(ScriptLoadRequest
* aRequest
) {
598 return aRequest
->IsModuleRequest() &&
599 aRequest
->AsModuleRequest()->IsDynamicImport();
602 nsContentPolicyType
WorkerScriptLoader::GetContentPolicyType(
603 ScriptLoadRequest
* aRequest
) {
604 if (aRequest
->GetWorkerLoadContext()->IsTopLevel()) {
605 // Implements https://html.spec.whatwg.org/#worker-processing-model
606 // Step 13: Let destination be "sharedworker" if is shared is true, and
607 // "worker" otherwise.
608 return mWorkerRef
->Private()->ContentPolicyType();
610 if (aRequest
->IsModuleRequest()) {
611 if (aRequest
->AsModuleRequest()->IsDynamicImport()) {
612 return nsIContentPolicy::TYPE_INTERNAL_MODULE
;
615 // Implements the destination for Step 14 in
616 // https://html.spec.whatwg.org/#worker-processing-model
618 // We need a special subresource type in order to correctly implement
619 // the graph fetch, where the destination is set to "worker" or
621 return nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE
;
623 // For script imported in worker's importScripts().
624 return nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS
;
627 already_AddRefed
<ScriptLoadRequest
> WorkerScriptLoader::CreateScriptLoadRequest(
628 const nsString
& aScriptURL
, const mozilla::Encoding
* aDocumentEncoding
,
629 bool aIsMainScript
) {
630 mWorkerRef
->Private()->AssertIsOnWorkerThread();
631 WorkerLoadContext::Kind kind
=
632 WorkerLoadContext::GetKind(aIsMainScript
, IsDebuggerScript());
634 Maybe
<ClientInfo
> clientInfo
= GetGlobal()->GetClientInfo();
636 // (For non-serviceworkers, this variable does not matter, but false best
637 // captures their behavior.)
638 bool onlyExistingCachedResourcesAllowed
= false;
639 if (mWorkerRef
->Private()->IsServiceWorker()) {
640 // https://w3c.github.io/ServiceWorker/#importscripts step 4:
641 // > 4. If serviceWorker’s state is not "parsed" or "installing":
642 // > 1. Return map[url] if it exists and a network error otherwise.
644 // So if our state is beyond installing, it's too late to make a request
645 // that would perform a new fetch which would be cached.
646 onlyExistingCachedResourcesAllowed
=
647 mWorkerRef
->Private()->GetServiceWorkerDescriptor().State() >
648 ServiceWorkerState::Installing
;
650 RefPtr
<WorkerLoadContext
> loadContext
= new WorkerLoadContext(
651 kind
, clientInfo
, this, onlyExistingCachedResourcesAllowed
);
653 // Create ScriptLoadRequests for this WorkerScriptLoader
654 ReferrerPolicy referrerPolicy
= mWorkerRef
->Private()->GetReferrerPolicy();
656 // Only top level workers' main script use the document charset for the
657 // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
658 MOZ_ASSERT_IF(bool(aDocumentEncoding
),
659 aIsMainScript
&& !mWorkerRef
->Private()->GetParent());
660 nsCOMPtr
<nsIURI
> baseURI
= aIsMainScript
? GetInitialBaseURI() : GetBaseURI();
661 nsCOMPtr
<nsIURI
> uri
;
662 bool setErrorResult
= false;
664 ConstructURI(aScriptURL
, baseURI
, aDocumentEncoding
, getter_AddRefs(uri
));
665 // If we failed to construct the URI, handle it in the LoadContext so it is
666 // thrown in the right order.
667 if (NS_WARN_IF(NS_FAILED(rv
))) {
668 setErrorResult
= true;
669 loadContext
->mLoadResult
= rv
;
672 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-worker-script
673 // Step 2.5. Let script be the result [...] and the default classic script
676 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
677 // Step 1. Let options be a script fetch options whose cryptographic nonce is
678 // the empty string, integrity metadata is the empty string, parser metadata
679 // is "not-parser-inserted", credentials mode is credentials mode, referrer
680 // policy is the empty string, and fetch priority is "auto".
681 RefPtr
<ScriptFetchOptions
> fetchOptions
= new ScriptFetchOptions(
682 CORSMode::CORS_NONE
, /* aNonce = */ u
""_ns
, RequestPriority::Auto
,
683 ParserMetadata::NotParserInserted
, nullptr);
685 RefPtr
<ScriptLoadRequest
> request
= nullptr;
686 // Bug 1817259 - For now the debugger scripts are always loaded a Classic.
687 if (mWorkerRef
->Private()->WorkerType() == WorkerType::Classic
||
688 IsDebuggerScript()) {
689 request
= new ScriptLoadRequest(ScriptKind::eClassic
, uri
, referrerPolicy
,
690 fetchOptions
, SRIMetadata(),
691 nullptr, // mReferrer
694 // Implements part of "To fetch a worklet/module worker script graph"
695 // including, setting up the request with a credentials mode,
698 // Step 1. Let options be a script fetch options.
699 // We currently don't track credentials in our ScriptFetchOptions
700 // implementation, so we are defaulting the fetchOptions object defined
701 // above. This behavior is handled fully in GetModuleSecFlags.
703 if (!StaticPrefs::dom_workers_modules_enabled()) {
704 mRv
.ThrowTypeError("Modules in workers are currently disallowed.");
707 RefPtr
<WorkerModuleLoader::ModuleLoaderBase
> moduleLoader
=
708 GetGlobal()->GetModuleLoader(nullptr);
710 // Implements the referrer for "To fetch a single module script"
711 // Our implementation does not have a "client" as a referrer.
712 // However, when client is resolved (per 8.3. Determine request’s
714 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer)
715 // This should result in the referrer source being the creation URL.
717 // In subresource modules, the referrer is the importing script.
718 nsCOMPtr
<nsIURI
> referrer
=
719 mWorkerRef
->Private()->GetReferrerInfo()->GetOriginalReferrer();
721 // Part of Step 2. This sets the Top-level flag to true
722 request
= new ModuleLoadRequest(
723 uri
, referrerPolicy
, fetchOptions
, SRIMetadata(), referrer
, loadContext
,
724 true, /* is top level */
725 false, /* is dynamic import */
726 moduleLoader
, ModuleLoadRequest::NewVisitedSetForTopLevelImport(uri
),
730 // Set the mURL, it will be used for error handling and debugging.
731 request
->mURL
= NS_ConvertUTF16toUTF8(aScriptURL
);
733 if (setErrorResult
) {
734 request
->SetPendingFetchingError();
736 request
->NoCacheEntryFound();
739 return request
.forget();
742 bool WorkerScriptLoader::DispatchLoadScript(ScriptLoadRequest
* aRequest
) {
743 mWorkerRef
->Private()->AssertIsOnWorkerThread();
745 IncreaseLoadingModuleRequestCount();
747 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> scriptLoadList
;
748 RefPtr
<ThreadSafeRequestHandle
> handle
=
749 new ThreadSafeRequestHandle(aRequest
, mSyncLoopTarget
.get());
750 scriptLoadList
.AppendElement(handle
.forget());
752 RefPtr
<ScriptLoaderRunnable
> runnable
=
753 new ScriptLoaderRunnable(this, std::move(scriptLoadList
));
755 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
756 mWorkerRef
->Private(), "WorkerScriptLoader::DispatchLoadScript",
758 NS_DispatchToMainThread(NewRunnableMethod(
759 "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
761 &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted
));
764 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
765 NS_ERROR("Failed to dispatch!");
766 mRv
.Throw(NS_ERROR_FAILURE
);
772 bool WorkerScriptLoader::DispatchLoadScripts() {
773 mWorkerRef
->Private()->AssertIsOnWorkerThread();
775 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> scriptLoadList
= GetLoadingList();
777 RefPtr
<ScriptLoaderRunnable
> runnable
=
778 new ScriptLoaderRunnable(this, std::move(scriptLoadList
));
780 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
781 mWorkerRef
->Private(), "WorkerScriptLoader::DispatchLoadScripts",
783 NS_DispatchToMainThread(NewRunnableMethod(
784 "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
786 &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted
));
789 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
790 NS_ERROR("Failed to dispatch!");
791 mRv
.Throw(NS_ERROR_FAILURE
);
797 nsIURI
* WorkerScriptLoader::GetInitialBaseURI() {
798 MOZ_ASSERT(mWorkerRef
->Private());
800 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
802 baseURI
= parentWorker
->GetBaseURI();
805 baseURI
= mWorkerRef
->Private()->GetBaseURI();
811 nsIURI
* WorkerScriptLoader::GetBaseURI() const {
812 MOZ_ASSERT(mWorkerRef
);
814 baseURI
= mWorkerRef
->Private()->GetBaseURI();
815 NS_ASSERTION(baseURI
, "Should have been set already!");
820 nsIGlobalObject
* WorkerScriptLoader::GetGlobal() {
821 mWorkerRef
->Private()->AssertIsOnWorkerThread();
822 return mWorkerScriptType
== WorkerScript
823 ? static_cast<nsIGlobalObject
*>(
824 mWorkerRef
->Private()->GlobalScope())
825 : mWorkerRef
->Private()->DebuggerGlobalScope();
828 void WorkerScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest
* aRequest
) {
829 mWorkerRef
->Private()->AssertIsOnWorkerThread();
830 // Only set to ready for regular scripts. Module loader will set the script to
831 // ready if it is a Module Request.
832 if (!aRequest
->IsModuleRequest()) {
833 aRequest
->SetReady();
836 // If the request is not in a list, we are in an illegal state.
837 MOZ_RELEASE_ASSERT(aRequest
->isInList());
839 while (!mLoadingRequests
.isEmpty()) {
840 ScriptLoadRequest
* request
= mLoadingRequests
.getFirst();
841 // We need to move requests in post order. If prior requests have not
842 // completed, delay execution.
843 if (!request
->IsFinished()) {
847 RefPtr
<ScriptLoadRequest
> req
= mLoadingRequests
.Steal(request
);
848 mLoadedRequests
.AppendElement(req
);
852 bool WorkerScriptLoader::StoreCSP() {
853 // We must be on the same worker as we started on.
854 mWorkerRef
->Private()->AssertIsOnWorkerThread();
856 if (!mWorkerRef
->Private()->GetJSContext()) {
860 MOZ_ASSERT(!mRv
.Failed());
862 // Move the CSP from the workerLoadInfo in the corresponding Client
863 // where the CSP code expects it!
864 mWorkerRef
->Private()->StoreCSPOnClient();
868 bool WorkerScriptLoader::ProcessPendingRequests(JSContext
* aCx
) {
869 mWorkerRef
->Private()->AssertIsOnWorkerThread();
870 // Don't run if something else has already failed.
871 if (mExecutionAborted
) {
872 mLoadedRequests
.CancelRequestsAndClear();
877 // If nothing else has failed, our ErrorResult better not be a failure
879 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
881 // Slightly icky action at a distance, but there's no better place to stash
882 // this value, really.
883 JS::Rooted
<JSObject
*> global(aCx
, JS::CurrentGlobalOrNull(aCx
));
886 while (!mLoadedRequests
.isEmpty()) {
887 RefPtr
<ScriptLoadRequest
> req
= mLoadedRequests
.StealFirst();
888 // We don't have a ProcessRequest method (like we do on the DOM), as there
889 // isn't much processing that we need to do per request that isn't related
890 // to evaluation (the processsing done for the DOM is handled in
891 // DataRecievedFrom{Cache,Network} for workers.
892 // So, this inner loop calls EvaluateScript directly. This will change
893 // once modules are introduced as we will have some extra work to do.
894 if (!EvaluateScript(aCx
, req
)) {
896 mExecutionAborted
= true;
897 WorkerLoadContext
* loadContext
= req
->GetWorkerLoadContext();
898 mMutedErrorFlag
= loadContext
->mMutedErrorFlag
.valueOr(true);
899 mLoadedRequests
.CancelRequestsAndClear();
908 nsresult
WorkerScriptLoader::LoadScript(
909 ThreadSafeRequestHandle
* aRequestHandle
) {
910 AssertIsOnMainThread();
912 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
913 ScriptLoadRequest
* request
= aRequestHandle
->GetRequest();
914 MOZ_ASSERT_IF(loadContext
->IsTopLevel(), !IsDebuggerScript());
916 // The URL passed to us for loading was invalid, stop loading at this point.
917 if (loadContext
->mLoadResult
!= NS_ERROR_NOT_INITIALIZED
) {
918 return loadContext
->mLoadResult
;
921 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
923 // For JavaScript debugging, the devtools server must run on the same
924 // thread as the debuggee, indicating the worker uses content principal.
925 // However, in Bug 863246, web content will no longer be able to load
926 // resource:// URIs by default, so we need system principal to load
928 nsIPrincipal
* principal
= (IsDebuggerScript())
929 ? nsContentUtils::GetSystemPrincipal()
930 : mWorkerRef
->Private()->GetPrincipal();
932 nsCOMPtr
<nsILoadGroup
> loadGroup
= mWorkerRef
->Private()->GetLoadGroup();
933 MOZ_DIAGNOSTIC_ASSERT(principal
);
935 NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
),
939 nsCOMPtr
<Document
> parentDoc
= mWorkerRef
->Private()->GetDocument();
941 nsCOMPtr
<nsIChannel
> channel
;
942 if (loadContext
->IsTopLevel()) {
944 channel
= mWorkerRef
->Private()->ForgetWorkerChannel();
947 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
949 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
950 NS_ASSERTION(secMan
, "This should never be null!");
952 nsresult
& rv
= loadContext
->mLoadResult
;
954 nsLoadFlags loadFlags
= mWorkerRef
->Private()->GetLoadFlags();
956 // Get the top-level worker.
957 WorkerPrivate
* topWorkerPrivate
= mWorkerRef
->Private();
958 WorkerPrivate
* parent
= topWorkerPrivate
->GetParent();
960 topWorkerPrivate
= parent
;
961 parent
= topWorkerPrivate
->GetParent();
964 // If the top-level worker is a dedicated worker and has a window, and the
965 // window has a docshell, the caching behavior of this worker should match
966 // that of that docshell.
967 if (topWorkerPrivate
->IsDedicatedWorker()) {
968 nsCOMPtr
<nsPIDOMWindowInner
> window
= topWorkerPrivate
->GetWindow();
970 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
972 nsresult rv
= docShell
->GetDefaultLoadFlags(&loadFlags
);
973 NS_ENSURE_SUCCESS(rv
, rv
);
979 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
981 if (request
->IsModuleRequest()) {
982 // https://fetch.spec.whatwg.org/#concept-main-fetch
983 // Step 8. If request’s referrer policy is the empty string, then set
984 // request’s referrer policy to request’s policy container’s
986 ReferrerPolicy policy
=
987 request
->ReferrerPolicy() == ReferrerPolicy::_empty
988 ? mWorkerRef
->Private()->GetReferrerPolicy()
989 : request
->ReferrerPolicy();
991 referrerInfo
= new ReferrerInfo(request
->mReferrer
, policy
);
993 // https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
994 // The default classic script fetch options are a script fetch options
995 // whose ... credentials mode is "same-origin", ....
996 RequestCredentials credentials
=
997 mWorkerRef
->Private()->WorkerType() == WorkerType::Classic
998 ? RequestCredentials::Same_origin
999 : mWorkerRef
->Private()->WorkerCredentials();
1001 rv
= GetModuleSecFlags(loadContext
->IsTopLevel(), principal
,
1002 mWorkerScriptType
, request
->mURI
, credentials
,
1005 referrerInfo
= ReferrerInfo::CreateForFetch(principal
, nullptr);
1006 if (parentWorker
&& !loadContext
->IsTopLevel()) {
1008 static_cast<ReferrerInfo
*>(referrerInfo
.get())
1009 ->CloneWithNewPolicy(parentWorker
->GetReferrerPolicy());
1011 rv
= GetClassicSecFlags(loadContext
->IsTopLevel(), request
->mURI
,
1012 principal
, mWorkerScriptType
, secFlags
);
1015 if (NS_WARN_IF(NS_FAILED(rv
))) {
1019 nsContentPolicyType contentPolicyType
= GetContentPolicyType(request
);
1021 rv
= ChannelFromScriptURL(
1022 principal
, parentDoc
, mWorkerRef
->Private(), loadGroup
, ios
, secMan
,
1023 request
->mURI
, loadContext
->mClientInfo
, mController
,
1024 loadContext
->IsTopLevel(), mWorkerScriptType
, contentPolicyType
,
1025 loadFlags
, secFlags
, mWorkerRef
->Private()->CookieJarSettings(),
1026 referrerInfo
, getter_AddRefs(channel
));
1027 if (NS_WARN_IF(NS_FAILED(rv
))) {
1032 // Associate any originating stack with the channel.
1033 if (!mOriginStackJSON
.IsEmpty()) {
1034 NotifyNetworkMonitorAlternateStack(channel
, mOriginStackJSON
);
1037 // We need to know which index we're on in OnStreamComplete so we know
1038 // where to put the result.
1039 RefPtr
<NetworkLoadHandler
> listener
=
1040 new NetworkLoadHandler(this, aRequestHandle
);
1042 RefPtr
<ScriptResponseHeaderProcessor
> headerProcessor
= nullptr;
1044 // For each debugger script, a non-debugger script load of the same script
1045 // should have occured prior that processed the headers.
1046 if (!IsDebuggerScript()) {
1047 headerProcessor
= MakeRefPtr
<ScriptResponseHeaderProcessor
>(
1048 mWorkerRef
->Private(),
1049 loadContext
->IsTopLevel() && !IsDynamicImport(request
),
1050 GetContentPolicyType(request
) ==
1051 nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS
);
1054 nsCOMPtr
<nsIStreamLoader
> loader
;
1055 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), listener
, headerProcessor
);
1056 if (NS_WARN_IF(NS_FAILED(rv
))) {
1060 if (loadContext
->IsTopLevel()) {
1061 MOZ_DIAGNOSTIC_ASSERT(loadContext
->mClientInfo
.isSome());
1063 // In order to get the correct foreign partitioned prinicpal, we need to
1064 // set the `IsThirdPartyContextToTopWindow` to the channel's loadInfo.
1065 // This flag reflects the fact that if the worker is created under a
1066 // third-party context.
1067 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1068 loadInfo
->SetIsThirdPartyContextToTopWindow(
1069 mWorkerRef
->Private()->IsThirdPartyContextToTopWindow());
1071 Maybe
<ClientInfo
> clientInfo
;
1072 clientInfo
.emplace(loadContext
->mClientInfo
.ref());
1073 rv
= AddClientChannelHelper(channel
, std::move(clientInfo
),
1074 Maybe
<ClientInfo
>(),
1075 mWorkerRef
->Private()->HybridEventTarget());
1076 if (NS_WARN_IF(NS_FAILED(rv
))) {
1081 if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
1082 nsILoadInfo::CrossOriginEmbedderPolicy respectedCOEP
=
1083 mWorkerRef
->Private()->GetEmbedderPolicy();
1084 if (mWorkerRef
->Private()->IsDedicatedWorker() &&
1085 respectedCOEP
== nsILoadInfo::EMBEDDER_POLICY_NULL
) {
1086 respectedCOEP
= mWorkerRef
->Private()->GetOwnerEmbedderPolicy();
1089 nsCOMPtr
<nsILoadInfo
> channelLoadInfo
= channel
->LoadInfo();
1090 channelLoadInfo
->SetLoadingEmbedderPolicy(respectedCOEP
);
1093 if (loadContext
->mCacheStatus
!= WorkerLoadContext::ToBeCached
) {
1094 rv
= channel
->AsyncOpen(loader
);
1095 if (NS_WARN_IF(NS_FAILED(rv
))) {
1099 nsCOMPtr
<nsIOutputStream
> writer
;
1101 // In case we return early.
1102 loadContext
->mCacheStatus
= WorkerLoadContext::Cancel
;
1104 NS_NewPipe(getter_AddRefs(loadContext
->mCacheReadStream
),
1105 getter_AddRefs(writer
), 0,
1106 UINT32_MAX
, // unlimited size to avoid writer WOULD_BLOCK case
1107 true, false); // non-blocking reader, blocking writer
1109 nsCOMPtr
<nsIStreamListenerTee
> tee
=
1110 do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID
);
1111 rv
= tee
->Init(loader
, writer
, listener
);
1112 if (NS_WARN_IF(NS_FAILED(rv
))) {
1116 nsresult rv
= channel
->AsyncOpen(tee
);
1117 if (NS_WARN_IF(NS_FAILED(rv
))) {
1122 loadContext
->mChannel
.swap(channel
);
1127 nsresult
WorkerScriptLoader::FillCompileOptionsForRequest(
1128 JSContext
* cx
, ScriptLoadRequest
* aRequest
, JS::CompileOptions
* aOptions
,
1129 JS::MutableHandle
<JSScript
*> aIntroductionScript
) {
1130 // The full URL shouldn't be exposed to the debugger. See Bug 1634872
1131 aOptions
->setFileAndLine(aRequest
->mURL
.get(), 1);
1132 aOptions
->setNoScriptRval(true);
1134 aOptions
->setMutedErrors(
1135 aRequest
->GetWorkerLoadContext()->mMutedErrorFlag
.value());
1137 if (aRequest
->mSourceMapURL
) {
1138 aOptions
->setSourceMapURL(aRequest
->mSourceMapURL
->get());
1144 bool WorkerScriptLoader::EvaluateScript(JSContext
* aCx
,
1145 ScriptLoadRequest
* aRequest
) {
1146 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1147 MOZ_ASSERT(!IsDynamicImport(aRequest
));
1149 WorkerLoadContext
* loadContext
= aRequest
->GetWorkerLoadContext();
1151 NS_ASSERTION(!loadContext
->mChannel
, "Should no longer have a channel!");
1152 NS_ASSERTION(aRequest
->IsFinished(), "Should be scheduled!");
1154 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
1155 mRv
.MightThrowJSException();
1156 if (NS_FAILED(loadContext
->mLoadResult
)) {
1157 ReportErrorToConsole(aRequest
, loadContext
->mLoadResult
);
1161 // If this is a top level script that succeeded, then mark the
1162 // Client execution ready and possible controlled by a service worker.
1163 if (loadContext
->IsTopLevel()) {
1164 if (mController
.isSome()) {
1165 MOZ_ASSERT(mWorkerScriptType
== WorkerScript
,
1166 "Debugger clients can't be controlled.");
1167 mWorkerRef
->Private()->GlobalScope()->Control(mController
.ref());
1169 mWorkerRef
->Private()->ExecutionReady();
1172 if (aRequest
->IsModuleRequest()) {
1173 // Only the top level module of the module graph will be executed from here,
1174 // the rest will be executed from SpiderMonkey as part of the execution of
1175 // the module graph.
1176 MOZ_ASSERT(aRequest
->IsTopLevel());
1177 ModuleLoadRequest
* request
= aRequest
->AsModuleRequest();
1178 if (!request
->mModuleScript
) {
1182 // https://html.spec.whatwg.org/#run-a-worker
1183 // if script's error to rethrow is non-null, then:
1184 // Queue a global task on the DOM manipulation task source given worker's
1185 // relevant global object to fire an event named error at worker.
1187 // The event will be dispatched in CompileScriptRunnable.
1188 if (request
->mModuleScript
->HasParseError()) {
1189 // Here we assign an error code that is not a JS Exception, so
1190 // CompileRunnable can dispatch the event.
1191 mRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1195 // Implements To fetch a worklet/module worker script graph
1196 // Step 5. Fetch the descendants of and link result.
1197 if (!request
->InstantiateModuleGraph()) {
1201 if (request
->mModuleScript
->HasErrorToRethrow()) {
1202 // See the comments when we check HasParseError() above.
1203 mRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1207 nsresult rv
= request
->EvaluateModule();
1208 return NS_SUCCEEDED(rv
);
1211 JS::CompileOptions
options(aCx
);
1212 // The introduction script is used by the DOM script loader as a way
1213 // to fill the Debugger Metadata for the JS Execution context. We don't use
1214 // the JS Execution context as we are not making use of async compilation
1215 // (delegation to another worker to produce bytecode or compile a string to a
1216 // JSScript), so it is not used in this context.
1217 JS::Rooted
<JSScript
*> unusedIntroductionScript(aCx
);
1218 nsresult rv
= FillCompileOptionsForRequest(aCx
, aRequest
, &options
,
1219 &unusedIntroductionScript
);
1221 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Filling compile options should not fail");
1223 // Our ErrorResult still shouldn't be a failure.
1224 MOZ_ASSERT(!mRv
.Failed(), "Who failed it and why?");
1226 // Get the source text.
1227 ScriptLoadRequest::MaybeSourceText maybeSource
;
1228 rv
= aRequest
->GetScriptSource(aCx
, &maybeSource
,
1229 aRequest
->mLoadContext
.get());
1230 if (NS_FAILED(rv
)) {
1231 mRv
.StealExceptionFromJSContext(aCx
);
1235 RefPtr
<JS::loader::ClassicScript
> classicScript
= nullptr;
1236 if (StaticPrefs::dom_workers_modules_enabled() &&
1237 !mWorkerRef
->Private()->IsServiceWorker()) {
1238 // We need a LoadedScript to be associated with the JSScript in order to
1239 // correctly resolve the referencing private for dynamic imports. In turn
1240 // this allows us to correctly resolve the BaseURL.
1242 // Dynamic import is disallowed on service workers. Additionally, causes
1243 // crashes because the life cycle isn't completed for service workers. To
1244 // keep things simple, we don't create a classic script for ServiceWorkers.
1245 // If this changes then we will need to ensure that the reference that is
1246 // held is released appropriately.
1247 nsCOMPtr
<nsIURI
> requestBaseURI
;
1248 if (loadContext
->mMutedErrorFlag
.valueOr(false)) {
1249 NS_NewURI(getter_AddRefs(requestBaseURI
), "about:blank"_ns
);
1251 requestBaseURI
= aRequest
->mBaseURL
;
1253 MOZ_ASSERT(aRequest
->mLoadedScript
->IsClassicScript());
1254 MOZ_ASSERT(aRequest
->mLoadedScript
->GetFetchOptions() ==
1255 aRequest
->mFetchOptions
);
1256 aRequest
->mLoadedScript
->SetBaseURL(requestBaseURI
);
1257 classicScript
= aRequest
->mLoadedScript
->AsClassicScript();
1260 JS::Rooted
<JSScript
*> script(aCx
);
1261 script
= aRequest
->IsUTF8Text()
1262 ? JS::Compile(aCx
, options
,
1263 maybeSource
.ref
<JS::SourceText
<Utf8Unit
>>())
1264 : JS::Compile(aCx
, options
,
1265 maybeSource
.ref
<JS::SourceText
<char16_t
>>());
1267 if (loadContext
->IsTopLevel()) {
1268 // This is a top-level worker script,
1270 // https://html.spec.whatwg.org/#run-a-worker
1271 // If script is null or if script's error to rethrow is non-null, then:
1272 // Queue a global task on the DOM manipulation task source given
1273 // worker's relevant global object to fire an event named error at
1275 JS_ClearPendingException(aCx
);
1276 mRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1278 // This is a script which is loaded by importScripts().
1280 // https://html.spec.whatwg.org/#import-scripts-into-worker-global-scope
1281 // For each url in the resulting URL records:
1282 // Fetch a classic worker-imported script given url and settings object,
1283 // passing along performFetch if provided. If this succeeds, let script
1284 // be the result. Otherwise, rethrow the exception.
1285 mRv
.StealExceptionFromJSContext(aCx
);
1291 bool successfullyEvaluated
= EvaluateSourceBuffer(aCx
, script
, classicScript
);
1292 if (aRequest
->IsCanceled()) {
1295 if (!successfullyEvaluated
) {
1296 mRv
.StealExceptionFromJSContext(aCx
);
1299 // steal the loadContext so that the cycle is broken and cycle collector can
1300 // collect the scriptLoadRequest.
1304 void WorkerScriptLoader::TryShutdown() {
1306 MutexAutoLock
lock(CleanUpLock());
1312 if (AllScriptsExecuted() && AllModuleRequestsLoaded()) {
1313 ShutdownScriptLoader(!mExecutionAborted
, mMutedErrorFlag
);
1317 void WorkerScriptLoader::ShutdownScriptLoader(bool aResult
, bool aMutedError
) {
1318 MOZ_ASSERT(AllScriptsExecuted());
1319 MOZ_ASSERT(AllModuleRequestsLoaded());
1320 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1323 // At this point there are two possibilities:
1325 // 1) mRv.Failed(). In that case we just want to leave it
1326 // as-is, except if it has a JS exception and we need to mute JS
1327 // exceptions. In that case, we log the exception without firing any
1328 // events and then replace it on the ErrorResult with a NetworkError,
1331 // 2) mRv succeeded. As far as I can tell, this can only
1332 // happen when loading the main worker script and
1333 // GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
1334 // got called. Does it matter what we throw in this case? I'm not
1337 if (aMutedError
&& mRv
.IsJSException()) {
1338 LogExceptionToConsole(mWorkerRef
->Private()->GetJSContext(),
1339 mWorkerRef
->Private());
1340 mRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
1343 mRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1347 // Lock, shutdown, and cleanup state. After this the Loader is closed.
1349 MutexAutoLock
lock(CleanUpLock());
1355 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1356 // Module loader doesn't use sync loop for dynamic import
1357 if (mSyncLoopTarget
) {
1358 mWorkerRef
->Private()->MaybeStopSyncLoop(
1359 mSyncLoopTarget
, aResult
? NS_OK
: NS_ERROR_FAILURE
);
1360 mSyncLoopTarget
= nullptr;
1366 // Allow worker shutdown.
1367 mWorkerRef
= nullptr;
1371 void WorkerScriptLoader::ReportErrorToConsole(ScriptLoadRequest
* aRequest
,
1372 nsresult aResult
) const {
1373 nsAutoString url
= NS_ConvertUTF8toUTF16(aRequest
->mURL
);
1374 workerinternals::ReportLoadError(mRv
, aResult
, url
);
1377 void WorkerScriptLoader::LogExceptionToConsole(JSContext
* aCx
,
1378 WorkerPrivate
* aWorkerPrivate
) {
1379 aWorkerPrivate
->AssertIsOnWorkerThread();
1381 MOZ_ASSERT(mRv
.IsJSException());
1383 JS::Rooted
<JS::Value
> exn(aCx
);
1384 if (!ToJSValue(aCx
, std::move(mRv
), &exn
)) {
1388 // Now the exception state should all be in exn.
1389 MOZ_ASSERT(!JS_IsExceptionPending(aCx
));
1390 MOZ_ASSERT(!mRv
.Failed());
1392 JS::ExceptionStack
exnStack(aCx
, exn
, nullptr);
1393 JS::ErrorReportBuilder
report(aCx
);
1394 if (!report
.init(aCx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
1395 JS_ClearPendingException(aCx
);
1399 RefPtr
<xpc::ErrorReport
> xpcReport
= new xpc::ErrorReport();
1400 xpcReport
->Init(report
.report(), report
.toStringResult().c_str(),
1401 aWorkerPrivate
->IsChromeWorker(), aWorkerPrivate
->WindowID());
1403 RefPtr
<AsyncErrorReporter
> r
= new AsyncErrorReporter(xpcReport
);
1404 NS_DispatchToMainThread(r
);
1407 bool WorkerScriptLoader::AllModuleRequestsLoaded() const {
1408 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1409 return mLoadingModuleRequestCount
== 0;
1412 void WorkerScriptLoader::IncreaseLoadingModuleRequestCount() {
1413 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1414 ++mLoadingModuleRequestCount
;
1417 void WorkerScriptLoader::DecreaseLoadingModuleRequestCount() {
1418 mWorkerRef
->Private()->AssertIsOnWorkerThread();
1419 --mLoadingModuleRequestCount
;
1422 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable
, nsIRunnable
, nsINamed
)
1424 NS_IMPL_ISUPPORTS(WorkerScriptLoader
, nsINamed
)
1426 ScriptLoaderRunnable::ScriptLoaderRunnable(
1427 WorkerScriptLoader
* aScriptLoader
,
1428 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> aLoadingRequests
)
1429 : mScriptLoader(aScriptLoader
),
1430 mWorkerRef(aScriptLoader
->mWorkerRef
),
1431 mLoadingRequests(std::move(aLoadingRequests
)),
1432 mCancelMainThread(Nothing()) {
1433 MOZ_ASSERT(aScriptLoader
);
1436 nsresult
ScriptLoaderRunnable::Run() {
1437 AssertIsOnMainThread();
1439 // Convert the origin stack to JSON (which must be done on the main
1440 // thread) explicitly, so that we can use the stack to notify the net
1441 // monitor about every script we load. We do this, rather than pass
1442 // the stack directly to the netmonitor, in order to be able to use this
1443 // for all subsequent scripts.
1444 if (mScriptLoader
->mOriginStack
&&
1445 mScriptLoader
->mOriginStackJSON
.IsEmpty()) {
1446 ConvertSerializedStackToJSON(std::move(mScriptLoader
->mOriginStack
),
1447 mScriptLoader
->mOriginStackJSON
);
1450 if (!mWorkerRef
->Private()->IsServiceWorker() ||
1451 mScriptLoader
->IsDebuggerScript()) {
1452 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1453 handle
->mRunnable
= this;
1456 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1457 nsresult rv
= mScriptLoader
->LoadScript(handle
);
1458 if (NS_WARN_IF(NS_FAILED(rv
))) {
1459 LoadingFinished(handle
, rv
);
1460 CancelMainThread(rv
);
1468 MOZ_ASSERT(!mCacheCreator
);
1469 mCacheCreator
= new CacheCreator(mWorkerRef
->Private());
1471 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1472 handle
->mRunnable
= this;
1473 WorkerLoadContext
* loadContext
= handle
->GetContext();
1474 mCacheCreator
->AddLoader(MakeNotNull
<RefPtr
<CacheLoadHandler
>>(
1475 mWorkerRef
, handle
, loadContext
->IsTopLevel(),
1476 loadContext
->mOnlyExistingCachedResourcesAllowed
, mScriptLoader
));
1479 // The worker may have a null principal on first load, but in that case its
1480 // parent definitely will have one.
1481 nsIPrincipal
* principal
= mWorkerRef
->Private()->GetPrincipal();
1483 WorkerPrivate
* parentWorker
= mWorkerRef
->Private()->GetParent();
1484 MOZ_ASSERT(parentWorker
, "Must have a parent!");
1485 principal
= parentWorker
->GetPrincipal();
1488 nsresult rv
= mCacheCreator
->Load(principal
);
1489 if (NS_WARN_IF(NS_FAILED(rv
))) {
1490 CancelMainThread(rv
);
1497 nsresult
ScriptLoaderRunnable::OnStreamComplete(
1498 ThreadSafeRequestHandle
* aRequestHandle
, nsresult aStatus
) {
1499 AssertIsOnMainThread();
1501 LoadingFinished(aRequestHandle
, aStatus
);
1505 void ScriptLoaderRunnable::LoadingFinished(
1506 ThreadSafeRequestHandle
* aRequestHandle
, nsresult aRv
) {
1507 AssertIsOnMainThread();
1509 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
1511 loadContext
->mLoadResult
= aRv
;
1512 MOZ_ASSERT(!loadContext
->mLoadingFinished
);
1513 loadContext
->mLoadingFinished
= true;
1515 if (loadContext
->IsTopLevel() && NS_SUCCEEDED(aRv
)) {
1516 MOZ_DIAGNOSTIC_ASSERT(
1517 mWorkerRef
->Private()->PrincipalURIMatchesScriptURL());
1520 MaybeExecuteFinishedScripts(aRequestHandle
);
1523 void ScriptLoaderRunnable::MaybeExecuteFinishedScripts(
1524 ThreadSafeRequestHandle
* aRequestHandle
) {
1525 AssertIsOnMainThread();
1527 // We execute the last step if we don't have a pending operation with the
1528 // cache and the loading is completed.
1529 WorkerLoadContext
* loadContext
= aRequestHandle
->GetContext();
1530 if (!loadContext
->IsAwaitingPromise()) {
1531 if (aRequestHandle
->GetContext()->IsTopLevel()) {
1532 mWorkerRef
->Private()->WorkerScriptLoaded();
1534 DispatchProcessPendingRequests();
1538 void ScriptLoaderRunnable::CancelMainThreadWithBindingAborted() {
1539 AssertIsOnMainThread();
1540 CancelMainThread(NS_BINDING_ABORTED
);
1543 void ScriptLoaderRunnable::CancelMainThread(nsresult aCancelResult
) {
1544 AssertIsOnMainThread();
1545 if (IsCancelled()) {
1550 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1552 // Check if we have already cancelled, or if the worker has been killed
1553 // before we cancel.
1554 if (mScriptLoader
->CleanedUp()) {
1558 mCancelMainThread
= Some(aCancelResult
);
1560 for (ThreadSafeRequestHandle
* handle
: mLoadingRequests
) {
1561 if (handle
->IsEmpty()) {
1565 bool callLoadingFinished
= true;
1567 WorkerLoadContext
* loadContext
= handle
->GetContext();
1572 if (loadContext
->IsAwaitingPromise()) {
1573 MOZ_ASSERT(mWorkerRef
->Private()->IsServiceWorker());
1574 loadContext
->mCachePromise
->MaybeReject(NS_BINDING_ABORTED
);
1575 loadContext
->mCachePromise
= nullptr;
1576 callLoadingFinished
= false;
1578 if (loadContext
->mChannel
) {
1579 if (NS_SUCCEEDED(loadContext
->mChannel
->Cancel(aCancelResult
))) {
1580 callLoadingFinished
= false;
1582 NS_WARNING("Failed to cancel channel!");
1585 if (callLoadingFinished
&& !loadContext
->mLoadingFinished
) {
1586 LoadingFinished(handle
, aCancelResult
);
1589 DispatchProcessPendingRequests();
1593 void ScriptLoaderRunnable::DispatchProcessPendingRequests() {
1594 AssertIsOnMainThread();
1596 const auto begin
= mLoadingRequests
.begin();
1597 const auto end
= mLoadingRequests
.end();
1598 using Iterator
= decltype(begin
);
1599 const auto maybeRangeToExecute
=
1600 [begin
, end
]() -> Maybe
<std::pair
<Iterator
, Iterator
>> {
1601 // firstItToExecute is the first loadInfo where mExecutionScheduled is
1603 auto firstItToExecute
= std::find_if(
1604 begin
, end
, [](const RefPtr
<ThreadSafeRequestHandle
>& requestHandle
) {
1605 return !requestHandle
->mExecutionScheduled
;
1608 if (firstItToExecute
== end
) {
1612 // firstItUnexecutable is the first loadInfo that is not yet finished.
1613 // Update mExecutionScheduled on the ones we're about to schedule for
1615 const auto firstItUnexecutable
=
1616 std::find_if(firstItToExecute
, end
,
1617 [](RefPtr
<ThreadSafeRequestHandle
>& requestHandle
) {
1618 MOZ_ASSERT(!requestHandle
->IsEmpty());
1619 if (!requestHandle
->Finished()) {
1623 // We can execute this one.
1624 requestHandle
->mExecutionScheduled
= true;
1629 return firstItUnexecutable
== firstItToExecute
1631 : Some(std::pair(firstItToExecute
, firstItUnexecutable
));
1634 // If there are no unexecutable load infos, we can unuse things before the
1635 // execution of the scripts and the stopping of the sync loop.
1636 if (maybeRangeToExecute
) {
1637 if (maybeRangeToExecute
->second
== end
) {
1638 mCacheCreator
= nullptr;
1641 RefPtr
<ScriptExecutorRunnable
> runnable
= new ScriptExecutorRunnable(
1642 mScriptLoader
, mWorkerRef
->Private(), mScriptLoader
->mSyncLoopTarget
,
1643 Span
<RefPtr
<ThreadSafeRequestHandle
>>{maybeRangeToExecute
->first
,
1644 maybeRangeToExecute
->second
});
1646 if (!runnable
->Dispatch() && mScriptLoader
->mSyncLoopTarget
) {
1647 MOZ_ASSERT(false, "This should never fail!");
1652 ScriptExecutorRunnable::ScriptExecutorRunnable(
1653 WorkerScriptLoader
* aScriptLoader
, WorkerPrivate
* aWorkerPrivate
,
1654 nsISerialEventTarget
* aSyncLoopTarget
,
1655 Span
<RefPtr
<ThreadSafeRequestHandle
>> aLoadedRequests
)
1656 : MainThreadWorkerSyncRunnable(aWorkerPrivate
, aSyncLoopTarget
,
1657 "ScriptExecutorRunnable"),
1658 mScriptLoader(aScriptLoader
),
1659 mLoadedRequests(aLoadedRequests
) {}
1661 bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
1662 // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
1663 // In the latter case, the runnable needs to be dispatched to the debugger
1665 return mScriptLoader
->IsDebuggerScript();
1668 bool ScriptExecutorRunnable::PreRun(WorkerPrivate
* aWorkerPrivate
) {
1669 aWorkerPrivate
->AssertIsOnWorkerThread();
1671 // We must be on the same worker as we started on.
1673 mScriptLoader
->mSyncLoopTarget
== mSyncLoopTarget
,
1674 "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
1677 // There is a possibility that we cleaned up while this task was waiting to
1678 // run. If this has happened, return and exit.
1679 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1680 if (mScriptLoader
->CleanedUp()) {
1684 const auto& requestHandle
= mLoadedRequests
[0];
1685 // Check if the request is still valid.
1686 if (requestHandle
->IsEmpty() ||
1687 !requestHandle
->GetContext()->IsTopLevel()) {
1692 return mScriptLoader
->StoreCSP();
1695 bool ScriptExecutorRunnable::ProcessModuleScript(
1696 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1697 // We should only ever have one script when processing modules
1698 MOZ_ASSERT(mLoadedRequests
.Length() == 1);
1699 RefPtr
<ScriptLoadRequest
> request
;
1701 // There is a possibility that we cleaned up while this task was waiting to
1702 // run. If this has happened, return and exit.
1703 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1704 if (mScriptLoader
->CleanedUp()) {
1708 MOZ_ASSERT(mLoadedRequests
.Length() == 1);
1709 const auto& requestHandle
= mLoadedRequests
[0];
1710 // The request must be valid.
1711 MOZ_ASSERT(!requestHandle
->IsEmpty());
1713 // Release the request to the worker. From this point on, the Request Handle
1715 request
= requestHandle
->ReleaseRequest();
1717 // release lock. We will need it later if we cleanup.
1720 MOZ_ASSERT(request
->IsModuleRequest());
1722 WorkerLoadContext
* loadContext
= request
->GetWorkerLoadContext();
1723 ModuleLoadRequest
* moduleRequest
= request
->AsModuleRequest();
1725 // DecreaseLoadingModuleRequestCount must be called before OnFetchComplete.
1726 // OnFetchComplete will call ProcessPendingRequests, and in
1727 // ProcessPendingRequests it will try to shutdown if
1728 // AllModuleRequestsLoaded() returns true.
1729 mScriptLoader
->DecreaseLoadingModuleRequestCount();
1730 moduleRequest
->OnFetchComplete(loadContext
->mLoadResult
);
1732 if (NS_FAILED(loadContext
->mLoadResult
)) {
1733 if (moduleRequest
->IsDynamicImport()) {
1734 if (request
->isInList()) {
1735 moduleRequest
->CancelDynamicImport(loadContext
->mLoadResult
);
1736 mScriptLoader
->TryShutdown();
1738 } else if (!moduleRequest
->IsTopLevel()) {
1739 moduleRequest
->Cancel();
1740 mScriptLoader
->TryShutdown();
1742 moduleRequest
->LoadFailed();
1748 bool ScriptExecutorRunnable::ProcessClassicScripts(
1749 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1750 // There is a possibility that we cleaned up while this task was waiting to
1751 // run. If this has happened, return and exit.
1753 MutexAutoLock
lock(mScriptLoader
->CleanUpLock());
1754 if (mScriptLoader
->CleanedUp()) {
1758 for (const auto& requestHandle
: mLoadedRequests
) {
1759 // The request must be valid.
1760 MOZ_ASSERT(!requestHandle
->IsEmpty());
1762 // Release the request to the worker. From this point on, the Request
1764 RefPtr
<ScriptLoadRequest
> request
= requestHandle
->ReleaseRequest();
1765 mScriptLoader
->MaybeMoveToLoadedList(request
);
1768 return mScriptLoader
->ProcessPendingRequests(aCx
);
1771 bool ScriptExecutorRunnable::WorkerRun(JSContext
* aCx
,
1772 WorkerPrivate
* aWorkerPrivate
) {
1773 aWorkerPrivate
->AssertIsOnWorkerThread();
1775 // We must be on the same worker as we started on.
1777 mScriptLoader
->mSyncLoopTarget
== mSyncLoopTarget
,
1778 "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
1780 if (mLoadedRequests
.begin()->get()->GetRequest()->IsModuleRequest()) {
1781 return ProcessModuleScript(aCx
, aWorkerPrivate
);
1784 return ProcessClassicScripts(aCx
, aWorkerPrivate
);
1787 nsresult
ScriptExecutorRunnable::Cancel() {
1788 if (mScriptLoader
->AllScriptsExecuted() &&
1789 mScriptLoader
->AllModuleRequestsLoaded()) {
1790 mScriptLoader
->ShutdownScriptLoader(false, false);
1795 } /* namespace loader */
1797 nsresult
ChannelFromScriptURLMainThread(
1798 nsIPrincipal
* aPrincipal
, Document
* aParentDoc
, nsILoadGroup
* aLoadGroup
,
1799 nsIURI
* aScriptURL
, const WorkerType
& aWorkerType
,
1800 const RequestCredentials
& aCredentials
,
1801 const Maybe
<ClientInfo
>& aClientInfo
,
1802 nsContentPolicyType aMainScriptContentPolicyType
,
1803 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
1804 nsIChannel
** aChannel
) {
1805 AssertIsOnMainThread();
1807 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
1809 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
1810 NS_ASSERTION(secMan
, "This should never be null!");
1814 if (aWorkerType
== WorkerType::Module
) {
1815 rv
= GetModuleSecFlags(true, aPrincipal
, WorkerScript
, aScriptURL
,
1816 aCredentials
, secFlags
);
1818 rv
= GetClassicSecFlags(true, aScriptURL
, aPrincipal
, WorkerScript
,
1821 if (NS_FAILED(rv
)) {
1825 return ChannelFromScriptURL(
1826 aPrincipal
, aParentDoc
, nullptr, aLoadGroup
, ios
, secMan
, aScriptURL
,
1827 aClientInfo
, Maybe
<ServiceWorkerDescriptor
>(), true, WorkerScript
,
1828 aMainScriptContentPolicyType
, nsIRequest::LOAD_NORMAL
, secFlags
,
1829 aCookieJarSettings
, aReferrerInfo
, aChannel
);
1832 nsresult
ChannelFromScriptURLWorkerThread(
1833 JSContext
* aCx
, WorkerPrivate
* aParent
, const nsAString
& aScriptURL
,
1834 const WorkerType
& aWorkerType
, const RequestCredentials
& aCredentials
,
1835 WorkerLoadInfo
& aLoadInfo
) {
1836 aParent
->AssertIsOnWorkerThread();
1838 RefPtr
<ChannelGetterRunnable
> getter
= new ChannelGetterRunnable(
1839 aParent
, aScriptURL
, aWorkerType
, aCredentials
, aLoadInfo
);
1842 getter
->Dispatch(Canceling
, rv
);
1844 NS_ERROR("Failed to dispatch!");
1845 return rv
.StealNSResult();
1848 return getter
->GetResult();
1851 void ReportLoadError(ErrorResult
& aRv
, nsresult aLoadResult
,
1852 const nsAString
& aScriptURL
) {
1853 MOZ_ASSERT(!aRv
.Failed());
1855 nsPrintfCString
err("Failed to load worker script at \"%s\"",
1856 NS_ConvertUTF16toUTF8(aScriptURL
).get());
1858 switch (aLoadResult
) {
1859 case NS_ERROR_FILE_NOT_FOUND
:
1860 case NS_ERROR_NOT_AVAILABLE
:
1861 case NS_ERROR_CORRUPTED_CONTENT
:
1862 aRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
1865 case NS_ERROR_MALFORMED_URI
:
1866 case NS_ERROR_DOM_SYNTAX_ERR
:
1867 aRv
.ThrowSyntaxError(err
);
1870 case NS_BINDING_ABORTED
:
1871 // Note: we used to pretend like we didn't set an exception for
1872 // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway. The
1873 // other callsite, in WorkerPrivate::Constructor, never passed in
1874 // NS_BINDING_ABORTED. So just throw it directly here. Consumers will
1875 // deal as needed. But note that we do NOT want to use one of the
1876 // Throw*Error() methods on ErrorResult for this case, because that will
1877 // make it impossible for consumers to realize that our error was
1878 // NS_BINDING_ABORTED.
1879 aRv
.Throw(aLoadResult
);
1882 case NS_ERROR_DOM_BAD_URI
:
1883 // This is actually a security error.
1884 case NS_ERROR_DOM_SECURITY_ERR
:
1885 aRv
.ThrowSecurityError(err
);
1889 // For lack of anything better, go ahead and throw a NetworkError here.
1890 // We don't want to throw a JS exception, because for toplevel script
1891 // loads that would get squelched.
1892 aRv
.ThrowNetworkError(nsPrintfCString(
1893 "Failed to load worker script at %s (nsresult = 0x%" PRIx32
")",
1894 NS_ConvertUTF16toUTF8(aScriptURL
).get(),
1895 static_cast<uint32_t>(aLoadResult
)));
1900 void LoadMainScript(WorkerPrivate
* aWorkerPrivate
,
1901 UniquePtr
<SerializedStackHolder
> aOriginStack
,
1902 const nsAString
& aScriptURL
,
1903 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
,
1904 const mozilla::Encoding
* aDocumentEncoding
) {
1905 nsTArray
<nsString
> scriptURLs
;
1907 scriptURLs
.AppendElement(aScriptURL
);
1909 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), scriptURLs
, true,
1910 aWorkerScriptType
, aRv
, aDocumentEncoding
);
1913 void Load(WorkerPrivate
* aWorkerPrivate
,
1914 UniquePtr
<SerializedStackHolder
> aOriginStack
,
1915 const nsTArray
<nsString
>& aScriptURLs
,
1916 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
) {
1917 const uint32_t urlCount
= aScriptURLs
.Length();
1923 if (urlCount
> MAX_CONCURRENT_SCRIPTS
) {
1924 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1928 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), aScriptURLs
, false,
1929 aWorkerScriptType
, aRv
);
1932 } // namespace mozilla::dom::workerinternals