Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / dom / workers / ScriptLoader.cpp
blob70c1c3b585bdd97e7b02bf015eb69329615bd7f5
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"
9 #include <algorithm>
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"
29 #include "nsIURI.h"
30 #include "nsIXPConnect.h"
32 #include "jsapi.h"
33 #include "jsfriendapi.h"
34 #include "js/CompilationAndEvaluation.h"
35 #include "js/Exception.h"
36 #include "js/SourceText.h"
37 #include "nsError.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"
44 #include "nsIPipe.h"
45 #include "nsIOutputStream.h"
46 #include "nsPrintfCString.h"
47 #include "nsString.h"
48 #include "nsStreamUtils.h"
49 #include "nsTArray.h"
50 #include "nsThreadUtils.h"
51 #include "nsXPCOM.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;
99 namespace mozilla {
100 namespace dom {
102 namespace {
104 nsIURI* GetBaseURI(bool aIsMainScript, WorkerPrivate* aWorkerPrivate) {
105 MOZ_ASSERT(aWorkerPrivate);
106 nsIURI* baseURI;
107 WorkerPrivate* parentWorker = aWorkerPrivate->GetParent();
108 if (aIsMainScript) {
109 if (parentWorker) {
110 baseURI = parentWorker->GetBaseURI();
111 NS_ASSERTION(baseURI, "Should have been set already!");
112 } else {
113 // May be null.
114 baseURI = aWorkerPrivate->GetBaseURI();
116 } else {
117 baseURI = aWorkerPrivate->GetBaseURI();
118 NS_ASSERTION(baseURI, "Should have been set already!");
121 return baseURI;
124 nsresult ConstructURI(const nsAString& aScriptURL, nsIURI* baseURI,
125 Document* parentDoc, bool aDefaultURIEncoding,
126 nsIURI** aResult) {
127 nsresult rv;
128 if (aDefaultURIEncoding) {
129 rv = NS_NewURI(aResult, aScriptURL, nullptr, baseURI);
130 } else {
131 rv = nsContentUtils::NewURIWithDocumentCharset(aResult, aScriptURL,
132 parentDoc, baseURI);
135 if (NS_FAILED(rv)) {
136 return NS_ERROR_DOM_SYNTAX_ERR;
138 return NS_OK;
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();
153 nsresult rv;
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
159 // situation.
160 if (parentDoc && parentDoc->NodePrincipal() != principal) {
161 parentDoc = nullptr;
164 uint32_t secFlags =
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,
181 &isUIResource);
182 if (NS_WARN_IF(NS_FAILED(rv))) {
183 return rv;
186 if (!isUIResource) {
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;
215 if (parentDoc) {
216 rv = NS_NewChannel(getter_AddRefs(channel), uri, parentDoc, secFlags,
217 contentPolicyType,
218 nullptr, // aPerformanceStorage
219 loadGroup,
220 nullptr, // aCallbacks
221 aLoadFlags, ios);
222 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
223 } else {
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
241 aLoadFlags, ios);
242 } else {
243 rv = NS_NewChannel(getter_AddRefs(channel), uri, principal, secFlags,
244 contentPolicyType, aCookieJarSettings,
245 performanceStorage, loadGroup, nullptr, // aCallbacks
246 aLoadFlags, ios);
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);
258 if (aReferrerInfo) {
259 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
260 if (httpChannel) {
261 rv = httpChannel->SetReferrerInfo(aReferrerInfo);
262 if (NS_WARN_IF(NS_FAILED(rv))) {
263 return rv;
268 channel.forget(aChannel);
269 return rv;
272 struct ScriptLoadInfo {
273 ScriptLoadInfo() {
274 MOZ_ASSERT(mScriptIsUTF8 == false, "set by member initializer");
275 MOZ_ASSERT(mScriptLength == 0, "set by member initializer");
276 mScript.mUTF16 = nullptr;
279 ~ScriptLoadInfo() {
280 if (void* data = mScriptIsUTF8 ? static_cast<void*>(mScript.mUTF8)
281 : static_cast<void*>(mScript.mUTF16)) {
282 js_free(data);
286 nsString mURL;
288 // This full URL string is populated only if this object is used in a
289 // ServiceWorker.
290 nsString mFullURL;
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
295 // resolution.
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.
307 union {
308 char16_t* mUTF16;
309 Utf8Unit* mUTF8;
310 } mScript;
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;
324 mScriptLength = 0;
327 void InitUTF16Script() {
328 MOZ_ASSERT(ScriptTextIsNull());
329 MOZ_ASSERT(mScriptLength == 0);
331 mScriptIsUTF8 = false;
332 mScript.mUTF16 = nullptr;
333 mScriptLength = 0;
336 bool mLoadingFinished = false;
337 bool mExecutionScheduled = false;
338 bool mExecutionResult = false;
340 Maybe<nsString> mSourceMapURL;
342 enum CacheStatus {
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.
346 Uncached,
348 WritingToCache,
350 ReadingFromCache,
352 // This script has been loaded from the ServiceWorker cache.
353 Cached,
355 // This script must be stored in the ServiceWorker cache.
356 ToBeCached,
358 // Something went wrong or the worker went away.
359 Cancel
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;
380 public:
381 ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
382 nsIEventTarget* aSyncLoopTarget, bool aIsWorkerScript,
383 Span<ScriptLoadInfo> aLoadInfosAlreadyExecuted,
384 Span<ScriptLoadInfo> aLoadInfosToExecute);
386 private:
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 {
412 public:
413 NS_DECL_ISUPPORTS
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();
439 MOZ_ASSERT(mCache);
440 return mCache;
443 nsIGlobalObject* Global() const {
444 AssertIsOnMainThread();
445 MOZ_ASSERT(mSandboxGlobalObject);
446 return mSandboxGlobalObject;
449 void DeleteCache();
451 private:
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;
463 nsString mCacheName;
464 OriginAttributes mOriginAttributes;
467 NS_IMPL_ISUPPORTS0(CacheCreator)
469 class CacheScriptLoader final : public PromiseNativeHandler,
470 public nsIStreamLoaderObserver {
471 public:
472 NS_DECL_ISUPPORTS
473 NS_DECL_NSISTREAMLOADEROBSERVER
475 CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo,
476 bool aIsWorkerScript, ScriptLoaderRunnable* aRunnable)
477 : mLoadInfo(aLoadInfo),
478 mRunnable(aRunnable),
479 mIsWorkerScript(aIsWorkerScript),
480 mFailed(false),
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;
500 private:
501 ~CacheScriptLoader() { AssertIsOnMainThread(); }
503 ScriptLoadInfo& mLoadInfo;
504 const RefPtr<ScriptLoaderRunnable> mRunnable;
505 const bool mIsWorkerScript;
506 bool mFailed;
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 {
521 public:
522 NS_DECL_ISUPPORTS
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;
537 private:
538 ~CachePromiseHandler() { AssertIsOnMainThread(); }
540 RefPtr<ScriptLoaderRunnable> mRunnable;
541 ScriptLoadInfo& mLoadInfo;
544 NS_IMPL_ISUPPORTS0(CachePromiseHandler)
546 class LoaderListener final : public nsIStreamLoaderObserver,
547 public nsIRequestObserver {
548 public:
549 NS_DECL_ISUPPORTS
551 LoaderListener(ScriptLoaderRunnable* aRunnable, ScriptLoadInfo& aLoadInfo)
552 : mRunnable(aRunnable), mLoadInfo(aLoadInfo) {
553 MOZ_ASSERT(mRunnable);
556 NS_IMETHOD
557 OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
558 nsresult aStatus, uint32_t aStringLen,
559 const uint8_t* aString) override;
561 NS_IMETHOD
562 OnStartRequest(nsIRequest* aRequest) override;
564 NS_IMETHOD
565 OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) override {
566 // Nothing to do here!
567 return NS_OK;
570 private:
571 ~LoaderListener() = default;
573 RefPtr<ScriptLoaderRunnable> mRunnable;
574 ScriptLoadInfo& mLoadInfo;
577 NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
579 class ScriptResponseHeaderProcessor final : public nsIRequestObserver {
580 public:
581 NS_DECL_ISUPPORTS
583 ScriptResponseHeaderProcessor(WorkerPrivate* aWorkerPrivate,
584 bool aIsMainScript)
585 : mWorkerPrivate(aWorkerPrivate), mIsMainScript(aIsMainScript) {
586 AssertIsOnMainThread();
589 NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override {
590 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
591 return NS_OK;
594 nsresult rv = ProcessCrossOriginEmbedderPolicyHeader(aRequest);
596 if (NS_WARN_IF(NS_FAILED(rv))) {
597 aRequest->Cancel(rv);
600 return rv;
603 NS_IMETHOD OnStopRequest(nsIRequest* aRequest,
604 nsresult aStatusCode) override {
605 return NS_OK;
608 static nsresult ProcessCrossOriginEmbedderPolicyHeader(
609 WorkerPrivate* aWorkerPrivate,
610 nsILoadInfo::CrossOriginEmbedderPolicy aPolicy, bool aIsMainScript) {
611 MOZ_ASSERT(aWorkerPrivate);
613 if (aIsMainScript) {
614 MOZ_TRY(aWorkerPrivate->SetEmbedderPolicy(aPolicy));
615 } else {
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));
623 return NS_OK;
626 private:
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
634 if (!httpChannel) {
635 if (mIsMainScript) {
636 mWorkerPrivate->InheritOwnerEmbedderPolicyOrNull(aRequest);
639 return NS_OK;
642 nsILoadInfo::CrossOriginEmbedderPolicy coep;
643 MOZ_TRY(httpChannel->GetResponseEmbedderPolicy(&coep));
645 return ProcessCrossOriginEmbedderPolicyHeader(mWorkerPrivate, coep,
646 mIsMainScript);
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;
672 ErrorResult& mRv;
674 public:
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,
684 ErrorResult& aRv)
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),
694 mRv(aRv) {
695 aWorkerPrivate->AssertIsOnWorkerThread();
696 MOZ_ASSERT(aSyncLoopTarget);
697 MOZ_ASSERT_IF(aIsMainScript, mLoadInfos.Length() == 1);
700 void CancelMainThreadWithBindingAborted() {
701 CancelMainThread(NS_BINDING_ABORTED);
704 private:
705 ~ScriptLoaderRunnable() = default;
707 NS_IMETHOD
708 Run() override {
709 AssertIsOnMainThread();
711 nsresult rv = RunInternal();
712 if (NS_WARN_IF(NS_FAILED(rv))) {
713 CancelMainThread(rv);
716 return NS_OK;
719 NS_IMETHOD
720 GetName(nsACString& aName) override {
721 aName.AssignLiteral("ScriptLoaderRunnable");
722 return NS_OK;
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,
756 aString, aLoadInfo);
757 LoadingFinished(aLoadInfo, rv);
758 return NS_OK;
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);
768 return 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
789 // error."
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),
803 aLoadInfo.mURL});
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.
854 AutoJSAPI jsapi;
855 jsapi.Init();
857 ErrorResult error;
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;
872 return NS_OK;
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) {
885 return;
888 mCanceledMainThread = true;
890 if (mCacheCreator) {
891 MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
892 DeleteCache();
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;
911 } else {
912 NS_WARNING("Failed to cancel channel!");
916 if (callLoadingFinished && !loadInfo.Finished()) {
917 LoadingFinished(loadInfo, aCancelResult);
921 ExecuteFinishedScripts();
924 void DeleteCache() {
925 AssertIsOnMainThread();
927 if (!mCacheCreator) {
928 return;
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.
945 if (mOriginStack) {
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);
954 return rv;
958 return NS_OK;
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();
972 if (!principal) {
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))) {
980 return rv;
983 return NS_OK;
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
996 // debugger scripts.
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),
1005 NS_ERROR_FAILURE);
1007 // Figure out our base URI.
1008 nsCOMPtr<nsIURI> baseURI = GetBaseURI(mIsMainScript, mWorkerPrivate);
1010 // May be null.
1011 nsCOMPtr<Document> parentDoc = mWorkerPrivate->GetDocument();
1013 nsCOMPtr<nsIChannel> channel;
1014 if (IsMainWorkerScript()) {
1015 // May be null.
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();
1031 while (parent) {
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();
1041 if (window) {
1042 nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
1043 if (docShell) {
1044 nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
1045 NS_ENSURE_SUCCESS(rv, rv);
1050 if (!channel) {
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)) {
1058 return rv;
1061 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1062 ReferrerInfo::CreateForFetch(principal, nullptr);
1063 if (parentWorker && !IsMainWorkerScript()) {
1064 referrerInfo =
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))) {
1076 return 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))) {
1101 return 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))) {
1110 return 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))) {
1129 return rv;
1131 } else {
1132 nsCOMPtr<nsIOutputStream> writer;
1134 // In case we return early.
1135 aLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1137 rv = NS_NewPipe(
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))) {
1142 return 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))) {
1149 return rv;
1152 nsresult rv = channel->AsyncOpen(tee);
1153 if (NS_WARN_IF(NS_FAILED(rv))) {
1154 return rv;
1158 aLoadInfo.mChannel.swap(channel);
1160 return NS_OK;
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)) {
1175 return 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))) {
1194 return rv;
1197 nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1198 if (!principal) {
1199 WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
1200 MOZ_ASSERT(parentWorker, "Must have a parent!");
1201 principal = parentWorker->GetPrincipal();
1204 #ifdef DEBUG
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));
1215 #endif
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;
1229 if (httpChannel) {
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,
1239 tCspHeaderValue);
1241 Unused << httpChannel->GetResponseHeader(
1242 "content-security-policy-report-only"_ns, tCspROHeaderValue);
1244 Unused << httpChannel->GetResponseHeader("referrer-policy"_ns,
1245 tRPHeaderCValue);
1247 nsAutoCString sourceMapURL;
1248 if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
1249 aLoadInfo.mSourceMapURL = Some(NS_ConvertUTF8toUTF16(sourceMapURL));
1253 // May be null.
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);
1265 } else {
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)) {
1272 return 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);
1294 if (isSameOrigin) {
1295 nsCString filename;
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),
1320 NS_ERROR_FAILURE);
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()) {
1334 if (!csp) {
1335 rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
1336 tCspROHeaderValue);
1337 NS_ENSURE_SUCCESS(rv, rv);
1338 } else {
1339 csp->EnsureEventTarget(mWorkerPrivate->MainThreadEventTarget());
1343 mWorkerPrivate->UpdateReferrerInfoFromHeader(tRPHeaderCValue);
1345 WorkerPrivate* parent = mWorkerPrivate->GetParent();
1346 if (parent) {
1347 // XHR Params Allowed
1348 mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
1351 nsCOMPtr<nsILoadInfo> chanLoadInfo = channel->LoadInfo();
1352 if (chanLoadInfo) {
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();
1369 return NS_OK;
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();
1386 if (!principal) {
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));
1396 // May be null.
1397 Document* parentDoc = mWorkerPrivate->GetDocument();
1399 MOZ_ASSERT(aLoadInfo.ScriptTextIsNull());
1401 nsresult rv;
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);
1407 } else {
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);
1424 bool equal = false;
1425 MOZ_ALWAYS_SUCCEEDS(responsePrincipal->Equals(principal, &equal));
1426 MOZ_DIAGNOSTIC_ASSERT(equal);
1428 nsCOMPtr<nsIContentSecurityPolicy> csp;
1429 if (parentDoc) {
1430 csp = parentDoc->GetCsp();
1432 MOZ_DIAGNOSTIC_ASSERT(!csp);
1433 #endif
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)) {
1460 DataReceived();
1463 LoadingFinished(aLoadInfo, rv);
1466 void DataReceived() {
1467 if (IsMainWorkerScript()) {
1468 WorkerPrivate* parent = mWorkerPrivate->GetParent();
1470 if (parent) {
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
1494 // unset.
1495 auto firstItToExecute =
1496 std::find_if(begin, end, [](const ScriptLoadInfo& loadInfo) {
1497 return !loadInfo.mExecutionScheduled;
1500 if (firstItToExecute == end) {
1501 return Nothing();
1504 // firstItUnexecutable is the first loadInfo that is not yet finished.
1505 // Update mExecutionScheduled on the ones we're about to schedule for
1506 // execution.
1507 const auto firstItUnexecutable =
1508 std::find_if(firstItToExecute, end, [](ScriptLoadInfo& loadInfo) {
1509 if (!loadInfo.Finished()) {
1510 return true;
1513 // We can execute this one.
1514 loadInfo.mExecutionScheduled = true;
1516 return false;
1519 return firstItUnexecutable == firstItToExecute
1520 ? Nothing()
1521 : Some(std::pair(firstItToExecute, firstItUnexecutable));
1522 }();
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)
1544 NS_IMETHODIMP
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,
1549 aString);
1552 NS_IMETHODIMP
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!");
1598 AutoJSAPI jsapi;
1599 jsapi.Init();
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))) {
1604 return rv;
1607 // The JSContext is not in a realm, so CreateSandbox returned an unwrapped
1608 // global.
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
1625 // to this point.
1626 ErrorResult error;
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();
1634 return NS_OK;
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))) {
1643 return rv;
1646 ErrorResult error;
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);
1654 return NS_OK;
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);
1668 mLoaders.Clear();
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);
1683 return;
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);
1691 return;
1694 mCache = cache;
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
1711 // running.
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));
1725 if (mFailed) {
1726 return;
1729 mFailed = true;
1731 if (mPump) {
1732 MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
1733 mPump->Cancel(aRv);
1734 mPump = nullptr;
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);
1746 return;
1749 mRunnable->LoadingFinished(mLoadInfo, aRv);
1752 void CacheScriptLoader::Load(Cache* aCache) {
1753 AssertIsOnMainThread();
1754 MOZ_ASSERT(aCache);
1756 nsCOMPtr<nsIURI> uri;
1757 nsresult rv =
1758 NS_NewURI(getter_AddRefs(uri), mLoadInfo.mURL, nullptr, mBaseURI);
1759 if (NS_WARN_IF(NS_FAILED(rv))) {
1760 Fail(rv);
1761 return;
1764 nsAutoCString spec;
1765 rv = uri->GetSpec(spec);
1766 if (NS_WARN_IF(NS_FAILED(rv))) {
1767 Fail(rv);
1768 return;
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.
1781 AutoJSAPI jsapi;
1782 jsapi.Init();
1784 ErrorResult error;
1785 RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
1786 if (NS_WARN_IF(error.Failed())) {
1787 Fail(error.StealNSResult());
1788 return;
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.
1805 if (mFailed) {
1806 return;
1809 MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
1811 nsresult rv;
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);
1828 return;
1831 mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
1832 rv = mRunnable->LoadScript(mLoadInfo);
1833 if (NS_WARN_IF(NS_FAILED(rv))) {
1834 Fail(rv);
1836 return;
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))) {
1845 Fail(rv);
1846 return;
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,
1855 IgnoreErrors());
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))) {
1867 Fail(rv);
1868 return;
1871 nsCOMPtr<nsIInputStream> inputStream;
1872 response->GetBody(getter_AddRefs(inputStream));
1873 mChannelInfo = response->GetChannelInfo();
1874 const UniquePtr<PrincipalInfo>& pInfo = response->GetPrincipalInfo();
1875 if (pInfo) {
1876 mPrincipalInfo = mozilla::MakeUnique<PrincipalInfo>(*pInfo);
1879 if (!inputStream) {
1880 mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1881 mRunnable->DataReceivedFromCache(
1882 mLoadInfo, (uint8_t*)"", 0, mChannelInfo, std::move(mPrincipalInfo),
1883 mCSPHeaderValue, mCSPReportOnlyHeaderValue, mReferrerPolicyHeaderValue);
1884 return;
1887 MOZ_ASSERT(!mPump);
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))) {
1894 Fail(rv);
1895 return;
1898 nsCOMPtr<nsIStreamLoader> loader;
1899 rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
1900 if (NS_WARN_IF(NS_FAILED(rv))) {
1901 Fail(rv);
1902 return;
1905 rv = mPump->AsyncRead(loader);
1906 if (NS_WARN_IF(NS_FAILED(rv))) {
1907 mPump = nullptr;
1908 Fail(rv);
1909 return;
1912 nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
1913 if (rr) {
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;
1925 NS_IMETHODIMP
1926 CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
1927 nsISupports* aContext, nsresult aStatus,
1928 uint32_t aStringLen,
1929 const uint8_t* aString) {
1930 AssertIsOnMainThread();
1932 mPump = nullptr;
1934 if (NS_FAILED(aStatus)) {
1935 MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache ||
1936 mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1937 Fail(aStatus);
1938 return NS_OK;
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);
1948 return NS_OK;
1951 class ChannelGetterRunnable final : public WorkerMainThreadRunnable {
1952 const nsAString& mScriptURL;
1953 const ClientInfo mClientInfo;
1954 WorkerLoadInfo& mLoadInfo;
1955 nsresult mResult;
1957 public:
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);
1989 // May be null.
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;
1997 mResult =
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,
2013 clientInfo,
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);
2023 return true;
2026 nsresult GetResult() const { return mResult; }
2028 private:
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,
2037 aSyncLoopTarget),
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
2053 // queue.
2054 return mScriptLoader.mWorkerScriptType == DebuggerScript;
2057 bool ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate) {
2058 aWorkerPrivate->AssertIsOnWorkerThread();
2060 if (!mIsWorkerScript) {
2061 return true;
2064 if (!aWorkerPrivate->GetJSContext()) {
2065 return false;
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();
2075 return true;
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)) {
2094 return false;
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.
2106 if (std::any_of(
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;
2114 })) {
2115 return true;
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));
2124 MOZ_ASSERT(global);
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,
2135 loadInfo.mURL);
2136 return true;
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,
2173 scriptLength)
2174 : EvaluateScriptData(aCx, options, loadInfo.mScript.mUTF16,
2175 scriptLength);
2176 MOZ_ASSERT(loadInfo.ScriptTextIsNull());
2177 if (!successfullyEvaluated) {
2178 mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
2179 return true;
2182 loadInfo.mExecutionResult = true;
2185 return true;
2188 void ScriptExecutorRunnable::PostRun(JSContext* aCx,
2189 WorkerPrivate* aWorkerPrivate,
2190 bool aRunResult) {
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.
2196 bool result = true;
2197 bool mutedError = false;
2198 for (const auto& loadInfo : mScriptLoader.mLoadInfos) {
2199 if (!loadInfo.mExecutionResult) {
2200 mutedError = loadInfo.mMutedErrorFlag.valueOr(true);
2201 result = false;
2202 break;
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,
2218 false);
2220 return MainThreadWorkerSyncRunnable::Cancel();
2223 void ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
2224 WorkerPrivate* aWorkerPrivate,
2225 bool aResult,
2226 bool aMutedError) {
2227 aWorkerPrivate->AssertIsOnWorkerThread();
2229 MOZ_ASSERT(AllScriptsExecutable());
2231 if (mIsWorkerScript) {
2232 aWorkerPrivate->SetLoadingWorkerScript(false);
2235 if (!aResult) {
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,
2242 // per spec.
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
2248 // sure...
2249 if (mScriptLoader.mRv.Failed()) {
2250 if (aMutedError && mScriptLoader.mRv.IsJSException()) {
2251 LogExceptionToConsole(aCx, aWorkerPrivate);
2252 mScriptLoader.mRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
2254 } else {
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)) {
2270 return;
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);
2281 return;
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);
2308 return;
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);
2339 return;
2342 if (NS_FAILED(NS_DispatchToMainThread(loader))) {
2343 NS_ERROR("Failed to dispatch!");
2344 aRv.Throw(NS_ERROR_FAILURE);
2345 return;
2348 syncLoop.Run();
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);
2384 ErrorResult rv;
2385 getter->Dispatch(Canceling, rv);
2386 if (rv.Failed()) {
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);
2405 break;
2407 case NS_ERROR_MALFORMED_URI:
2408 case NS_ERROR_DOM_SYNTAX_ERR:
2409 aRv.ThrowSyntaxError(err);
2410 break;
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);
2422 return;
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);
2428 break;
2430 default:
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)));
2438 return;
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
2453 // reserved.
2454 if (aWorkerScriptType == WorkerScript) {
2455 info->mReservedClientInfo = aWorkerPrivate->GlobalScope()->GetClientInfo();
2456 } else {
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();
2471 if (!urlCount) {
2472 return;
2475 if (urlCount > MAX_CONCURRENT_SCRIPTS) {
2476 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2477 return;
2480 nsTArray<ScriptLoadInfo> loadInfos = TransformIntoNewArray(
2481 aScriptURLs,
2482 [loadFlags = aWorkerPrivate->GetLoadFlags()](const auto& scriptURL) {
2483 ScriptLoadInfo res;
2484 res.mURL = scriptURL;
2485 res.mLoadFlags = loadFlags;
2486 return res;
2489 LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), std::move(loadInfos),
2490 false, aWorkerScriptType, aRv);
2493 } // namespace workerinternals
2495 } // namespace dom
2496 } // namespace mozilla