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 "nsIInputStreamPump.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 "nsIStreamLoader.h"
27 #include "nsIStreamListenerTee.h"
28 #include "nsIThreadRetargetableRequest.h"
30 #include "nsIXPConnect.h"
33 #include "jsfriendapi.h"
34 #include "js/CompilationAndEvaluation.h"
35 #include "js/Exception.h"
36 #include "js/SourceText.h"
38 #include "nsComponentManagerUtils.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"
48 #include "nsStreamUtils.h"
50 #include "nsThreadUtils.h"
52 #include "xpcpublic.h"
54 #include "mozilla/ArrayAlgorithm.h"
55 #include "mozilla/Assertions.h"
56 #include "mozilla/LoadContext.h"
57 #include "mozilla/Maybe.h"
58 #include "mozilla/ipc/BackgroundUtils.h"
59 #include "mozilla/dom/BlobURLProtocolHandler.h"
60 #include "mozilla/dom/CacheBinding.h"
61 #include "mozilla/dom/cache/CacheTypes.h"
62 #include "mozilla/dom/cache/Cache.h"
63 #include "mozilla/dom/cache/CacheStorage.h"
64 #include "mozilla/dom/ChannelInfo.h"
65 #include "mozilla/dom/ClientChannelHelper.h"
66 #include "mozilla/dom/ClientInfo.h"
67 #include "mozilla/dom/Exceptions.h"
68 #include "mozilla/dom/InternalResponse.h"
69 #include "mozilla/dom/nsCSPService.h"
70 #include "mozilla/dom/nsCSPUtils.h"
71 #include "mozilla/dom/PerformanceStorage.h"
72 #include "mozilla/dom/Promise.h"
73 #include "mozilla/dom/PromiseNativeHandler.h"
74 #include "mozilla/dom/Response.h"
75 #include "mozilla/dom/ScriptLoader.h"
76 #include "mozilla/dom/ScriptSettings.h"
77 #include "mozilla/dom/SerializedStackHolder.h"
78 #include "mozilla/dom/SRILogHelper.h"
79 #include "mozilla/dom/SerializedStackHolder.h"
80 #include "mozilla/dom/ServiceWorkerBinding.h"
81 #include "mozilla/dom/ServiceWorkerManager.h"
82 #include "mozilla/Result.h"
83 #include "mozilla/ResultExtensions.h"
84 #include "mozilla/StaticPrefs_browser.h"
85 #include "mozilla/StaticPrefs_dom.h"
86 #include "mozilla/StaticPrefs_security.h"
87 #include "mozilla/UniquePtr.h"
88 #include "Principal.h"
89 #include "WorkerPrivate.h"
90 #include "WorkerRunnable.h"
91 #include "WorkerScope.h"
93 #define MAX_CONCURRENT_SCRIPTS 1000
95 using mozilla::dom::cache::Cache
;
96 using mozilla::dom::cache::CacheStorage
;
97 using mozilla::ipc::PrincipalInfo
;
104 nsIURI
* GetBaseURI(bool aIsMainScript
, WorkerPrivate
* aWorkerPrivate
) {
105 MOZ_ASSERT(aWorkerPrivate
);
107 WorkerPrivate
* parentWorker
= aWorkerPrivate
->GetParent();
110 baseURI
= parentWorker
->GetBaseURI();
111 NS_ASSERTION(baseURI
, "Should have been set already!");
114 baseURI
= aWorkerPrivate
->GetBaseURI();
117 baseURI
= aWorkerPrivate
->GetBaseURI();
118 NS_ASSERTION(baseURI
, "Should have been set already!");
124 nsresult
ConstructURI(const nsAString
& aScriptURL
, nsIURI
* baseURI
,
125 Document
* parentDoc
, bool aDefaultURIEncoding
,
128 if (aDefaultURIEncoding
) {
129 rv
= NS_NewURI(aResult
, aScriptURL
, nullptr, baseURI
);
131 rv
= nsContentUtils::NewURIWithDocumentCharset(aResult
, aScriptURL
,
136 return NS_ERROR_DOM_SYNTAX_ERR
;
141 nsresult
ChannelFromScriptURL(
142 nsIPrincipal
* principal
, Document
* parentDoc
, WorkerPrivate
* aWorkerPrivate
,
143 nsILoadGroup
* loadGroup
, nsIIOService
* ios
,
144 nsIScriptSecurityManager
* secMan
, nsIURI
* aScriptURL
,
145 const Maybe
<ClientInfo
>& aClientInfo
,
146 const Maybe
<ServiceWorkerDescriptor
>& aController
, bool aIsMainScript
,
147 WorkerScriptType aWorkerScriptType
,
148 nsContentPolicyType aMainScriptContentPolicyType
, nsLoadFlags aLoadFlags
,
149 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
150 nsIChannel
** aChannel
) {
151 AssertIsOnMainThread();
154 nsCOMPtr
<nsIURI
> uri
= aScriptURL
;
156 // If we have the document, use it. Unfortunately, for dedicated workers
157 // 'parentDoc' ends up being the parent document, which is not the document
158 // that we want to use. So make sure to avoid using 'parentDoc' in that
160 if (parentDoc
&& parentDoc
->NodePrincipal() != principal
) {
165 aIsMainScript
? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
166 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
168 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
169 principal
, uri
, true /* aInheritForAboutBlank */,
170 false /* aForceInherit */);
172 bool isData
= uri
->SchemeIs("data");
173 if (inheritAttrs
&& !isData
) {
174 secFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
177 if (aWorkerScriptType
== DebuggerScript
) {
178 // A DebuggerScript needs to be a local resource like chrome: or resource:
179 bool isUIResource
= false;
180 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
,
182 if (NS_WARN_IF(NS_FAILED(rv
))) {
187 return NS_ERROR_DOM_SECURITY_ERR
;
190 secFlags
|= nsILoadInfo::SEC_ALLOW_CHROME
;
193 // Note: this is for backwards compatibility and goes against spec.
194 // We should find a better solution.
195 if (aIsMainScript
&& isData
) {
196 secFlags
= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
199 nsContentPolicyType contentPolicyType
=
200 aIsMainScript
? aMainScriptContentPolicyType
201 : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS
;
203 // The main service worker script should never be loaded over the network
204 // in this path. It should always be offlined by ServiceWorkerScriptCache.
205 // We assert here since this error should also be caught by the runtime
206 // check in CacheScriptLoader.
208 // Note, if we ever allow service worker scripts to be loaded from network
209 // here we need to configure the channel properly. For example, it must
210 // not allow redirects.
211 MOZ_DIAGNOSTIC_ASSERT(contentPolicyType
!=
212 nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
);
214 nsCOMPtr
<nsIChannel
> channel
;
216 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, parentDoc
, secFlags
,
218 nullptr, // aPerformanceStorage
220 nullptr, // aCallbacks
222 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
224 // We must have a loadGroup with a load context for the principal to
225 // traverse the channel correctly.
226 MOZ_ASSERT(loadGroup
);
227 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
));
229 RefPtr
<PerformanceStorage
> performanceStorage
;
230 nsCOMPtr
<nsICSPEventListener
> cspEventListener
;
231 if (aWorkerPrivate
&& !aIsMainScript
) {
232 performanceStorage
= aWorkerPrivate
->GetPerformanceStorage();
233 cspEventListener
= aWorkerPrivate
->CSPEventListener();
236 if (aClientInfo
.isSome()) {
237 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
,
238 aClientInfo
.ref(), aController
, secFlags
,
239 contentPolicyType
, aCookieJarSettings
,
240 performanceStorage
, loadGroup
, nullptr, // aCallbacks
243 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, principal
, secFlags
,
244 contentPolicyType
, aCookieJarSettings
,
245 performanceStorage
, loadGroup
, nullptr, // aCallbacks
249 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SECURITY_ERR
);
251 if (cspEventListener
) {
252 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
253 rv
= loadInfo
->SetCspEventListener(cspEventListener
);
254 NS_ENSURE_SUCCESS(rv
, rv
);
259 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
);
261 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
262 if (NS_WARN_IF(NS_FAILED(rv
))) {
268 channel
.forget(aChannel
);
272 struct ScriptLoadInfo
{
274 MOZ_ASSERT(mScriptIsUTF8
== false, "set by member initializer");
275 MOZ_ASSERT(mScriptLength
== 0, "set by member initializer");
276 mScript
.mUTF16
= nullptr;
280 if (void* data
= mScriptIsUTF8
? static_cast<void*>(mScript
.mUTF8
)
281 : static_cast<void*>(mScript
.mUTF16
)) {
288 // This full URL string is populated only if this object is used in a
292 // This promise is set only when the script is for a ServiceWorker but
293 // it's not in the cache yet. The promise is resolved when the full body is
294 // stored into the cache. mCachePromise will be set to nullptr after
296 RefPtr
<Promise
> mCachePromise
;
298 // The reader stream the cache entry should be filled from, for those cases
299 // when we're going to have an mCachePromise.
300 nsCOMPtr
<nsIInputStream
> mCacheReadStream
;
302 nsCOMPtr
<nsIChannel
> mChannel
;
303 Maybe
<ClientInfo
> mReservedClientInfo
;
304 nsresult mLoadResult
= NS_ERROR_NOT_INITIALIZED
;
306 // If |mScriptIsUTF8|, then |mUTF8| is active, otherwise |mUTF16| is active.
311 size_t mScriptLength
= 0; // in code units
312 bool mScriptIsUTF8
= false;
314 bool ScriptTextIsNull() const {
315 return mScriptIsUTF8
? mScript
.mUTF8
== nullptr : mScript
.mUTF16
== nullptr;
318 void InitUTF8Script() {
319 MOZ_ASSERT(ScriptTextIsNull());
320 MOZ_ASSERT(mScriptLength
== 0);
322 mScriptIsUTF8
= true;
323 mScript
.mUTF8
= nullptr;
327 void InitUTF16Script() {
328 MOZ_ASSERT(ScriptTextIsNull());
329 MOZ_ASSERT(mScriptLength
== 0);
331 mScriptIsUTF8
= false;
332 mScript
.mUTF16
= nullptr;
336 bool mLoadingFinished
= false;
337 bool mExecutionScheduled
= false;
338 bool mExecutionResult
= false;
340 Maybe
<nsString
> mSourceMapURL
;
343 // By default a normal script is just loaded from the network. But for
344 // ServiceWorkers, we have to check if the cache contains the script and
345 // load it from the cache.
352 // This script has been loaded from the ServiceWorker cache.
355 // This script must be stored in the ServiceWorker cache.
358 // Something went wrong or the worker went away.
362 CacheStatus mCacheStatus
= Uncached
;
364 nsLoadFlags mLoadFlags
= nsIRequest::LOAD_NORMAL
;
366 Maybe
<bool> mMutedErrorFlag
;
368 bool Finished() const {
369 return mLoadingFinished
&& !mCachePromise
&& !mChannel
;
373 class ScriptLoaderRunnable
;
375 class ScriptExecutorRunnable final
: public MainThreadWorkerSyncRunnable
{
376 ScriptLoaderRunnable
& mScriptLoader
;
377 const bool mIsWorkerScript
;
378 const Span
<ScriptLoadInfo
> mLoadInfosAlreadyExecuted
, mLoadInfosToExecute
;
381 ScriptExecutorRunnable(ScriptLoaderRunnable
& aScriptLoader
,
382 nsIEventTarget
* aSyncLoopTarget
, bool aIsWorkerScript
,
383 Span
<ScriptLoadInfo
> aLoadInfosAlreadyExecuted
,
384 Span
<ScriptLoadInfo
> aLoadInfosToExecute
);
387 ~ScriptExecutorRunnable() = default;
389 virtual bool IsDebuggerRunnable() const override
;
391 virtual bool PreRun(WorkerPrivate
* aWorkerPrivate
) override
;
393 virtual bool WorkerRun(JSContext
* aCx
,
394 WorkerPrivate
* aWorkerPrivate
) override
;
396 virtual void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
397 bool aRunResult
) override
;
399 nsresult
Cancel() override
;
401 void ShutdownScriptLoader(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
402 bool aResult
, bool aMutedError
);
404 void LogExceptionToConsole(JSContext
* aCx
, WorkerPrivate
* WorkerPrivate
);
406 bool AllScriptsExecutable() const;
409 class CacheScriptLoader
;
411 class CacheCreator final
: public PromiseNativeHandler
{
415 explicit CacheCreator(WorkerPrivate
* aWorkerPrivate
)
416 : mCacheName(aWorkerPrivate
->ServiceWorkerCacheName()),
417 mOriginAttributes(aWorkerPrivate
->GetOriginAttributes()) {
418 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
419 AssertIsOnMainThread();
422 void AddLoader(MovingNotNull
<RefPtr
<CacheScriptLoader
>> aLoader
) {
423 AssertIsOnMainThread();
424 MOZ_ASSERT(!mCacheStorage
);
425 mLoaders
.AppendElement(std::move(aLoader
));
428 virtual void ResolvedCallback(JSContext
* aCx
,
429 JS::Handle
<JS::Value
> aValue
) override
;
431 virtual void RejectedCallback(JSContext
* aCx
,
432 JS::Handle
<JS::Value
> aValue
) override
;
434 // Try to load from cache with aPrincipal used for cache access.
435 nsresult
Load(nsIPrincipal
* aPrincipal
);
437 Cache
* Cache_() const {
438 AssertIsOnMainThread();
443 nsIGlobalObject
* Global() const {
444 AssertIsOnMainThread();
445 MOZ_ASSERT(mSandboxGlobalObject
);
446 return mSandboxGlobalObject
;
452 ~CacheCreator() = default;
454 nsresult
CreateCacheStorage(nsIPrincipal
* aPrincipal
);
456 void FailLoaders(nsresult aRv
);
458 RefPtr
<Cache
> mCache
;
459 RefPtr
<CacheStorage
> mCacheStorage
;
460 nsCOMPtr
<nsIGlobalObject
> mSandboxGlobalObject
;
461 nsTArray
<NotNull
<RefPtr
<CacheScriptLoader
>>> mLoaders
;
464 OriginAttributes mOriginAttributes
;
467 NS_IMPL_ISUPPORTS0(CacheCreator
)
469 class CacheScriptLoader final
: public PromiseNativeHandler
,
470 public nsIStreamLoaderObserver
{
473 NS_DECL_NSISTREAMLOADEROBSERVER
475 CacheScriptLoader(WorkerPrivate
* aWorkerPrivate
, ScriptLoadInfo
& aLoadInfo
,
476 bool aIsWorkerScript
, ScriptLoaderRunnable
* aRunnable
)
477 : mLoadInfo(aLoadInfo
),
478 mRunnable(aRunnable
),
479 mIsWorkerScript(aIsWorkerScript
),
481 mState(aWorkerPrivate
->GetServiceWorkerDescriptor().State()) {
482 MOZ_ASSERT(aWorkerPrivate
);
483 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
484 mMainThreadEventTarget
= aWorkerPrivate
->MainThreadEventTarget();
485 MOZ_ASSERT(mMainThreadEventTarget
);
486 mBaseURI
= GetBaseURI(mIsWorkerScript
, aWorkerPrivate
);
487 AssertIsOnMainThread();
490 void Fail(nsresult aRv
);
492 void Load(Cache
* aCache
);
494 virtual void ResolvedCallback(JSContext
* aCx
,
495 JS::Handle
<JS::Value
> aValue
) override
;
497 virtual void RejectedCallback(JSContext
* aCx
,
498 JS::Handle
<JS::Value
> aValue
) override
;
501 ~CacheScriptLoader() { AssertIsOnMainThread(); }
503 ScriptLoadInfo
& mLoadInfo
;
504 const RefPtr
<ScriptLoaderRunnable
> mRunnable
;
505 const bool mIsWorkerScript
;
507 const ServiceWorkerState mState
;
508 nsCOMPtr
<nsIInputStreamPump
> mPump
;
509 nsCOMPtr
<nsIURI
> mBaseURI
;
510 mozilla::dom::ChannelInfo mChannelInfo
;
511 UniquePtr
<PrincipalInfo
> mPrincipalInfo
;
512 nsCString mCSPHeaderValue
;
513 nsCString mCSPReportOnlyHeaderValue
;
514 nsCString mReferrerPolicyHeaderValue
;
515 nsCOMPtr
<nsIEventTarget
> mMainThreadEventTarget
;
518 NS_IMPL_ISUPPORTS(CacheScriptLoader
, nsIStreamLoaderObserver
)
520 class CachePromiseHandler final
: public PromiseNativeHandler
{
524 CachePromiseHandler(ScriptLoaderRunnable
* aRunnable
,
525 ScriptLoadInfo
& aLoadInfo
)
526 : mRunnable(aRunnable
), mLoadInfo(aLoadInfo
) {
527 AssertIsOnMainThread();
528 MOZ_ASSERT(mRunnable
);
531 virtual void ResolvedCallback(JSContext
* aCx
,
532 JS::Handle
<JS::Value
> aValue
) override
;
534 virtual void RejectedCallback(JSContext
* aCx
,
535 JS::Handle
<JS::Value
> aValue
) override
;
538 ~CachePromiseHandler() { AssertIsOnMainThread(); }
540 RefPtr
<ScriptLoaderRunnable
> mRunnable
;
541 ScriptLoadInfo
& mLoadInfo
;
544 NS_IMPL_ISUPPORTS0(CachePromiseHandler
)
546 class LoaderListener final
: public nsIStreamLoaderObserver
,
547 public nsIRequestObserver
{
551 LoaderListener(ScriptLoaderRunnable
* aRunnable
, ScriptLoadInfo
& aLoadInfo
)
552 : mRunnable(aRunnable
), mLoadInfo(aLoadInfo
) {
553 MOZ_ASSERT(mRunnable
);
557 OnStreamComplete(nsIStreamLoader
* aLoader
, nsISupports
* aContext
,
558 nsresult aStatus
, uint32_t aStringLen
,
559 const uint8_t* aString
) override
;
562 OnStartRequest(nsIRequest
* aRequest
) override
;
565 OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) override
{
566 // Nothing to do here!
571 ~LoaderListener() = default;
573 RefPtr
<ScriptLoaderRunnable
> mRunnable
;
574 ScriptLoadInfo
& mLoadInfo
;
577 NS_IMPL_ISUPPORTS(LoaderListener
, nsIStreamLoaderObserver
, nsIRequestObserver
)
579 class ScriptResponseHeaderProcessor final
: public nsIRequestObserver
{
583 ScriptResponseHeaderProcessor(WorkerPrivate
* aWorkerPrivate
,
585 : mWorkerPrivate(aWorkerPrivate
), mIsMainScript(aIsMainScript
) {
586 AssertIsOnMainThread();
589 NS_IMETHOD
OnStartRequest(nsIRequest
* aRequest
) override
{
590 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
594 nsresult rv
= ProcessCrossOriginEmbedderPolicyHeader(aRequest
);
596 if (NS_WARN_IF(NS_FAILED(rv
))) {
597 aRequest
->Cancel(rv
);
603 NS_IMETHOD
OnStopRequest(nsIRequest
* aRequest
,
604 nsresult aStatusCode
) override
{
608 static nsresult
ProcessCrossOriginEmbedderPolicyHeader(
609 WorkerPrivate
* aWorkerPrivate
,
610 nsILoadInfo::CrossOriginEmbedderPolicy aPolicy
, bool aIsMainScript
) {
611 MOZ_ASSERT(aWorkerPrivate
);
614 MOZ_TRY(aWorkerPrivate
->SetEmbedderPolicy(aPolicy
));
616 // NOTE: Spec doesn't mention non-main scripts must match COEP header with
617 // the main script, but it must pass CORP checking.
618 // see: wpt window-simple-success.https.html, the worker import script
619 // test-incrementer.js without coep header.
620 Unused
<< NS_WARN_IF(!aWorkerPrivate
->MatchEmbedderPolicy(aPolicy
));
627 ~ScriptResponseHeaderProcessor() = default;
629 nsresult
ProcessCrossOriginEmbedderPolicyHeader(nsIRequest
* aRequest
) {
630 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
= do_QueryInterface(aRequest
);
632 // NOTE: the spec doesn't say what to do with non-HTTP workers.
633 // See: https://github.com/whatwg/html/issues/4916
636 mWorkerPrivate
->InheritOwnerEmbedderPolicyOrNull(aRequest
);
642 nsILoadInfo::CrossOriginEmbedderPolicy coep
;
643 MOZ_TRY(httpChannel
->GetResponseEmbedderPolicy(&coep
));
645 return ProcessCrossOriginEmbedderPolicyHeader(mWorkerPrivate
, coep
,
649 WorkerPrivate
* const mWorkerPrivate
;
650 const bool mIsMainScript
;
653 NS_IMPL_ISUPPORTS(ScriptResponseHeaderProcessor
, nsIRequestObserver
);
655 class ScriptLoaderRunnable final
: public nsIRunnable
, public nsINamed
{
656 friend class ScriptExecutorRunnable
;
657 friend class CachePromiseHandler
;
658 friend class CacheScriptLoader
;
659 friend class LoaderListener
;
661 WorkerPrivate
* const mWorkerPrivate
;
662 UniquePtr
<SerializedStackHolder
> mOriginStack
;
663 nsString mOriginStackJSON
;
664 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
665 nsTArrayView
<ScriptLoadInfo
> mLoadInfos
;
666 RefPtr
<CacheCreator
> mCacheCreator
;
667 Maybe
<ClientInfo
> mClientInfo
;
668 Maybe
<ServiceWorkerDescriptor
> mController
;
669 const bool mIsMainScript
;
670 WorkerScriptType mWorkerScriptType
;
671 bool mCanceledMainThread
;
675 NS_DECL_THREADSAFE_ISUPPORTS
677 ScriptLoaderRunnable(WorkerPrivate
* aWorkerPrivate
,
678 UniquePtr
<SerializedStackHolder
> aOriginStack
,
679 nsIEventTarget
* aSyncLoopTarget
,
680 nsTArray
<ScriptLoadInfo
> aLoadInfos
,
681 const Maybe
<ClientInfo
>& aClientInfo
,
682 const Maybe
<ServiceWorkerDescriptor
>& aController
,
683 bool aIsMainScript
, WorkerScriptType aWorkerScriptType
,
685 : mWorkerPrivate(aWorkerPrivate
),
686 mOriginStack(std::move(aOriginStack
)),
687 mSyncLoopTarget(aSyncLoopTarget
),
688 mLoadInfos(std::move(aLoadInfos
)),
689 mClientInfo(aClientInfo
),
690 mController(aController
),
691 mIsMainScript(aIsMainScript
),
692 mWorkerScriptType(aWorkerScriptType
),
693 mCanceledMainThread(false),
695 aWorkerPrivate
->AssertIsOnWorkerThread();
696 MOZ_ASSERT(aSyncLoopTarget
);
697 MOZ_ASSERT_IF(aIsMainScript
, mLoadInfos
.Length() == 1);
700 void CancelMainThreadWithBindingAborted() {
701 CancelMainThread(NS_BINDING_ABORTED
);
705 ~ScriptLoaderRunnable() = default;
709 AssertIsOnMainThread();
711 nsresult rv
= RunInternal();
712 if (NS_WARN_IF(NS_FAILED(rv
))) {
713 CancelMainThread(rv
);
720 GetName(nsACString
& aName
) override
{
721 aName
.AssignLiteral("ScriptLoaderRunnable");
725 void LoadingFinished(ScriptLoadInfo
& aLoadInfo
, nsresult aRv
) {
726 AssertIsOnMainThread();
728 aLoadInfo
.mLoadResult
= aRv
;
730 MOZ_ASSERT(!aLoadInfo
.mLoadingFinished
);
731 aLoadInfo
.mLoadingFinished
= true;
733 if (IsMainWorkerScript() && NS_SUCCEEDED(aRv
)) {
734 MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate
->PrincipalURIMatchesScriptURL());
737 MaybeExecuteFinishedScripts(aLoadInfo
);
740 void MaybeExecuteFinishedScripts(const ScriptLoadInfo
& aLoadInfo
) {
741 AssertIsOnMainThread();
743 // We execute the last step if we don't have a pending operation with the
744 // cache and the loading is completed.
745 if (aLoadInfo
.Finished()) {
746 ExecuteFinishedScripts();
750 nsresult
OnStreamComplete(nsIStreamLoader
* aLoader
, ScriptLoadInfo
& aLoadInfo
,
751 nsresult aStatus
, uint32_t aStringLen
,
752 const uint8_t* aString
) {
753 AssertIsOnMainThread();
755 nsresult rv
= OnStreamCompleteInternal(aLoader
, aStatus
, aStringLen
,
757 LoadingFinished(aLoadInfo
, rv
);
761 nsresult
OnStartRequest(nsIRequest
* aRequest
, ScriptLoadInfo
& aLoadInfo
) {
762 nsresult rv
= OnStartRequestInternal(aRequest
, aLoadInfo
);
764 if (NS_WARN_IF(NS_FAILED(rv
))) {
765 aRequest
->Cancel(rv
);
771 nsresult
OnStartRequestInternal(nsIRequest
* aRequest
,
772 ScriptLoadInfo
& aLoadInfo
) {
773 AssertIsOnMainThread();
775 // If one load info cancels or hits an error, it can race with the start
776 // callback coming from another load info.
777 if (mCanceledMainThread
|| !mCacheCreator
) {
778 return NS_ERROR_FAILURE
;
781 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
783 // Checking the MIME type is only required for ServiceWorkers'
784 // importScripts, per step 10 of
785 // https://w3c.github.io/ServiceWorker/#importscripts
787 // "Extract a MIME type from the response’s header list. If this MIME type
788 // (ignoring parameters) is not a JavaScript MIME type, return a network
790 if (mWorkerPrivate
->IsServiceWorker()) {
791 nsAutoCString mimeType
;
792 channel
->GetContentType(mimeType
);
794 if (!nsContentUtils::IsJavascriptMIMEType(
795 NS_ConvertUTF8toUTF16(mimeType
))) {
796 const nsCString
& scope
=
797 mWorkerPrivate
->GetServiceWorkerRegistrationDescriptor().Scope();
799 ServiceWorkerManager::LocalizeAndReportToAllClients(
800 scope
, "ServiceWorkerRegisterMimeTypeError2",
801 nsTArray
<nsString
>{NS_ConvertUTF8toUTF16(scope
),
802 NS_ConvertUTF8toUTF16(mimeType
),
805 return NS_ERROR_DOM_NETWORK_ERR
;
809 // Note that importScripts() can redirect. In theory the main
810 // script could also encounter an internal redirect, but currently
811 // the assert does not allow that.
812 MOZ_ASSERT_IF(mIsMainScript
, channel
== aLoadInfo
.mChannel
);
813 aLoadInfo
.mChannel
= channel
;
815 // We synthesize the result code, but its never exposed to content.
816 RefPtr
<mozilla::dom::InternalResponse
> ir
=
817 new mozilla::dom::InternalResponse(200, "OK"_ns
);
818 ir
->SetBody(aLoadInfo
.mCacheReadStream
,
819 InternalResponse::UNKNOWN_BODY_SIZE
);
821 // Drop our reference to the stream now that we've passed it along, so it
822 // doesn't hang around once the cache is done with it and keep data alive.
823 aLoadInfo
.mCacheReadStream
= nullptr;
825 // Set the channel info of the channel on the response so that it's
826 // saved in the cache.
827 ir
->InitChannelInfo(channel
);
829 // Save the principal of the channel since its URI encodes the script URI
830 // rather than the ServiceWorkerRegistrationInfo URI.
831 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
832 NS_ASSERTION(ssm
, "Should never be null!");
834 nsCOMPtr
<nsIPrincipal
> channelPrincipal
;
835 MOZ_TRY(ssm
->GetChannelResultPrincipal(channel
,
836 getter_AddRefs(channelPrincipal
)));
838 UniquePtr
<PrincipalInfo
> principalInfo(new PrincipalInfo());
839 MOZ_TRY(PrincipalToPrincipalInfo(channelPrincipal
, principalInfo
.get()));
841 ir
->SetPrincipalInfo(std::move(principalInfo
));
842 ir
->Headers()->FillResponseHeaders(aLoadInfo
.mChannel
);
844 RefPtr
<mozilla::dom::Response
> response
=
845 new mozilla::dom::Response(mCacheCreator
->Global(), ir
, nullptr);
847 mozilla::dom::RequestOrUSVString request
;
849 MOZ_ASSERT(!aLoadInfo
.mFullURL
.IsEmpty());
850 request
.SetAsUSVString().ShareOrDependUpon(aLoadInfo
.mFullURL
);
852 // This JSContext will not end up executing JS code because here there are
853 // no ReadableStreams involved.
858 RefPtr
<Promise
> cachePromise
=
859 mCacheCreator
->Cache_()->Put(jsapi
.cx(), request
, *response
, error
);
860 error
.WouldReportJSException();
861 if (NS_WARN_IF(error
.Failed())) {
862 return error
.StealNSResult();
865 RefPtr
<CachePromiseHandler
> promiseHandler
=
866 new CachePromiseHandler(this, aLoadInfo
);
867 cachePromise
->AppendNativeHandler(promiseHandler
);
869 aLoadInfo
.mCachePromise
.swap(cachePromise
);
870 aLoadInfo
.mCacheStatus
= ScriptLoadInfo::WritingToCache
;
875 bool IsMainWorkerScript() const {
876 return mIsMainScript
&& mWorkerScriptType
== WorkerScript
;
879 bool IsDebuggerScript() const { return mWorkerScriptType
== DebuggerScript
; }
881 void CancelMainThread(nsresult aCancelResult
) {
882 AssertIsOnMainThread();
884 if (mCanceledMainThread
) {
888 mCanceledMainThread
= true;
891 MOZ_ASSERT(mWorkerPrivate
->IsServiceWorker());
895 // Cancel all the channels that were already opened.
896 for (ScriptLoadInfo
& loadInfo
: mLoadInfos
) {
897 // If promise or channel is non-null, their failures will lead to
898 // LoadingFinished being called.
899 bool callLoadingFinished
= true;
901 if (loadInfo
.mCachePromise
) {
902 MOZ_ASSERT(mWorkerPrivate
->IsServiceWorker());
903 loadInfo
.mCachePromise
->MaybeReject(aCancelResult
);
904 loadInfo
.mCachePromise
= nullptr;
905 callLoadingFinished
= false;
908 if (loadInfo
.mChannel
) {
909 if (NS_SUCCEEDED(loadInfo
.mChannel
->Cancel(aCancelResult
))) {
910 callLoadingFinished
= false;
912 NS_WARNING("Failed to cancel channel!");
916 if (callLoadingFinished
&& !loadInfo
.Finished()) {
917 LoadingFinished(loadInfo
, aCancelResult
);
921 ExecuteFinishedScripts();
925 AssertIsOnMainThread();
927 if (!mCacheCreator
) {
931 mCacheCreator
->DeleteCache();
932 mCacheCreator
= nullptr;
935 nsresult
RunInternal() {
936 AssertIsOnMainThread();
938 if (IsMainWorkerScript()) {
939 mWorkerPrivate
->SetLoadingWorkerScript(true);
942 // Convert the origin stack to JSON (which must be done on the main
943 // thread) explicitly, so that we can use the stack to notify the net
944 // monitor about every script we load.
946 ConvertSerializedStackToJSON(std::move(mOriginStack
), mOriginStackJSON
);
949 if (!mWorkerPrivate
->IsServiceWorker() || IsDebuggerScript()) {
950 for (ScriptLoadInfo
& loadInfo
: mLoadInfos
) {
951 nsresult rv
= LoadScript(loadInfo
);
952 if (NS_WARN_IF(NS_FAILED(rv
))) {
953 LoadingFinished(loadInfo
, rv
);
961 MOZ_ASSERT(!mCacheCreator
);
962 mCacheCreator
= new CacheCreator(mWorkerPrivate
);
964 for (ScriptLoadInfo
& loadInfo
: mLoadInfos
) {
965 mCacheCreator
->AddLoader(MakeNotNull
<RefPtr
<CacheScriptLoader
>>(
966 mWorkerPrivate
, loadInfo
, IsMainWorkerScript(), this));
969 // The worker may have a null principal on first load, but in that case its
970 // parent definitely will have one.
971 nsIPrincipal
* principal
= mWorkerPrivate
->GetPrincipal();
973 WorkerPrivate
* parentWorker
= mWorkerPrivate
->GetParent();
974 MOZ_ASSERT(parentWorker
, "Must have a parent!");
975 principal
= parentWorker
->GetPrincipal();
978 nsresult rv
= mCacheCreator
->Load(principal
);
979 if (NS_WARN_IF(NS_FAILED(rv
))) {
986 nsresult
LoadScript(ScriptLoadInfo
& aLoadInfo
) {
987 AssertIsOnMainThread();
988 MOZ_ASSERT_IF(IsMainWorkerScript(), mWorkerScriptType
!= DebuggerScript
);
990 WorkerPrivate
* parentWorker
= mWorkerPrivate
->GetParent();
992 // For JavaScript debugging, the devtools server must run on the same
993 // thread as the debuggee, indicating the worker uses content principal.
994 // However, in Bug 863246, web content will no longer be able to load
995 // resource:// URIs by default, so we need system principal to load
997 nsIPrincipal
* principal
= (mWorkerScriptType
== DebuggerScript
)
998 ? nsContentUtils::GetSystemPrincipal()
999 : mWorkerPrivate
->GetPrincipal();
1001 nsCOMPtr
<nsILoadGroup
> loadGroup
= mWorkerPrivate
->GetLoadGroup();
1002 MOZ_DIAGNOSTIC_ASSERT(principal
);
1004 NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup
, principal
),
1007 // Figure out our base URI.
1008 nsCOMPtr
<nsIURI
> baseURI
= GetBaseURI(mIsMainScript
, mWorkerPrivate
);
1011 nsCOMPtr
<Document
> parentDoc
= mWorkerPrivate
->GetDocument();
1013 nsCOMPtr
<nsIChannel
> channel
;
1014 if (IsMainWorkerScript()) {
1016 channel
= mWorkerPrivate
->ForgetWorkerChannel();
1019 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
1021 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
1022 NS_ASSERTION(secMan
, "This should never be null!");
1024 nsresult
& rv
= aLoadInfo
.mLoadResult
;
1026 nsLoadFlags loadFlags
= aLoadInfo
.mLoadFlags
;
1028 // Get the top-level worker.
1029 WorkerPrivate
* topWorkerPrivate
= mWorkerPrivate
;
1030 WorkerPrivate
* parent
= topWorkerPrivate
->GetParent();
1032 topWorkerPrivate
= parent
;
1033 parent
= topWorkerPrivate
->GetParent();
1036 // If the top-level worker is a dedicated worker and has a window, and the
1037 // window has a docshell, the caching behavior of this worker should match
1038 // that of that docshell.
1039 if (topWorkerPrivate
->IsDedicatedWorker()) {
1040 nsCOMPtr
<nsPIDOMWindowInner
> window
= topWorkerPrivate
->GetWindow();
1042 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
1044 nsresult rv
= docShell
->GetDefaultLoadFlags(&loadFlags
);
1045 NS_ENSURE_SUCCESS(rv
, rv
);
1051 // Only top level workers' main script use the document charset for the
1052 // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
1053 bool useDefaultEncoding
= !(!parentWorker
&& IsMainWorkerScript());
1054 nsCOMPtr
<nsIURI
> url
;
1055 rv
= ConstructURI(aLoadInfo
.mURL
, baseURI
, parentDoc
, useDefaultEncoding
,
1056 getter_AddRefs(url
));
1057 if (NS_FAILED(rv
)) {
1061 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
1062 ReferrerInfo::CreateForFetch(principal
, nullptr);
1063 if (parentWorker
&& !IsMainWorkerScript()) {
1065 static_cast<ReferrerInfo
*>(referrerInfo
.get())
1066 ->CloneWithNewPolicy(parentWorker
->GetReferrerPolicy());
1069 rv
= ChannelFromScriptURL(principal
, parentDoc
, mWorkerPrivate
, loadGroup
,
1070 ios
, secMan
, url
, mClientInfo
, mController
,
1071 IsMainWorkerScript(), mWorkerScriptType
,
1072 mWorkerPrivate
->ContentPolicyType(), loadFlags
,
1073 mWorkerPrivate
->CookieJarSettings(),
1074 referrerInfo
, getter_AddRefs(channel
));
1075 if (NS_WARN_IF(NS_FAILED(rv
))) {
1080 // Associate any originating stack with the channel.
1081 if (!mOriginStackJSON
.IsEmpty()) {
1082 NotifyNetworkMonitorAlternateStack(channel
, mOriginStackJSON
);
1085 // We need to know which index we're on in OnStreamComplete so we know
1086 // where to put the result.
1087 RefPtr
<LoaderListener
> listener
= new LoaderListener(this, aLoadInfo
);
1089 RefPtr
<ScriptResponseHeaderProcessor
> headerProcessor
= nullptr;
1091 // For each debugger script, a non-debugger script load of the same script
1092 // should have occured prior that processed the headers.
1093 if (!IsDebuggerScript()) {
1094 headerProcessor
= MakeRefPtr
<ScriptResponseHeaderProcessor
>(
1095 mWorkerPrivate
, mIsMainScript
);
1098 nsCOMPtr
<nsIStreamLoader
> loader
;
1099 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), listener
, headerProcessor
);
1100 if (NS_WARN_IF(NS_FAILED(rv
))) {
1104 if (IsMainWorkerScript()) {
1105 MOZ_DIAGNOSTIC_ASSERT(aLoadInfo
.mReservedClientInfo
.isSome());
1106 rv
= AddClientChannelHelper(
1107 channel
, std::move(aLoadInfo
.mReservedClientInfo
),
1108 Maybe
<ClientInfo
>(), mWorkerPrivate
->HybridEventTarget());
1109 if (NS_WARN_IF(NS_FAILED(rv
))) {
1114 if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
1115 nsILoadInfo::CrossOriginEmbedderPolicy respectedCOEP
=
1116 mWorkerPrivate
->GetEmbedderPolicy();
1117 if (mWorkerPrivate
->IsDedicatedWorker() &&
1118 respectedCOEP
== nsILoadInfo::EMBEDDER_POLICY_NULL
) {
1119 respectedCOEP
= mWorkerPrivate
->GetOwnerEmbedderPolicy();
1122 nsCOMPtr
<nsILoadInfo
> channelLoadInfo
= channel
->LoadInfo();
1123 channelLoadInfo
->SetLoadingEmbedderPolicy(respectedCOEP
);
1126 if (aLoadInfo
.mCacheStatus
!= ScriptLoadInfo::ToBeCached
) {
1127 rv
= channel
->AsyncOpen(loader
);
1128 if (NS_WARN_IF(NS_FAILED(rv
))) {
1132 nsCOMPtr
<nsIOutputStream
> writer
;
1134 // In case we return early.
1135 aLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cancel
;
1138 getter_AddRefs(aLoadInfo
.mCacheReadStream
), getter_AddRefs(writer
), 0,
1139 UINT32_MAX
, // unlimited size to avoid writer WOULD_BLOCK case
1140 true, false); // non-blocking reader, blocking writer
1141 if (NS_WARN_IF(NS_FAILED(rv
))) {
1145 nsCOMPtr
<nsIStreamListenerTee
> tee
=
1146 do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID
);
1147 rv
= tee
->Init(loader
, writer
, listener
);
1148 if (NS_WARN_IF(NS_FAILED(rv
))) {
1152 nsresult rv
= channel
->AsyncOpen(tee
);
1153 if (NS_WARN_IF(NS_FAILED(rv
))) {
1158 aLoadInfo
.mChannel
.swap(channel
);
1163 nsresult
OnStreamCompleteInternal(nsIStreamLoader
* aLoader
, nsresult aStatus
,
1164 uint32_t aStringLen
, const uint8_t* aString
,
1165 ScriptLoadInfo
& aLoadInfo
) {
1166 AssertIsOnMainThread();
1168 if (!aLoadInfo
.mChannel
) {
1169 return NS_BINDING_ABORTED
;
1172 aLoadInfo
.mChannel
= nullptr;
1174 if (NS_FAILED(aStatus
)) {
1178 NS_ASSERTION(aString
, "This should never be null!");
1180 nsCOMPtr
<nsIRequest
> request
;
1181 nsresult rv
= aLoader
->GetRequest(getter_AddRefs(request
));
1182 NS_ENSURE_SUCCESS(rv
, rv
);
1184 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
1185 MOZ_ASSERT(channel
);
1187 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
1188 NS_ASSERTION(ssm
, "Should never be null!");
1190 nsCOMPtr
<nsIPrincipal
> channelPrincipal
;
1191 rv
= ssm
->GetChannelResultPrincipal(channel
,
1192 getter_AddRefs(channelPrincipal
));
1193 if (NS_WARN_IF(NS_FAILED(rv
))) {
1197 nsIPrincipal
* principal
= mWorkerPrivate
->GetPrincipal();
1199 WorkerPrivate
* parentWorker
= mWorkerPrivate
->GetParent();
1200 MOZ_ASSERT(parentWorker
, "Must have a parent!");
1201 principal
= parentWorker
->GetPrincipal();
1205 if (IsMainWorkerScript()) {
1206 nsCOMPtr
<nsIPrincipal
> loadingPrincipal
=
1207 mWorkerPrivate
->GetLoadingPrincipal();
1208 // if we are not in a ServiceWorker, and the principal is not null, then
1209 // the loading principal must subsume the worker principal if it is not a
1210 // nullPrincipal (sandbox).
1211 MOZ_ASSERT(!loadingPrincipal
|| loadingPrincipal
->GetIsNullPrincipal() ||
1212 principal
->GetIsNullPrincipal() ||
1213 loadingPrincipal
->Subsumes(principal
));
1217 // We don't mute the main worker script becase we've already done
1218 // same-origin checks on them so we should be able to see their errors.
1219 // Note that for data: url, where we allow it through the same-origin check
1220 // but then give it a different origin.
1221 aLoadInfo
.mMutedErrorFlag
.emplace(!IsMainWorkerScript() &&
1222 !principal
->Subsumes(channelPrincipal
));
1224 // Make sure we're not seeing the result of a 404 or something by checking
1225 // the 'requestSucceeded' attribute on the http channel.
1226 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(request
);
1227 nsAutoCString tCspHeaderValue
, tCspROHeaderValue
, tRPHeaderCValue
;
1230 bool requestSucceeded
;
1231 rv
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
1232 NS_ENSURE_SUCCESS(rv
, rv
);
1234 if (!requestSucceeded
) {
1235 return NS_ERROR_NOT_AVAILABLE
;
1238 Unused
<< httpChannel
->GetResponseHeader("content-security-policy"_ns
,
1241 Unused
<< httpChannel
->GetResponseHeader(
1242 "content-security-policy-report-only"_ns
, tCspROHeaderValue
);
1244 Unused
<< httpChannel
->GetResponseHeader("referrer-policy"_ns
,
1247 nsAutoCString sourceMapURL
;
1248 if (nsContentUtils::GetSourceMapURL(httpChannel
, sourceMapURL
)) {
1249 aLoadInfo
.mSourceMapURL
= Some(NS_ConvertUTF8toUTF16(sourceMapURL
));
1254 Document
* parentDoc
= mWorkerPrivate
->GetDocument();
1256 // Use the regular ScriptLoader for this grunt work! Should be just fine
1257 // because we're running on the main thread.
1258 // Worker scripts are always decoded as UTF-8 per spec. Passing null for a
1259 // channel and UTF-8 for the hint will always interpret |aString| as UTF-8.
1260 if (StaticPrefs::dom_worker_script_loader_utf8_parsing_enabled()) {
1261 aLoadInfo
.InitUTF8Script();
1262 rv
= ScriptLoader::ConvertToUTF8(
1263 nullptr, aString
, aStringLen
, u
"UTF-8"_ns
, parentDoc
,
1264 aLoadInfo
.mScript
.mUTF8
, aLoadInfo
.mScriptLength
);
1266 aLoadInfo
.InitUTF16Script();
1267 rv
= ScriptLoader::ConvertToUTF16(
1268 nullptr, aString
, aStringLen
, u
"UTF-8"_ns
, parentDoc
,
1269 aLoadInfo
.mScript
.mUTF16
, aLoadInfo
.mScriptLength
);
1271 if (NS_FAILED(rv
)) {
1275 if (aLoadInfo
.ScriptTextIsNull()) {
1276 if (aLoadInfo
.mScriptLength
!= 0) {
1277 return NS_ERROR_FAILURE
;
1280 nsContentUtils::ReportToConsole(
1281 nsIScriptError::warningFlag
, "DOM"_ns
, parentDoc
,
1282 nsContentUtils::eDOM_PROPERTIES
, "EmptyWorkerSourceWarning");
1285 // Figure out what we actually loaded.
1286 nsCOMPtr
<nsIURI
> finalURI
;
1287 rv
= NS_GetFinalChannelURI(channel
, getter_AddRefs(finalURI
));
1288 NS_ENSURE_SUCCESS(rv
, rv
);
1290 bool isSameOrigin
= false;
1291 rv
= principal
->IsSameOrigin(finalURI
, false, &isSameOrigin
);
1292 NS_ENSURE_SUCCESS(rv
, rv
);
1296 rv
= finalURI
->GetSpec(filename
);
1297 NS_ENSURE_SUCCESS(rv
, rv
);
1299 if (!filename
.IsEmpty()) {
1300 // This will help callers figure out what their script url resolved to
1301 // in case of errors.
1302 aLoadInfo
.mURL
.Assign(NS_ConvertUTF8toUTF16(filename
));
1306 // Update the principal of the worker and its base URI if we just loaded the
1307 // worker's primary script.
1308 if (IsMainWorkerScript()) {
1309 // Take care of the base URI first.
1310 mWorkerPrivate
->SetBaseURI(finalURI
);
1312 // Store the channel info if needed.
1313 mWorkerPrivate
->InitChannelInfo(channel
);
1315 // Our final channel principal should match the loading principal
1316 // in terms of the origin. This used to be an assert, but it seems
1317 // there are some rare cases where this check can fail in practice.
1318 // Perhaps some browser script setting nsIChannel.owner, etc.
1319 NS_ENSURE_TRUE(mWorkerPrivate
->FinalChannelPrincipalIsValid(channel
),
1322 // However, we must still override the principal since the nsIPrincipal
1323 // URL may be different due to same-origin redirects. Unfortunately this
1324 // URL must exactly match the final worker script URL in order to
1325 // properly set the referrer header on fetch/xhr requests. If bug 1340694
1326 // is ever fixed this can be removed.
1327 rv
= mWorkerPrivate
->SetPrincipalsAndCSPFromChannel(channel
);
1328 NS_ENSURE_SUCCESS(rv
, rv
);
1330 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mWorkerPrivate
->GetCSP();
1331 // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
1332 // should get it from the HTTP headers on the worker script.
1333 if (StaticPrefs::security_csp_enable()) {
1335 rv
= mWorkerPrivate
->SetCSPFromHeaderValues(tCspHeaderValue
,
1337 NS_ENSURE_SUCCESS(rv
, rv
);
1339 csp
->EnsureEventTarget(mWorkerPrivate
->MainThreadEventTarget());
1343 mWorkerPrivate
->UpdateReferrerInfoFromHeader(tRPHeaderCValue
);
1345 WorkerPrivate
* parent
= mWorkerPrivate
->GetParent();
1347 // XHR Params Allowed
1348 mWorkerPrivate
->SetXHRParamsAllowed(parent
->XHRParamsAllowed());
1351 nsCOMPtr
<nsILoadInfo
> chanLoadInfo
= channel
->LoadInfo();
1353 mController
= chanLoadInfo
->GetController();
1356 // If we are loading a blob URL we must inherit the controller
1357 // from the parent. This is a bit odd as the blob URL may have
1358 // been created in a different context with a different controller.
1359 // For now, though, this is what the spec says. See:
1361 // https://github.com/w3c/ServiceWorker/issues/1261
1363 if (IsBlobURI(mWorkerPrivate
->GetBaseURI())) {
1364 MOZ_DIAGNOSTIC_ASSERT(mController
.isNothing());
1365 mController
= mWorkerPrivate
->GetParentController();
1372 void DataReceivedFromCache(ScriptLoadInfo
& aLoadInfo
, const uint8_t* aString
,
1373 uint32_t aStringLen
,
1374 const mozilla::dom::ChannelInfo
& aChannelInfo
,
1375 UniquePtr
<PrincipalInfo
> aPrincipalInfo
,
1376 const nsACString
& aCSPHeaderValue
,
1377 const nsACString
& aCSPReportOnlyHeaderValue
,
1378 const nsACString
& aReferrerPolicyHeaderValue
) {
1379 AssertIsOnMainThread();
1380 MOZ_ASSERT(aLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cached
);
1382 auto responsePrincipalOrErr
= PrincipalInfoToPrincipal(*aPrincipalInfo
);
1383 MOZ_DIAGNOSTIC_ASSERT(responsePrincipalOrErr
.isOk());
1385 nsIPrincipal
* principal
= mWorkerPrivate
->GetPrincipal();
1387 WorkerPrivate
* parentWorker
= mWorkerPrivate
->GetParent();
1388 MOZ_ASSERT(parentWorker
, "Must have a parent!");
1389 principal
= parentWorker
->GetPrincipal();
1392 nsCOMPtr
<nsIPrincipal
> responsePrincipal
= responsePrincipalOrErr
.unwrap();
1394 aLoadInfo
.mMutedErrorFlag
.emplace(!principal
->Subsumes(responsePrincipal
));
1397 Document
* parentDoc
= mWorkerPrivate
->GetDocument();
1399 MOZ_ASSERT(aLoadInfo
.ScriptTextIsNull());
1402 if (StaticPrefs::dom_worker_script_loader_utf8_parsing_enabled()) {
1403 aLoadInfo
.InitUTF8Script();
1404 rv
= ScriptLoader::ConvertToUTF8(
1405 nullptr, aString
, aStringLen
, u
"UTF-8"_ns
, parentDoc
,
1406 aLoadInfo
.mScript
.mUTF8
, aLoadInfo
.mScriptLength
);
1408 aLoadInfo
.InitUTF16Script();
1409 rv
= ScriptLoader::ConvertToUTF16(
1410 nullptr, aString
, aStringLen
, u
"UTF-8"_ns
, parentDoc
,
1411 aLoadInfo
.mScript
.mUTF16
, aLoadInfo
.mScriptLength
);
1413 if (NS_SUCCEEDED(rv
) && IsMainWorkerScript()) {
1414 nsCOMPtr
<nsIURI
> finalURI
;
1415 rv
= NS_NewURI(getter_AddRefs(finalURI
), aLoadInfo
.mFullURL
);
1416 if (NS_SUCCEEDED(rv
)) {
1417 mWorkerPrivate
->SetBaseURI(finalURI
);
1420 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1421 nsIPrincipal
* principal
= mWorkerPrivate
->GetPrincipal();
1422 MOZ_DIAGNOSTIC_ASSERT(principal
);
1425 MOZ_ALWAYS_SUCCEEDS(responsePrincipal
->Equals(principal
, &equal
));
1426 MOZ_DIAGNOSTIC_ASSERT(equal
);
1428 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
1430 csp
= parentDoc
->GetCsp();
1432 MOZ_DIAGNOSTIC_ASSERT(!csp
);
1435 mWorkerPrivate
->InitChannelInfo(aChannelInfo
);
1437 nsILoadGroup
* loadGroup
= mWorkerPrivate
->GetLoadGroup();
1438 MOZ_DIAGNOSTIC_ASSERT(loadGroup
);
1440 // Override the principal on the WorkerPrivate. This is only necessary
1441 // in order to get a principal with exactly the correct URL. The fetch
1442 // referrer logic depends on the WorkerPrivate principal having a URL
1443 // that matches the worker script URL. If bug 1340694 is ever fixed
1444 // this can be removed.
1445 // XXX: force the partitionedPrincipal to be equal to the response one.
1446 // This is OK for now because we don't want to expose partitionedPrincipal
1447 // functionality in ServiceWorkers yet.
1448 rv
= mWorkerPrivate
->SetPrincipalsAndCSPOnMainThread(
1449 responsePrincipal
, responsePrincipal
, loadGroup
, nullptr);
1450 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
1452 rv
= mWorkerPrivate
->SetCSPFromHeaderValues(aCSPHeaderValue
,
1453 aCSPReportOnlyHeaderValue
);
1454 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
1456 mWorkerPrivate
->UpdateReferrerInfoFromHeader(aReferrerPolicyHeaderValue
);
1459 if (NS_SUCCEEDED(rv
)) {
1463 LoadingFinished(aLoadInfo
, rv
);
1466 void DataReceived() {
1467 if (IsMainWorkerScript()) {
1468 WorkerPrivate
* parent
= mWorkerPrivate
->GetParent();
1471 // XHR Params Allowed
1472 mWorkerPrivate
->SetXHRParamsAllowed(parent
->XHRParamsAllowed());
1474 // Set Eval and ContentSecurityPolicy
1475 mWorkerPrivate
->SetCSP(parent
->GetCSP());
1476 mWorkerPrivate
->SetEvalAllowed(parent
->IsEvalAllowed());
1481 void ExecuteFinishedScripts() {
1482 AssertIsOnMainThread();
1484 if (IsMainWorkerScript()) {
1485 mWorkerPrivate
->WorkerScriptLoaded();
1488 const auto begin
= mLoadInfos
.begin();
1489 const auto end
= mLoadInfos
.end();
1490 using Iterator
= decltype(begin
);
1491 const auto maybeRangeToExecute
=
1492 [begin
, end
]() -> Maybe
<std::pair
<Iterator
, Iterator
>> {
1493 // firstItToExecute is the first loadInfo where mExecutionScheduled is
1495 auto firstItToExecute
=
1496 std::find_if(begin
, end
, [](const ScriptLoadInfo
& loadInfo
) {
1497 return !loadInfo
.mExecutionScheduled
;
1500 if (firstItToExecute
== end
) {
1504 // firstItUnexecutable is the first loadInfo that is not yet finished.
1505 // Update mExecutionScheduled on the ones we're about to schedule for
1507 const auto firstItUnexecutable
=
1508 std::find_if(firstItToExecute
, end
, [](ScriptLoadInfo
& loadInfo
) {
1509 if (!loadInfo
.Finished()) {
1513 // We can execute this one.
1514 loadInfo
.mExecutionScheduled
= true;
1519 return firstItUnexecutable
== firstItToExecute
1521 : Some(std::pair(firstItToExecute
, firstItUnexecutable
));
1524 // If there are no unexecutable load infos, we can unuse things before the
1525 // execution of the scripts and the stopping of the sync loop.
1526 if (maybeRangeToExecute
) {
1527 if (maybeRangeToExecute
->second
== end
) {
1528 mCacheCreator
= nullptr;
1531 RefPtr
<ScriptExecutorRunnable
> runnable
= new ScriptExecutorRunnable(
1532 *this, mSyncLoopTarget
, IsMainWorkerScript(),
1533 Span
{begin
, maybeRangeToExecute
->first
},
1534 Span
{maybeRangeToExecute
->first
, maybeRangeToExecute
->second
});
1535 if (!runnable
->Dispatch()) {
1536 MOZ_ASSERT(false, "This should never fail!");
1542 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable
, nsIRunnable
, nsINamed
)
1545 LoaderListener::OnStreamComplete(nsIStreamLoader
* aLoader
,
1546 nsISupports
* aContext
, nsresult aStatus
,
1547 uint32_t aStringLen
, const uint8_t* aString
) {
1548 return mRunnable
->OnStreamComplete(aLoader
, mLoadInfo
, aStatus
, aStringLen
,
1553 LoaderListener::OnStartRequest(nsIRequest
* aRequest
) {
1554 return mRunnable
->OnStartRequest(aRequest
, mLoadInfo
);
1557 void CachePromiseHandler::ResolvedCallback(JSContext
* aCx
,
1558 JS::Handle
<JS::Value
> aValue
) {
1559 AssertIsOnMainThread();
1560 // May already have been canceled by CacheScriptLoader::Fail from
1561 // CancelMainThread.
1562 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::WritingToCache
||
1563 mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cancel
);
1564 MOZ_ASSERT_IF(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cancel
,
1565 !mLoadInfo
.mCachePromise
);
1567 if (mLoadInfo
.mCachePromise
) {
1568 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cached
;
1569 mLoadInfo
.mCachePromise
= nullptr;
1570 mRunnable
->MaybeExecuteFinishedScripts(mLoadInfo
);
1574 void CachePromiseHandler::RejectedCallback(JSContext
* aCx
,
1575 JS::Handle
<JS::Value
> aValue
) {
1576 AssertIsOnMainThread();
1577 // May already have been canceled by CacheScriptLoader::Fail from
1578 // CancelMainThread.
1579 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::WritingToCache
||
1580 mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cancel
);
1581 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cancel
;
1583 mLoadInfo
.mCachePromise
= nullptr;
1585 // This will delete the cache object and will call LoadingFinished() with an
1586 // error for each ongoing operation.
1587 mRunnable
->DeleteCache();
1590 nsresult
CacheCreator::CreateCacheStorage(nsIPrincipal
* aPrincipal
) {
1591 AssertIsOnMainThread();
1592 MOZ_ASSERT(!mCacheStorage
);
1593 MOZ_ASSERT(aPrincipal
);
1595 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
1596 MOZ_ASSERT(xpc
, "This should never be null!");
1600 JSContext
* cx
= jsapi
.cx();
1601 JS::Rooted
<JSObject
*> sandbox(cx
);
1602 nsresult rv
= xpc
->CreateSandbox(cx
, aPrincipal
, sandbox
.address());
1603 if (NS_WARN_IF(NS_FAILED(rv
))) {
1607 // The JSContext is not in a realm, so CreateSandbox returned an unwrapped
1609 MOZ_ASSERT(JS_IsGlobalObject(sandbox
));
1611 mSandboxGlobalObject
= xpc::NativeGlobal(sandbox
);
1612 if (NS_WARN_IF(!mSandboxGlobalObject
)) {
1613 return NS_ERROR_FAILURE
;
1616 // If we're in private browsing mode, don't even try to create the
1617 // CacheStorage. Instead, just fail immediately to terminate the
1618 // ServiceWorker load.
1619 if (NS_WARN_IF(mOriginAttributes
.mPrivateBrowsingId
> 0)) {
1620 return NS_ERROR_DOM_SECURITY_ERR
;
1623 // Create a CacheStorage bypassing its trusted origin checks. The
1624 // ServiceWorker has already performed its own checks before getting
1627 mCacheStorage
= CacheStorage::CreateOnMainThread(
1628 mozilla::dom::cache::CHROME_ONLY_NAMESPACE
, mSandboxGlobalObject
,
1629 aPrincipal
, true /* force trusted origin */, error
);
1630 if (NS_WARN_IF(error
.Failed())) {
1631 return error
.StealNSResult();
1637 nsresult
CacheCreator::Load(nsIPrincipal
* aPrincipal
) {
1638 AssertIsOnMainThread();
1639 MOZ_ASSERT(!mLoaders
.IsEmpty());
1641 nsresult rv
= CreateCacheStorage(aPrincipal
);
1642 if (NS_WARN_IF(NS_FAILED(rv
))) {
1647 MOZ_ASSERT(!mCacheName
.IsEmpty());
1648 RefPtr
<Promise
> promise
= mCacheStorage
->Open(mCacheName
, error
);
1649 if (NS_WARN_IF(error
.Failed())) {
1650 return error
.StealNSResult();
1653 promise
->AppendNativeHandler(this);
1657 void CacheCreator::FailLoaders(nsresult aRv
) {
1658 AssertIsOnMainThread();
1660 // Fail() can call LoadingFinished() which may call ExecuteFinishedScripts()
1661 // which sets mCacheCreator to null, so hold a ref.
1662 RefPtr
<CacheCreator
> kungfuDeathGrip
= this;
1664 for (uint32_t i
= 0, len
= mLoaders
.Length(); i
< len
; ++i
) {
1665 mLoaders
[i
]->Fail(aRv
);
1671 void CacheCreator::RejectedCallback(JSContext
* aCx
,
1672 JS::Handle
<JS::Value
> aValue
) {
1673 AssertIsOnMainThread();
1674 FailLoaders(NS_ERROR_FAILURE
);
1677 void CacheCreator::ResolvedCallback(JSContext
* aCx
,
1678 JS::Handle
<JS::Value
> aValue
) {
1679 AssertIsOnMainThread();
1681 if (!aValue
.isObject()) {
1682 FailLoaders(NS_ERROR_FAILURE
);
1686 JS::Rooted
<JSObject
*> obj(aCx
, &aValue
.toObject());
1687 Cache
* cache
= nullptr;
1688 nsresult rv
= UNWRAP_OBJECT(Cache
, &obj
, cache
);
1689 if (NS_WARN_IF(NS_FAILED(rv
))) {
1690 FailLoaders(NS_ERROR_FAILURE
);
1695 MOZ_DIAGNOSTIC_ASSERT(mCache
);
1697 // If the worker is canceled, CancelMainThread() will have cleared the
1698 // loaders via DeleteCache().
1699 for (uint32_t i
= 0, len
= mLoaders
.Length(); i
< len
; ++i
) {
1700 mLoaders
[i
]->Load(cache
);
1704 void CacheCreator::DeleteCache() {
1705 AssertIsOnMainThread();
1707 // This is called when the load is canceled which can occur before
1708 // mCacheStorage is initialized.
1709 if (mCacheStorage
) {
1710 // It's safe to do this while Cache::Match() and Cache::Put() calls are
1712 RefPtr
<Promise
> promise
= mCacheStorage
->Delete(mCacheName
, IgnoreErrors());
1714 // We don't care to know the result of the promise object.
1717 // Always call this here to ensure the loaders array is cleared.
1718 FailLoaders(NS_ERROR_FAILURE
);
1721 void CacheScriptLoader::Fail(nsresult aRv
) {
1722 AssertIsOnMainThread();
1723 MOZ_ASSERT(NS_FAILED(aRv
));
1732 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::ReadingFromCache
);
1737 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cancel
;
1739 // Stop if the load was aborted on the main thread.
1740 // Can't use Finished() because mCachePromise may still be true.
1741 if (mLoadInfo
.mLoadingFinished
) {
1742 MOZ_ASSERT(!mLoadInfo
.mChannel
);
1743 MOZ_ASSERT_IF(mLoadInfo
.mCachePromise
,
1744 mLoadInfo
.mCacheStatus
== ScriptLoadInfo::WritingToCache
||
1745 mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cancel
);
1749 mRunnable
->LoadingFinished(mLoadInfo
, aRv
);
1752 void CacheScriptLoader::Load(Cache
* aCache
) {
1753 AssertIsOnMainThread();
1756 nsCOMPtr
<nsIURI
> uri
;
1758 NS_NewURI(getter_AddRefs(uri
), mLoadInfo
.mURL
, nullptr, mBaseURI
);
1759 if (NS_WARN_IF(NS_FAILED(rv
))) {
1765 rv
= uri
->GetSpec(spec
);
1766 if (NS_WARN_IF(NS_FAILED(rv
))) {
1771 MOZ_ASSERT(mLoadInfo
.mFullURL
.IsEmpty());
1772 CopyUTF8toUTF16(spec
, mLoadInfo
.mFullURL
);
1774 mozilla::dom::RequestOrUSVString request
;
1775 request
.SetAsUSVString().ShareOrDependUpon(mLoadInfo
.mFullURL
);
1777 mozilla::dom::CacheQueryOptions params
;
1779 // This JSContext will not end up executing JS code because here there are
1780 // no ReadableStreams involved.
1785 RefPtr
<Promise
> promise
= aCache
->Match(jsapi
.cx(), request
, params
, error
);
1786 if (NS_WARN_IF(error
.Failed())) {
1787 Fail(error
.StealNSResult());
1791 promise
->AppendNativeHandler(this);
1794 void CacheScriptLoader::RejectedCallback(JSContext
* aCx
,
1795 JS::Handle
<JS::Value
> aValue
) {
1796 AssertIsOnMainThread();
1797 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Uncached
);
1798 Fail(NS_ERROR_FAILURE
);
1801 void CacheScriptLoader::ResolvedCallback(JSContext
* aCx
,
1802 JS::Handle
<JS::Value
> aValue
) {
1803 AssertIsOnMainThread();
1804 // If we have already called 'Fail', we should not proceed.
1809 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Uncached
);
1813 // The ServiceWorkerScriptCache will store data for any scripts it
1814 // it knows about. This is always at least the top level script.
1815 // Depending on if a previous version of the service worker has
1816 // been installed or not it may also know about importScripts(). We
1817 // must handle loading and offlining new importScripts() here, however.
1818 if (aValue
.isUndefined()) {
1819 // If this is the main script or we're not loading a new service worker
1820 // then this is an error. This can happen for internal reasons, like
1821 // storage was probably wiped without removing the service worker
1822 // registration. It can also happen for exposed reasons like the
1823 // service worker script calling importScripts() after install.
1824 if (NS_WARN_IF(mIsWorkerScript
||
1825 (mState
!= ServiceWorkerState::Parsed
&&
1826 mState
!= ServiceWorkerState::Installing
))) {
1827 Fail(NS_ERROR_DOM_INVALID_STATE_ERR
);
1831 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::ToBeCached
;
1832 rv
= mRunnable
->LoadScript(mLoadInfo
);
1833 if (NS_WARN_IF(NS_FAILED(rv
))) {
1839 MOZ_ASSERT(aValue
.isObject());
1841 JS::Rooted
<JSObject
*> obj(aCx
, &aValue
.toObject());
1842 mozilla::dom::Response
* response
= nullptr;
1843 rv
= UNWRAP_OBJECT(Response
, &obj
, response
);
1844 if (NS_WARN_IF(NS_FAILED(rv
))) {
1849 InternalHeaders
* headers
= response
->GetInternalHeaders();
1851 headers
->Get("content-security-policy"_ns
, mCSPHeaderValue
, IgnoreErrors());
1852 headers
->Get("content-security-policy-report-only"_ns
,
1853 mCSPReportOnlyHeaderValue
, IgnoreErrors());
1854 headers
->Get("referrer-policy"_ns
, mReferrerPolicyHeaderValue
,
1857 nsAutoCString coepHeader
;
1858 headers
->Get("cross-origin-embedder-policy"_ns
, coepHeader
, IgnoreErrors());
1860 nsILoadInfo::CrossOriginEmbedderPolicy coep
=
1861 NS_GetCrossOriginEmbedderPolicyFromHeader(coepHeader
);
1863 rv
= ScriptResponseHeaderProcessor::ProcessCrossOriginEmbedderPolicyHeader(
1864 mRunnable
->mWorkerPrivate
, coep
, mRunnable
->mIsMainScript
);
1866 if (NS_WARN_IF(NS_FAILED(rv
))) {
1871 nsCOMPtr
<nsIInputStream
> inputStream
;
1872 response
->GetBody(getter_AddRefs(inputStream
));
1873 mChannelInfo
= response
->GetChannelInfo();
1874 const UniquePtr
<PrincipalInfo
>& pInfo
= response
->GetPrincipalInfo();
1876 mPrincipalInfo
= mozilla::MakeUnique
<PrincipalInfo
>(*pInfo
);
1880 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cached
;
1881 mRunnable
->DataReceivedFromCache(
1882 mLoadInfo
, (uint8_t*)"", 0, mChannelInfo
, std::move(mPrincipalInfo
),
1883 mCSPHeaderValue
, mCSPReportOnlyHeaderValue
, mReferrerPolicyHeaderValue
);
1888 rv
= NS_NewInputStreamPump(getter_AddRefs(mPump
), inputStream
.forget(),
1889 0, /* default segsize */
1890 0, /* default segcount */
1891 false, /* default closeWhenDone */
1892 mMainThreadEventTarget
);
1893 if (NS_WARN_IF(NS_FAILED(rv
))) {
1898 nsCOMPtr
<nsIStreamLoader
> loader
;
1899 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), this);
1900 if (NS_WARN_IF(NS_FAILED(rv
))) {
1905 rv
= mPump
->AsyncRead(loader
);
1906 if (NS_WARN_IF(NS_FAILED(rv
))) {
1912 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(mPump
);
1914 nsCOMPtr
<nsIEventTarget
> sts
=
1915 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
1916 rv
= rr
->RetargetDeliveryTo(sts
);
1917 if (NS_FAILED(rv
)) {
1918 NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
1922 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::ReadingFromCache
;
1926 CacheScriptLoader::OnStreamComplete(nsIStreamLoader
* aLoader
,
1927 nsISupports
* aContext
, nsresult aStatus
,
1928 uint32_t aStringLen
,
1929 const uint8_t* aString
) {
1930 AssertIsOnMainThread();
1934 if (NS_FAILED(aStatus
)) {
1935 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::ReadingFromCache
||
1936 mLoadInfo
.mCacheStatus
== ScriptLoadInfo::Cancel
);
1941 MOZ_ASSERT(mLoadInfo
.mCacheStatus
== ScriptLoadInfo::ReadingFromCache
);
1942 mLoadInfo
.mCacheStatus
= ScriptLoadInfo::Cached
;
1944 MOZ_ASSERT(mPrincipalInfo
);
1945 mRunnable
->DataReceivedFromCache(
1946 mLoadInfo
, aString
, aStringLen
, mChannelInfo
, std::move(mPrincipalInfo
),
1947 mCSPHeaderValue
, mCSPReportOnlyHeaderValue
, mReferrerPolicyHeaderValue
);
1951 class ChannelGetterRunnable final
: public WorkerMainThreadRunnable
{
1952 const nsAString
& mScriptURL
;
1953 const ClientInfo mClientInfo
;
1954 WorkerLoadInfo
& mLoadInfo
;
1958 ChannelGetterRunnable(WorkerPrivate
* aParentWorker
,
1959 const nsAString
& aScriptURL
, WorkerLoadInfo
& aLoadInfo
)
1960 : WorkerMainThreadRunnable(aParentWorker
,
1961 "ScriptLoader :: ChannelGetter"_ns
),
1962 mScriptURL(aScriptURL
)
1963 // ClientInfo should always be present since this should not be called
1964 // if parent's status is greater than Running.
1966 mClientInfo(aParentWorker
->GlobalScope()->GetClientInfo().ref()),
1967 mLoadInfo(aLoadInfo
),
1968 mResult(NS_ERROR_FAILURE
) {
1969 MOZ_ASSERT(aParentWorker
);
1970 aParentWorker
->AssertIsOnWorkerThread();
1973 virtual bool MainThreadRun() override
{
1974 AssertIsOnMainThread();
1976 // Initialize the WorkerLoadInfo principal to our triggering principal
1977 // before doing anything else. Normally we do this in the WorkerPrivate
1978 // Constructor, but we can't do so off the main thread when creating
1979 // a nested worker. So do it here instead.
1980 mLoadInfo
.mLoadingPrincipal
= mWorkerPrivate
->GetPrincipal();
1981 MOZ_DIAGNOSTIC_ASSERT(mLoadInfo
.mLoadingPrincipal
);
1983 mLoadInfo
.mPrincipal
= mLoadInfo
.mLoadingPrincipal
;
1985 // Figure out our base URI.
1986 nsCOMPtr
<nsIURI
> baseURI
= mWorkerPrivate
->GetBaseURI();
1987 MOZ_ASSERT(baseURI
);
1990 nsCOMPtr
<Document
> parentDoc
= mWorkerPrivate
->GetDocument();
1992 mLoadInfo
.mLoadGroup
= mWorkerPrivate
->GetLoadGroup();
1993 mLoadInfo
.mCookieJarSettings
= mWorkerPrivate
->CookieJarSettings();
1995 // Nested workers use default uri encoding.
1996 nsCOMPtr
<nsIURI
> url
;
1998 ConstructURI(mScriptURL
, baseURI
, parentDoc
, true, getter_AddRefs(url
));
1999 NS_ENSURE_SUCCESS(mResult
, true);
2001 Maybe
<ClientInfo
> clientInfo
;
2002 clientInfo
.emplace(mClientInfo
);
2004 nsCOMPtr
<nsIChannel
> channel
;
2005 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
2006 ReferrerInfo::CreateForFetch(mLoadInfo
.mLoadingPrincipal
, nullptr);
2007 mLoadInfo
.mReferrerInfo
=
2008 static_cast<ReferrerInfo
*>(referrerInfo
.get())
2009 ->CloneWithNewPolicy(mWorkerPrivate
->GetReferrerPolicy());
2011 mResult
= workerinternals::ChannelFromScriptURLMainThread(
2012 mLoadInfo
.mLoadingPrincipal
, parentDoc
, mLoadInfo
.mLoadGroup
, url
,
2014 // Nested workers are always dedicated.
2015 nsIContentPolicy::TYPE_INTERNAL_WORKER
, mLoadInfo
.mCookieJarSettings
,
2016 mLoadInfo
.mReferrerInfo
, getter_AddRefs(channel
));
2017 NS_ENSURE_SUCCESS(mResult
, true);
2019 mResult
= mLoadInfo
.SetPrincipalsAndCSPFromChannel(channel
);
2020 NS_ENSURE_SUCCESS(mResult
, true);
2022 mLoadInfo
.mChannel
= std::move(channel
);
2026 nsresult
GetResult() const { return mResult
; }
2029 virtual ~ChannelGetterRunnable() = default;
2032 ScriptExecutorRunnable::ScriptExecutorRunnable(
2033 ScriptLoaderRunnable
& aScriptLoader
, nsIEventTarget
* aSyncLoopTarget
,
2034 bool aIsWorkerScript
, Span
<ScriptLoadInfo
> aLoadInfosAlreadyExecuted
,
2035 Span
<ScriptLoadInfo
> aLoadInfosToExecute
)
2036 : MainThreadWorkerSyncRunnable(aScriptLoader
.mWorkerPrivate
,
2038 mScriptLoader(aScriptLoader
),
2039 mIsWorkerScript(aIsWorkerScript
),
2040 mLoadInfosAlreadyExecuted(aLoadInfosAlreadyExecuted
),
2041 mLoadInfosToExecute(aLoadInfosToExecute
) {
2042 // If there are load infos for scripts that have already been executed, the
2043 // load infos for the scripts to execute must immediate follow them.
2044 MOZ_ASSERT_IF(mLoadInfosAlreadyExecuted
.Length(),
2045 mLoadInfosAlreadyExecuted
.Elements() +
2046 mLoadInfosAlreadyExecuted
.Length() ==
2047 mLoadInfosToExecute
.Elements());
2050 bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
2051 // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
2052 // In the latter case, the runnable needs to be dispatched to the debugger
2054 return mScriptLoader
.mWorkerScriptType
== DebuggerScript
;
2057 bool ScriptExecutorRunnable::PreRun(WorkerPrivate
* aWorkerPrivate
) {
2058 aWorkerPrivate
->AssertIsOnWorkerThread();
2060 if (!mIsWorkerScript
) {
2064 if (!aWorkerPrivate
->GetJSContext()) {
2068 MOZ_ASSERT(mLoadInfosAlreadyExecuted
.Length() == 0);
2069 MOZ_ASSERT(!mScriptLoader
.mRv
.Failed());
2071 // Move the CSP from the workerLoadInfo in the corresponding Client
2072 // where the CSP code expects it!
2073 aWorkerPrivate
->StoreCSPOnClient();
2078 template <typename Unit
>
2079 static bool EvaluateScriptData(JSContext
* aCx
,
2080 const JS::CompileOptions
& aOptions
,
2081 Unit
*& aScriptData
, size_t aScriptLength
) {
2082 static_assert(std::is_same
<Unit
, char16_t
>::value
||
2083 std::is_same
<Unit
, Utf8Unit
>::value
,
2084 "inferred units must be UTF-8 or UTF-16");
2086 // Transfer script data to a local variable.
2087 Unit
* script
= nullptr;
2088 std::swap(script
, aScriptData
);
2090 // Transfer the local to appropriate |SourceText|.
2091 JS::SourceText
<Unit
> srcBuf
;
2092 if (!srcBuf
.init(aCx
, script
, aScriptLength
,
2093 JS::SourceOwnership::TakeOwnership
)) {
2097 JS::Rooted
<JS::Value
> unused(aCx
);
2098 return Evaluate(aCx
, aOptions
, srcBuf
, &unused
);
2101 bool ScriptExecutorRunnable::WorkerRun(JSContext
* aCx
,
2102 WorkerPrivate
* aWorkerPrivate
) {
2103 aWorkerPrivate
->AssertIsOnWorkerThread();
2105 // Don't run if something else has already failed.
2107 mLoadInfosAlreadyExecuted
.cbegin(), mLoadInfosAlreadyExecuted
.cend(),
2108 [](const ScriptLoadInfo
& loadInfo
) {
2109 NS_ASSERTION(!loadInfo
.mChannel
,
2110 "Should no longer have a channel!");
2111 NS_ASSERTION(loadInfo
.mExecutionScheduled
, "Should be scheduled!");
2113 return !loadInfo
.mExecutionResult
;
2118 // If nothing else has failed, our ErrorResult better not be a failure either.
2119 MOZ_ASSERT(!mScriptLoader
.mRv
.Failed(), "Who failed it and why?");
2121 // Slightly icky action at a distance, but there's no better place to stash
2122 // this value, really.
2123 JS::Rooted
<JSObject
*> global(aCx
, JS::CurrentGlobalOrNull(aCx
));
2126 for (ScriptLoadInfo
& loadInfo
: mLoadInfosToExecute
) {
2127 NS_ASSERTION(!loadInfo
.mChannel
, "Should no longer have a channel!");
2128 NS_ASSERTION(loadInfo
.mExecutionScheduled
, "Should be scheduled!");
2129 NS_ASSERTION(!loadInfo
.mExecutionResult
, "Should not have executed yet!");
2131 MOZ_ASSERT(!mScriptLoader
.mRv
.Failed(), "Who failed it and why?");
2132 mScriptLoader
.mRv
.MightThrowJSException();
2133 if (NS_FAILED(loadInfo
.mLoadResult
)) {
2134 workerinternals::ReportLoadError(mScriptLoader
.mRv
, loadInfo
.mLoadResult
,
2139 // If this is a top level script that succeeded, then mark the
2140 // Client execution ready and possible controlled by a service worker.
2141 if (mIsWorkerScript
) {
2142 if (mScriptLoader
.mController
.isSome()) {
2143 MOZ_ASSERT(mScriptLoader
.mWorkerScriptType
== WorkerScript
,
2144 "Debugger clients can't be controlled.");
2145 aWorkerPrivate
->GlobalScope()->Control(mScriptLoader
.mController
.ref());
2147 aWorkerPrivate
->ExecutionReady();
2150 NS_ConvertUTF16toUTF8
filename(loadInfo
.mURL
);
2152 JS::CompileOptions
options(aCx
);
2153 options
.setFileAndLine(filename
.get(), 1).setNoScriptRval(true);
2155 MOZ_ASSERT(loadInfo
.mMutedErrorFlag
.isSome());
2156 options
.setMutedErrors(loadInfo
.mMutedErrorFlag
.valueOr(true));
2158 if (loadInfo
.mSourceMapURL
) {
2159 options
.setSourceMapURL(loadInfo
.mSourceMapURL
->get());
2162 // Our ErrorResult still shouldn't be a failure.
2163 MOZ_ASSERT(!mScriptLoader
.mRv
.Failed(), "Who failed it and why?");
2165 // Transfer script length to a local variable, encoding-agnostically.
2166 size_t scriptLength
= 0;
2167 std::swap(scriptLength
, loadInfo
.mScriptLength
);
2169 // This transfers script data out of the active arm of |loadInfo.mScript|.
2170 bool successfullyEvaluated
=
2171 loadInfo
.mScriptIsUTF8
2172 ? EvaluateScriptData(aCx
, options
, loadInfo
.mScript
.mUTF8
,
2174 : EvaluateScriptData(aCx
, options
, loadInfo
.mScript
.mUTF16
,
2176 MOZ_ASSERT(loadInfo
.ScriptTextIsNull());
2177 if (!successfullyEvaluated
) {
2178 mScriptLoader
.mRv
.StealExceptionFromJSContext(aCx
);
2182 loadInfo
.mExecutionResult
= true;
2188 void ScriptExecutorRunnable::PostRun(JSContext
* aCx
,
2189 WorkerPrivate
* aWorkerPrivate
,
2191 aWorkerPrivate
->AssertIsOnWorkerThread();
2192 MOZ_ASSERT(!JS_IsExceptionPending(aCx
), "Who left an exception on there?");
2194 if (AllScriptsExecutable()) {
2195 // All done. If anything failed then return false.
2197 bool mutedError
= false;
2198 for (const auto& loadInfo
: mScriptLoader
.mLoadInfos
) {
2199 if (!loadInfo
.mExecutionResult
) {
2200 mutedError
= loadInfo
.mMutedErrorFlag
.valueOr(true);
2206 // The only way we can get here with "result" false but without
2207 // mScriptLoader.mRv being a failure is if we're loading the main worker
2208 // script and GetOrCreateGlobalScope() fails. In that case we would have
2209 // returned false from WorkerRun, so assert that.
2210 MOZ_ASSERT_IF(!result
&& !mScriptLoader
.mRv
.Failed(), !aRunResult
);
2211 ShutdownScriptLoader(aCx
, aWorkerPrivate
, result
, mutedError
);
2215 nsresult
ScriptExecutorRunnable::Cancel() {
2216 if (AllScriptsExecutable()) {
2217 ShutdownScriptLoader(mWorkerPrivate
->GetJSContext(), mWorkerPrivate
, false,
2220 return MainThreadWorkerSyncRunnable::Cancel();
2223 void ScriptExecutorRunnable::ShutdownScriptLoader(JSContext
* aCx
,
2224 WorkerPrivate
* aWorkerPrivate
,
2227 aWorkerPrivate
->AssertIsOnWorkerThread();
2229 MOZ_ASSERT(AllScriptsExecutable());
2231 if (mIsWorkerScript
) {
2232 aWorkerPrivate
->SetLoadingWorkerScript(false);
2236 // At this point there are two possibilities:
2238 // 1) mScriptLoader.mRv.Failed(). In that case we just want to leave it
2239 // as-is, except if it has a JS exception and we need to mute JS
2240 // exceptions. In that case, we log the exception without firing any
2241 // events and then replace it on the ErrorResult with a NetworkError,
2244 // 2) mScriptLoader.mRv succeeded. As far as I can tell, this can only
2245 // happen when loading the main worker script and
2246 // GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
2247 // got called. Does it matter what we throw in this case? I'm not
2249 if (mScriptLoader
.mRv
.Failed()) {
2250 if (aMutedError
&& mScriptLoader
.mRv
.IsJSException()) {
2251 LogExceptionToConsole(aCx
, aWorkerPrivate
);
2252 mScriptLoader
.mRv
.Throw(NS_ERROR_DOM_NETWORK_ERR
);
2255 mScriptLoader
.mRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2259 aWorkerPrivate
->StopSyncLoop(mSyncLoopTarget
, aResult
);
2262 void ScriptExecutorRunnable::LogExceptionToConsole(
2263 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
2264 aWorkerPrivate
->AssertIsOnWorkerThread();
2266 MOZ_ASSERT(mScriptLoader
.mRv
.IsJSException());
2268 JS::Rooted
<JS::Value
> exn(aCx
);
2269 if (!ToJSValue(aCx
, std::move(mScriptLoader
.mRv
), &exn
)) {
2273 // Now the exception state should all be in exn.
2274 MOZ_ASSERT(!JS_IsExceptionPending(aCx
));
2275 MOZ_ASSERT(!mScriptLoader
.mRv
.Failed());
2277 JS::ExceptionStack
exnStack(aCx
, exn
, nullptr);
2278 JS::ErrorReportBuilder
report(aCx
);
2279 if (!report
.init(aCx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
2280 JS_ClearPendingException(aCx
);
2284 RefPtr
<xpc::ErrorReport
> xpcReport
= new xpc::ErrorReport();
2285 xpcReport
->Init(report
.report(), report
.toStringResult().c_str(),
2286 aWorkerPrivate
->IsChromeWorker(), aWorkerPrivate
->WindowID());
2288 RefPtr
<AsyncErrorReporter
> r
= new AsyncErrorReporter(xpcReport
);
2289 NS_DispatchToMainThread(r
);
2292 bool ScriptExecutorRunnable::AllScriptsExecutable() const {
2293 return mScriptLoader
.mLoadInfos
.Length() ==
2294 mLoadInfosAlreadyExecuted
.Length() + mLoadInfosToExecute
.Length();
2297 void LoadAllScripts(WorkerPrivate
* aWorkerPrivate
,
2298 UniquePtr
<SerializedStackHolder
> aOriginStack
,
2299 nsTArray
<ScriptLoadInfo
> aLoadInfos
, bool aIsMainScript
,
2300 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
) {
2301 aWorkerPrivate
->AssertIsOnWorkerThread();
2302 NS_ASSERTION(!aLoadInfos
.IsEmpty(), "Bad arguments!");
2304 AutoSyncLoopHolder
syncLoop(aWorkerPrivate
, Canceling
);
2305 nsCOMPtr
<nsIEventTarget
> syncLoopTarget
= syncLoop
.GetEventTarget();
2306 if (!syncLoopTarget
) {
2307 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2311 Maybe
<ClientInfo
> clientInfo
;
2312 Maybe
<ServiceWorkerDescriptor
> controller
;
2313 if (!aIsMainScript
) {
2314 nsIGlobalObject
* global
=
2315 aWorkerScriptType
== WorkerScript
2316 ? static_cast<nsIGlobalObject
*>(aWorkerPrivate
->GlobalScope())
2317 : aWorkerPrivate
->DebuggerGlobalScope();
2319 clientInfo
= global
->GetClientInfo();
2320 controller
= global
->GetController();
2323 RefPtr
<ScriptLoaderRunnable
> loader
= new ScriptLoaderRunnable(
2324 aWorkerPrivate
, std::move(aOriginStack
), syncLoopTarget
,
2325 std::move(aLoadInfos
), clientInfo
, controller
, aIsMainScript
,
2326 aWorkerScriptType
, aRv
);
2328 NS_ASSERTION(aLoadInfos
.IsEmpty(), "Should have swapped!");
2330 RefPtr
<StrongWorkerRef
> workerRef
=
2331 StrongWorkerRef::Create(aWorkerPrivate
, "ScriptLoader", [loader
]() {
2332 NS_DispatchToMainThread(NewRunnableMethod(
2333 "ScriptLoader::CancelMainThreadWithBindingAborted", loader
,
2334 &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted
));
2337 if (NS_WARN_IF(!workerRef
)) {
2338 aRv
.Throw(NS_ERROR_FAILURE
);
2342 if (NS_FAILED(NS_DispatchToMainThread(loader
))) {
2343 NS_ERROR("Failed to dispatch!");
2344 aRv
.Throw(NS_ERROR_FAILURE
);
2351 } /* anonymous namespace */
2353 namespace workerinternals
{
2355 nsresult
ChannelFromScriptURLMainThread(
2356 nsIPrincipal
* aPrincipal
, Document
* aParentDoc
, nsILoadGroup
* aLoadGroup
,
2357 nsIURI
* aScriptURL
, const Maybe
<ClientInfo
>& aClientInfo
,
2358 nsContentPolicyType aMainScriptContentPolicyType
,
2359 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
2360 nsIChannel
** aChannel
) {
2361 AssertIsOnMainThread();
2363 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
2365 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
2366 NS_ASSERTION(secMan
, "This should never be null!");
2368 return ChannelFromScriptURL(
2369 aPrincipal
, aParentDoc
, nullptr, aLoadGroup
, ios
, secMan
, aScriptURL
,
2370 aClientInfo
, Maybe
<ServiceWorkerDescriptor
>(), true, WorkerScript
,
2371 aMainScriptContentPolicyType
, nsIRequest::LOAD_NORMAL
, aCookieJarSettings
,
2372 aReferrerInfo
, aChannel
);
2375 nsresult
ChannelFromScriptURLWorkerThread(JSContext
* aCx
,
2376 WorkerPrivate
* aParent
,
2377 const nsAString
& aScriptURL
,
2378 WorkerLoadInfo
& aLoadInfo
) {
2379 aParent
->AssertIsOnWorkerThread();
2381 RefPtr
<ChannelGetterRunnable
> getter
=
2382 new ChannelGetterRunnable(aParent
, aScriptURL
, aLoadInfo
);
2385 getter
->Dispatch(Canceling
, rv
);
2387 NS_ERROR("Failed to dispatch!");
2388 return rv
.StealNSResult();
2391 return getter
->GetResult();
2394 void ReportLoadError(ErrorResult
& aRv
, nsresult aLoadResult
,
2395 const nsAString
& aScriptURL
) {
2396 MOZ_ASSERT(!aRv
.Failed());
2398 nsPrintfCString
err("Failed to load worker script at \"%s\"",
2399 NS_ConvertUTF16toUTF8(aScriptURL
).get());
2401 switch (aLoadResult
) {
2402 case NS_ERROR_FILE_NOT_FOUND
:
2403 case NS_ERROR_NOT_AVAILABLE
:
2404 aRv
.ThrowNetworkError(err
);
2407 case NS_ERROR_MALFORMED_URI
:
2408 case NS_ERROR_DOM_SYNTAX_ERR
:
2409 aRv
.ThrowSyntaxError(err
);
2412 case NS_BINDING_ABORTED
:
2413 // Note: we used to pretend like we didn't set an exception for
2414 // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway. The
2415 // other callsite, in WorkerPrivate::Constructor, never passed in
2416 // NS_BINDING_ABORTED. So just throw it directly here. Consumers will
2417 // deal as needed. But note that we do NOT want to use one of the
2418 // Throw*Error() methods on ErrorResult for this case, because that will
2419 // make it impossible for consumers to realize that our error was
2420 // NS_BINDING_ABORTED.
2421 aRv
.Throw(aLoadResult
);
2424 case NS_ERROR_DOM_BAD_URI
:
2425 // This is actually a security error.
2426 case NS_ERROR_DOM_SECURITY_ERR
:
2427 aRv
.ThrowSecurityError(err
);
2431 // For lack of anything better, go ahead and throw a NetworkError here.
2432 // We don't want to throw a JS exception, because for toplevel script
2433 // loads that would get squelched.
2434 aRv
.ThrowNetworkError(nsPrintfCString(
2435 "Failed to load worker script at %s (nsresult = 0x%" PRIx32
")",
2436 NS_ConvertUTF16toUTF8(aScriptURL
).get(),
2437 static_cast<uint32_t>(aLoadResult
)));
2442 void LoadMainScript(WorkerPrivate
* aWorkerPrivate
,
2443 UniquePtr
<SerializedStackHolder
> aOriginStack
,
2444 const nsAString
& aScriptURL
,
2445 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
) {
2446 nsTArray
<ScriptLoadInfo
> loadInfos
;
2448 ScriptLoadInfo
* info
= loadInfos
.AppendElement();
2449 info
->mURL
= aScriptURL
;
2450 info
->mLoadFlags
= aWorkerPrivate
->GetLoadFlags();
2452 // We are loading the main script, so the worker's Client must be
2454 if (aWorkerScriptType
== WorkerScript
) {
2455 info
->mReservedClientInfo
= aWorkerPrivate
->GlobalScope()->GetClientInfo();
2457 info
->mReservedClientInfo
=
2458 aWorkerPrivate
->DebuggerGlobalScope()->GetClientInfo();
2461 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), std::move(loadInfos
),
2462 true, aWorkerScriptType
, aRv
);
2465 void Load(WorkerPrivate
* aWorkerPrivate
,
2466 UniquePtr
<SerializedStackHolder
> aOriginStack
,
2467 const nsTArray
<nsString
>& aScriptURLs
,
2468 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
) {
2469 const uint32_t urlCount
= aScriptURLs
.Length();
2475 if (urlCount
> MAX_CONCURRENT_SCRIPTS
) {
2476 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2480 nsTArray
<ScriptLoadInfo
> loadInfos
= TransformIntoNewArray(
2482 [loadFlags
= aWorkerPrivate
->GetLoadFlags()](const auto& scriptURL
) {
2484 res
.mURL
= scriptURL
;
2485 res
.mLoadFlags
= loadFlags
;
2489 LoadAllScripts(aWorkerPrivate
, std::move(aOriginStack
), std::move(loadInfos
),
2490 false, aWorkerScriptType
, aRv
);
2493 } // namespace workerinternals
2496 } // namespace mozilla