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/. */
6 
7 #include "ScriptLoader.h"
8 
9 #include "nsIChannel.h"
10 #include "nsIContentPolicy.h"
11 #include "nsIContentSecurityPolicy.h"
12 #include "nsIDocShell.h"
13 #include "nsIDOMDocument.h"
14 #include "nsIHttpChannel.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsIInputStreamPump.h"
17 #include "nsIIOService.h"
18 #include "nsIProtocolHandler.h"
19 #include "nsIScriptError.h"
20 #include "nsIScriptSecurityManager.h"
21 #include "nsIStreamLoader.h"
22 #include "nsIStreamListenerTee.h"
23 #include "nsIThreadRetargetableRequest.h"
24 #include "nsIURI.h"
25 
26 #include "jsapi.h"
27 #include "jsfriendapi.h"
28 #include "nsError.h"
29 #include "nsContentPolicyUtils.h"
30 #include "nsContentUtils.h"
31 #include "nsDocShellCID.h"
32 #include "nsHostObjectProtocolHandler.h"
33 #include "nsISupportsPrimitives.h"
34 #include "nsNetUtil.h"
35 #include "nsIPipe.h"
36 #include "nsIOutputStream.h"
37 #include "nsPrintfCString.h"
38 #include "nsString.h"
39 #include "nsStreamUtils.h"
40 #include "nsTArray.h"
41 #include "nsThreadUtils.h"
42 #include "nsXPCOM.h"
43 #include "xpcpublic.h"
44 
45 #include "mozilla/Assertions.h"
46 #include "mozilla/LoadContext.h"
47 #include "mozilla/Maybe.h"
48 #include "mozilla/ipc/BackgroundUtils.h"
49 #include "mozilla/dom/CacheBinding.h"
50 #include "mozilla/dom/cache/CacheTypes.h"
51 #include "mozilla/dom/cache/Cache.h"
52 #include "mozilla/dom/cache/CacheStorage.h"
53 #include "mozilla/dom/ChannelInfo.h"
54 #include "mozilla/dom/ClientChannelHelper.h"
55 #include "mozilla/dom/ClientInfo.h"
56 #include "mozilla/dom/Exceptions.h"
57 #include "mozilla/dom/InternalResponse.h"
58 #include "mozilla/dom/nsCSPService.h"
59 #include "mozilla/dom/nsCSPUtils.h"
60 #include "mozilla/dom/PerformanceStorage.h"
61 #include "mozilla/dom/Promise.h"
62 #include "mozilla/dom/PromiseNativeHandler.h"
63 #include "mozilla/dom/Response.h"
64 #include "mozilla/dom/ScriptLoader.h"
65 #include "mozilla/dom/ScriptSettings.h"
66 #include "mozilla/dom/SRILogHelper.h"
67 #include "mozilla/UniquePtr.h"
68 #include "Principal.h"
69 #include "WorkerHolder.h"
70 #include "WorkerPrivate.h"
71 #include "WorkerRunnable.h"
72 #include "WorkerScope.h"
73 
74 #define MAX_CONCURRENT_SCRIPTS 1000
75 
76 using mozilla::dom::cache::Cache;
77 using mozilla::dom::cache::CacheStorage;
78 using mozilla::ipc::PrincipalInfo;
79 
80 namespace mozilla {
81 namespace dom {
82 
83 namespace {
84 
GetBaseURI(bool aIsMainScript,WorkerPrivate * aWorkerPrivate)85 nsIURI* GetBaseURI(bool aIsMainScript, WorkerPrivate* aWorkerPrivate) {
86   MOZ_ASSERT(aWorkerPrivate);
87   nsIURI* baseURI;
88   WorkerPrivate* parentWorker = aWorkerPrivate->GetParent();
89   if (aIsMainScript) {
90     if (parentWorker) {
91       baseURI = parentWorker->GetBaseURI();
92       NS_ASSERTION(baseURI, "Should have been set already!");
93     } else {
94       // May be null.
95       baseURI = aWorkerPrivate->GetBaseURI();
96     }
97   } else {
98     baseURI = aWorkerPrivate->GetBaseURI();
99     NS_ASSERTION(baseURI, "Should have been set already!");
100   }
101 
102   return baseURI;
103 }
104 
ChannelFromScriptURL(nsIPrincipal * principal,nsIURI * baseURI,nsIDocument * parentDoc,WorkerPrivate * aWorkerPrivate,nsILoadGroup * loadGroup,nsIIOService * ios,nsIScriptSecurityManager * secMan,const nsAString & aScriptURL,const Maybe<ClientInfo> & aClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,bool aIsMainScript,WorkerScriptType aWorkerScriptType,nsContentPolicyType aMainScriptContentPolicyType,nsLoadFlags aLoadFlags,bool aDefaultURIEncoding,nsIChannel ** aChannel)105 nsresult ChannelFromScriptURL(
106     nsIPrincipal* principal, nsIURI* baseURI, nsIDocument* parentDoc,
107     WorkerPrivate* aWorkerPrivate, nsILoadGroup* loadGroup, nsIIOService* ios,
108     nsIScriptSecurityManager* secMan, const nsAString& aScriptURL,
109     const Maybe<ClientInfo>& aClientInfo,
110     const Maybe<ServiceWorkerDescriptor>& aController, bool aIsMainScript,
111     WorkerScriptType aWorkerScriptType,
112     nsContentPolicyType aMainScriptContentPolicyType, nsLoadFlags aLoadFlags,
113     bool aDefaultURIEncoding, nsIChannel** aChannel) {
114   AssertIsOnMainThread();
115 
116   nsresult rv;
117   nsCOMPtr<nsIURI> uri;
118 
119   if (aDefaultURIEncoding) {
120     rv = NS_NewURI(getter_AddRefs(uri), aScriptURL, nullptr, baseURI);
121   } else {
122     rv = nsContentUtils::NewURIWithDocumentCharset(
123         getter_AddRefs(uri), aScriptURL, parentDoc, baseURI);
124   }
125 
126   if (NS_FAILED(rv)) {
127     return NS_ERROR_DOM_SYNTAX_ERR;
128   }
129 
130   // If we have the document, use it. Unfortunately, for dedicated workers
131   // 'parentDoc' ends up being the parent document, which is not the document
132   // that we want to use. So make sure to avoid using 'parentDoc' in that
133   // situation.
134   if (parentDoc && parentDoc->NodePrincipal() != principal) {
135     parentDoc = nullptr;
136   }
137 
138   aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
139   uint32_t secFlags = aIsMainScript
140                           ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
141                           : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
142 
143   if (aWorkerScriptType == DebuggerScript) {
144     // A DebuggerScript needs to be a local resource like chrome: or resource:
145     bool isUIResource = false;
146     rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
147                              &isUIResource);
148     if (NS_WARN_IF(NS_FAILED(rv))) {
149       return rv;
150     }
151 
152     if (!isUIResource) {
153       return NS_ERROR_DOM_SECURITY_ERR;
154     }
155 
156     secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
157   }
158 
159   // Note: this is for backwards compatibility and goes against spec.
160   // We should find a better solution.
161   bool isData = false;
162   if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
163     secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
164   }
165 
166   nsContentPolicyType contentPolicyType =
167       aIsMainScript ? aMainScriptContentPolicyType
168                     : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
169 
170   nsCOMPtr<nsIChannel> channel;
171   // If we have the document, use it. Unfortunately, for dedicated workers
172   // 'parentDoc' ends up being the parent document, which is not the document
173   // that we want to use. So make sure to avoid using 'parentDoc' in that
174   // situation.
175   if (parentDoc && parentDoc->NodePrincipal() == principal) {
176     rv = NS_NewChannel(getter_AddRefs(channel), uri, parentDoc, secFlags,
177                        contentPolicyType,
178                        nullptr,  // aPerformanceStorage
179                        loadGroup,
180                        nullptr,  // aCallbacks
181                        aLoadFlags, ios);
182   } else {
183     // We must have a loadGroup with a load context for the principal to
184     // traverse the channel correctly.
185     MOZ_ASSERT(loadGroup);
186     MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
187 
188     RefPtr<PerformanceStorage> performanceStorage;
189     if (aWorkerPrivate && !aIsMainScript) {
190       performanceStorage = aWorkerPrivate->GetPerformanceStorage();
191     }
192 
193     if (aClientInfo.isSome()) {
194       rv = NS_NewChannel(getter_AddRefs(channel), uri, principal,
195                          aClientInfo.ref(), aController, secFlags,
196                          contentPolicyType, performanceStorage, loadGroup,
197                          nullptr,  // aCallbacks
198                          aLoadFlags, ios);
199     } else {
200       rv = NS_NewChannel(getter_AddRefs(channel), uri, principal, secFlags,
201                          contentPolicyType, performanceStorage, loadGroup,
202                          nullptr,  // aCallbacks
203                          aLoadFlags, ios);
204     }
205   }
206 
207   NS_ENSURE_SUCCESS(rv, rv);
208 
209   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
210     mozilla::net::ReferrerPolicy referrerPolicy =
211         parentDoc ? parentDoc->GetReferrerPolicy() : mozilla::net::RP_Unset;
212     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(
213         principal, parentDoc, httpChannel, referrerPolicy);
214     if (NS_WARN_IF(NS_FAILED(rv))) {
215       return rv;
216     }
217   }
218 
219   channel.forget(aChannel);
220   return rv;
221 }
222 
223 struct ScriptLoadInfo {
ScriptLoadInfomozilla::dom::__anonfbd4ccac0111::ScriptLoadInfo224   ScriptLoadInfo()
225       : mScriptTextBuf(nullptr),
226         mScriptTextLength(0),
227         mLoadResult(NS_ERROR_NOT_INITIALIZED),
228         mLoadingFinished(false),
229         mExecutionScheduled(false),
230         mExecutionResult(false),
231         mCacheStatus(Uncached),
232         mLoadFlags(nsIRequest::LOAD_NORMAL) {}
233 
~ScriptLoadInfomozilla::dom::__anonfbd4ccac0111::ScriptLoadInfo234   ~ScriptLoadInfo() {
235     if (mScriptTextBuf) {
236       js_free(mScriptTextBuf);
237     }
238   }
239 
240   nsString mURL;
241 
242   // This full URL string is populated only if this object is used in a
243   // ServiceWorker.
244   nsString mFullURL;
245 
246   // This promise is set only when the script is for a ServiceWorker but
247   // it's not in the cache yet. The promise is resolved when the full body is
248   // stored into the cache.  mCachePromise will be set to nullptr after
249   // resolution.
250   RefPtr<Promise> mCachePromise;
251 
252   // The reader stream the cache entry should be filled from, for those cases
253   // when we're going to have an mCachePromise.
254   nsCOMPtr<nsIInputStream> mCacheReadStream;
255 
256   nsCOMPtr<nsIChannel> mChannel;
257   Maybe<ClientInfo> mReservedClientInfo;
258   char16_t* mScriptTextBuf;
259   size_t mScriptTextLength;
260 
261   nsresult mLoadResult;
262   bool mLoadingFinished;
263   bool mExecutionScheduled;
264   bool mExecutionResult;
265 
266   enum CacheStatus {
267     // By default a normal script is just loaded from the network. But for
268     // ServiceWorkers, we have to check if the cache contains the script and
269     // load it from the cache.
270     Uncached,
271 
272     WritingToCache,
273 
274     ReadingFromCache,
275 
276     // This script has been loaded from the ServiceWorker cache.
277     Cached,
278 
279     // This script must be stored in the ServiceWorker cache.
280     ToBeCached,
281 
282     // Something went wrong or the worker went away.
283     Cancel
284   };
285 
286   CacheStatus mCacheStatus;
287 
288   nsLoadFlags mLoadFlags;
289 
290   Maybe<bool> mMutedErrorFlag;
291 
Finishedmozilla::dom::__anonfbd4ccac0111::ScriptLoadInfo292   bool Finished() const {
293     return mLoadingFinished && !mCachePromise && !mChannel;
294   }
295 };
296 
297 class ScriptLoaderRunnable;
298 
299 class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable {
300   ScriptLoaderRunnable& mScriptLoader;
301   bool mIsWorkerScript;
302   uint32_t mFirstIndex;
303   uint32_t mLastIndex;
304 
305  public:
306   ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
307                          nsIEventTarget* aSyncLoopTarget, bool aIsWorkerScript,
308                          uint32_t aFirstIndex, uint32_t aLastIndex);
309 
310  private:
~ScriptExecutorRunnable()311   ~ScriptExecutorRunnable() {}
312 
313   virtual bool IsDebuggerRunnable() const override;
314 
315   virtual bool PreRun(WorkerPrivate* aWorkerPrivate) override;
316 
317   virtual bool WorkerRun(JSContext* aCx,
318                          WorkerPrivate* aWorkerPrivate) override;
319 
320   virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
321                        bool aRunResult) override;
322 
323   nsresult Cancel() override;
324 
325   void ShutdownScriptLoader(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
326                             bool aResult, bool aMutedError);
327 
328   void LogExceptionToConsole(JSContext* aCx, WorkerPrivate* WorkerPrivate);
329 };
330 
331 class CacheScriptLoader;
332 
333 class CacheCreator final : public PromiseNativeHandler {
334  public:
335   NS_DECL_ISUPPORTS
336 
CacheCreator(WorkerPrivate * aWorkerPrivate)337   explicit CacheCreator(WorkerPrivate* aWorkerPrivate)
338       : mCacheName(aWorkerPrivate->ServiceWorkerCacheName()),
339         mOriginAttributes(aWorkerPrivate->GetOriginAttributes()) {
340     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
341     MOZ_ASSERT(aWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript());
342     AssertIsOnMainThread();
343   }
344 
AddLoader(CacheScriptLoader * aLoader)345   void AddLoader(CacheScriptLoader* aLoader) {
346     AssertIsOnMainThread();
347     MOZ_ASSERT(!mCacheStorage);
348     mLoaders.AppendElement(aLoader);
349   }
350 
351   virtual void ResolvedCallback(JSContext* aCx,
352                                 JS::Handle<JS::Value> aValue) override;
353 
354   virtual void RejectedCallback(JSContext* aCx,
355                                 JS::Handle<JS::Value> aValue) override;
356 
357   // Try to load from cache with aPrincipal used for cache access.
358   nsresult Load(nsIPrincipal* aPrincipal);
359 
Cache_() const360   Cache* Cache_() const {
361     AssertIsOnMainThread();
362     MOZ_ASSERT(mCache);
363     return mCache;
364   }
365 
Global() const366   nsIGlobalObject* Global() const {
367     AssertIsOnMainThread();
368     MOZ_ASSERT(mSandboxGlobalObject);
369     return mSandboxGlobalObject;
370   }
371 
372   void DeleteCache();
373 
374  private:
~CacheCreator()375   ~CacheCreator() {}
376 
377   nsresult CreateCacheStorage(nsIPrincipal* aPrincipal);
378 
379   void FailLoaders(nsresult aRv);
380 
381   RefPtr<Cache> mCache;
382   RefPtr<CacheStorage> mCacheStorage;
383   nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject;
384   nsTArray<RefPtr<CacheScriptLoader>> mLoaders;
385 
386   nsString mCacheName;
387   OriginAttributes mOriginAttributes;
388 };
389 
390 NS_IMPL_ISUPPORTS0(CacheCreator)
391 
392 class CacheScriptLoader final : public PromiseNativeHandler,
393                                 public nsIStreamLoaderObserver {
394  public:
395   NS_DECL_ISUPPORTS
396   NS_DECL_NSISTREAMLOADEROBSERVER
397 
CacheScriptLoader(WorkerPrivate * aWorkerPrivate,ScriptLoadInfo & aLoadInfo,uint32_t aIndex,bool aIsWorkerScript,ScriptLoaderRunnable * aRunnable)398   CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo,
399                     uint32_t aIndex, bool aIsWorkerScript,
400                     ScriptLoaderRunnable* aRunnable)
401       : mLoadInfo(aLoadInfo),
402         mIndex(aIndex),
403         mRunnable(aRunnable),
404         mIsWorkerScript(aIsWorkerScript),
405         mFailed(false) {
406     MOZ_ASSERT(aWorkerPrivate);
407     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
408     mMainThreadEventTarget = aWorkerPrivate->MainThreadEventTarget();
409     MOZ_ASSERT(mMainThreadEventTarget);
410     mBaseURI = GetBaseURI(mIsWorkerScript, aWorkerPrivate);
411     AssertIsOnMainThread();
412   }
413 
414   void Fail(nsresult aRv);
415 
416   void Load(Cache* aCache);
417 
418   virtual void ResolvedCallback(JSContext* aCx,
419                                 JS::Handle<JS::Value> aValue) override;
420 
421   virtual void RejectedCallback(JSContext* aCx,
422                                 JS::Handle<JS::Value> aValue) override;
423 
424  private:
~CacheScriptLoader()425   ~CacheScriptLoader() { AssertIsOnMainThread(); }
426 
427   ScriptLoadInfo& mLoadInfo;
428   uint32_t mIndex;
429   RefPtr<ScriptLoaderRunnable> mRunnable;
430   bool mIsWorkerScript;
431   bool mFailed;
432   nsCOMPtr<nsIInputStreamPump> mPump;
433   nsCOMPtr<nsIURI> mBaseURI;
434   mozilla::dom::ChannelInfo mChannelInfo;
435   UniquePtr<PrincipalInfo> mPrincipalInfo;
436   nsCString mCSPHeaderValue;
437   nsCString mCSPReportOnlyHeaderValue;
438   nsCString mReferrerPolicyHeaderValue;
439   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
440 };
441 
442 NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
443 
444 class CachePromiseHandler final : public PromiseNativeHandler {
445  public:
446   NS_DECL_ISUPPORTS
447 
CachePromiseHandler(ScriptLoaderRunnable * aRunnable,ScriptLoadInfo & aLoadInfo,uint32_t aIndex)448   CachePromiseHandler(ScriptLoaderRunnable* aRunnable,
449                       ScriptLoadInfo& aLoadInfo, uint32_t aIndex)
450       : mRunnable(aRunnable), mLoadInfo(aLoadInfo), mIndex(aIndex) {
451     AssertIsOnMainThread();
452     MOZ_ASSERT(mRunnable);
453   }
454 
455   virtual void ResolvedCallback(JSContext* aCx,
456                                 JS::Handle<JS::Value> aValue) override;
457 
458   virtual void RejectedCallback(JSContext* aCx,
459                                 JS::Handle<JS::Value> aValue) override;
460 
461  private:
~CachePromiseHandler()462   ~CachePromiseHandler() { AssertIsOnMainThread(); }
463 
464   RefPtr<ScriptLoaderRunnable> mRunnable;
465   ScriptLoadInfo& mLoadInfo;
466   uint32_t mIndex;
467 };
468 
469 NS_IMPL_ISUPPORTS0(CachePromiseHandler)
470 
471 class LoaderListener final : public nsIStreamLoaderObserver,
472                              public nsIRequestObserver {
473  public:
474   NS_DECL_ISUPPORTS
475 
LoaderListener(ScriptLoaderRunnable * aRunnable,uint32_t aIndex)476   LoaderListener(ScriptLoaderRunnable* aRunnable, uint32_t aIndex)
477       : mRunnable(aRunnable), mIndex(aIndex) {
478     MOZ_ASSERT(mRunnable);
479   }
480 
481   NS_IMETHOD
482   OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
483                    nsresult aStatus, uint32_t aStringLen,
484                    const uint8_t* aString) override;
485 
486   NS_IMETHOD
487   OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override;
488 
489   NS_IMETHOD
OnStopRequest(nsIRequest * aRequest,nsISupports * aContext,nsresult aStatusCode)490   OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
491                 nsresult aStatusCode) override {
492     // Nothing to do here!
493     return NS_OK;
494   }
495 
496  private:
~LoaderListener()497   ~LoaderListener() {}
498 
499   RefPtr<ScriptLoaderRunnable> mRunnable;
500   uint32_t mIndex;
501 };
502 
503 NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
504 
505 class ScriptLoaderHolder;
506 
507 class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
508   friend class ScriptExecutorRunnable;
509   friend class ScriptLoaderHolder;
510   friend class CachePromiseHandler;
511   friend class CacheScriptLoader;
512   friend class LoaderListener;
513 
514   WorkerPrivate* mWorkerPrivate;
515   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
516   nsTArray<ScriptLoadInfo> mLoadInfos;
517   RefPtr<CacheCreator> mCacheCreator;
518   Maybe<ClientInfo> mClientInfo;
519   Maybe<ServiceWorkerDescriptor> mController;
520   bool mIsMainScript;
521   WorkerScriptType mWorkerScriptType;
522   bool mCanceled;
523   bool mCanceledMainThread;
524   ErrorResult& mRv;
525 
526  public:
527   NS_DECL_THREADSAFE_ISUPPORTS
528 
ScriptLoaderRunnable(WorkerPrivate * aWorkerPrivate,nsIEventTarget * aSyncLoopTarget,nsTArray<ScriptLoadInfo> & aLoadInfos,const Maybe<ClientInfo> & aClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,bool aIsMainScript,WorkerScriptType aWorkerScriptType,ErrorResult & aRv)529   ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
530                        nsIEventTarget* aSyncLoopTarget,
531                        nsTArray<ScriptLoadInfo>& aLoadInfos,
532                        const Maybe<ClientInfo>& aClientInfo,
533                        const Maybe<ServiceWorkerDescriptor>& aController,
534                        bool aIsMainScript, WorkerScriptType aWorkerScriptType,
535                        ErrorResult& aRv)
536       : mWorkerPrivate(aWorkerPrivate),
537         mSyncLoopTarget(aSyncLoopTarget),
538         mClientInfo(aClientInfo),
539         mController(aController),
540         mIsMainScript(aIsMainScript),
541         mWorkerScriptType(aWorkerScriptType),
542         mCanceled(false),
543         mCanceledMainThread(false),
544         mRv(aRv) {
545     aWorkerPrivate->AssertIsOnWorkerThread();
546     MOZ_ASSERT(aSyncLoopTarget);
547     MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1);
548 
549     mLoadInfos.SwapElements(aLoadInfos);
550   }
551 
552  private:
~ScriptLoaderRunnable()553   ~ScriptLoaderRunnable() {}
554 
555   NS_IMETHOD
Run()556   Run() override {
557     AssertIsOnMainThread();
558 
559     nsresult rv = RunInternal();
560     if (NS_WARN_IF(NS_FAILED(rv))) {
561       CancelMainThread(rv);
562     }
563 
564     return NS_OK;
565   }
566 
567   NS_IMETHOD
GetName(nsACString & aName)568   GetName(nsACString& aName) override {
569     aName.AssignASCII("ScriptLoaderRunnable");
570     return NS_OK;
571   }
572 
LoadingFinished(uint32_t aIndex,nsresult aRv)573   void LoadingFinished(uint32_t aIndex, nsresult aRv) {
574     AssertIsOnMainThread();
575     MOZ_ASSERT(aIndex < mLoadInfos.Length());
576     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
577 
578     loadInfo.mLoadResult = aRv;
579 
580     MOZ_ASSERT(!loadInfo.mLoadingFinished);
581     loadInfo.mLoadingFinished = true;
582 
583     if (IsMainWorkerScript() && NS_SUCCEEDED(aRv)) {
584       MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->PrincipalURIMatchesScriptURL());
585     }
586 
587     MaybeExecuteFinishedScripts(aIndex);
588   }
589 
MaybeExecuteFinishedScripts(uint32_t aIndex)590   void MaybeExecuteFinishedScripts(uint32_t aIndex) {
591     AssertIsOnMainThread();
592     MOZ_ASSERT(aIndex < mLoadInfos.Length());
593     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
594 
595     // We execute the last step if we don't have a pending operation with the
596     // cache and the loading is completed.
597     if (loadInfo.Finished()) {
598       ExecuteFinishedScripts();
599     }
600   }
601 
OnStreamComplete(nsIStreamLoader * aLoader,uint32_t aIndex,nsresult aStatus,uint32_t aStringLen,const uint8_t * aString)602   nsresult OnStreamComplete(nsIStreamLoader* aLoader, uint32_t aIndex,
603                             nsresult aStatus, uint32_t aStringLen,
604                             const uint8_t* aString) {
605     AssertIsOnMainThread();
606     MOZ_ASSERT(aIndex < mLoadInfos.Length());
607 
608     nsresult rv = OnStreamCompleteInternal(aLoader, aStatus, aStringLen,
609                                            aString, mLoadInfos[aIndex]);
610     LoadingFinished(aIndex, rv);
611     return NS_OK;
612   }
613 
OnStartRequest(nsIRequest * aRequest,uint32_t aIndex)614   nsresult OnStartRequest(nsIRequest* aRequest, uint32_t aIndex) {
615     AssertIsOnMainThread();
616     MOZ_ASSERT(aIndex < mLoadInfos.Length());
617 
618     // If one load info cancels or hits an error, it can race with the start
619     // callback coming from another load info.
620     if (mCanceledMainThread || !mCacheCreator) {
621       aRequest->Cancel(NS_ERROR_FAILURE);
622       return NS_ERROR_FAILURE;
623     }
624 
625     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
626 
627     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
628 
629     // Note that importScripts() can redirect.  In theory the main
630     // script could also encounter an internal redirect, but currently
631     // the assert does not allow that.
632     MOZ_ASSERT_IF(mIsMainScript, channel == loadInfo.mChannel);
633     loadInfo.mChannel = channel;
634 
635     // We synthesize the result code, but its never exposed to content.
636     RefPtr<mozilla::dom::InternalResponse> ir =
637         new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
638     ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE);
639 
640     // Drop our reference to the stream now that we've passed it along, so it
641     // doesn't hang around once the cache is done with it and keep data alive.
642     loadInfo.mCacheReadStream = nullptr;
643 
644     // Set the channel info of the channel on the response so that it's
645     // saved in the cache.
646     ir->InitChannelInfo(channel);
647 
648     // Save the principal of the channel since its URI encodes the script URI
649     // rather than the ServiceWorkerRegistrationInfo URI.
650     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
651     NS_ASSERTION(ssm, "Should never be null!");
652 
653     nsCOMPtr<nsIPrincipal> channelPrincipal;
654     nsresult rv = ssm->GetChannelResultPrincipal(
655         channel, getter_AddRefs(channelPrincipal));
656     if (NS_WARN_IF(NS_FAILED(rv))) {
657       channel->Cancel(rv);
658       return rv;
659     }
660 
661     UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
662     rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
663     if (NS_WARN_IF(NS_FAILED(rv))) {
664       channel->Cancel(rv);
665       return rv;
666     }
667 
668     ir->SetPrincipalInfo(Move(principalInfo));
669     ir->Headers()->FillResponseHeaders(loadInfo.mChannel);
670 
671     RefPtr<mozilla::dom::Response> response =
672         new mozilla::dom::Response(mCacheCreator->Global(), ir, nullptr);
673 
674     mozilla::dom::RequestOrUSVString request;
675 
676     MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
677     request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
678                                     loadInfo.mFullURL.Length());
679 
680     // This JSContext will not end up executing JS code because here there are
681     // no ReadableStreams involved.
682     AutoJSAPI jsapi;
683     jsapi.Init();
684 
685     ErrorResult error;
686     RefPtr<Promise> cachePromise =
687         mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
688     if (NS_WARN_IF(error.Failed())) {
689       nsresult rv = error.StealNSResult();
690       channel->Cancel(rv);
691       return rv;
692     }
693 
694     RefPtr<CachePromiseHandler> promiseHandler =
695         new CachePromiseHandler(this, loadInfo, aIndex);
696     cachePromise->AppendNativeHandler(promiseHandler);
697 
698     loadInfo.mCachePromise.swap(cachePromise);
699     loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
700 
701     return NS_OK;
702   }
703 
Notify(WorkerStatus aStatus)704   bool Notify(WorkerStatus aStatus) {
705     mWorkerPrivate->AssertIsOnWorkerThread();
706 
707     if (aStatus >= Terminating && !mCanceled) {
708       mCanceled = true;
709 
710       MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(
711           "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted", this,
712           &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted)));
713     }
714 
715     return true;
716   }
717 
IsMainWorkerScript() const718   bool IsMainWorkerScript() const {
719     return mIsMainScript && mWorkerScriptType == WorkerScript;
720   }
721 
CancelMainThreadWithBindingAborted()722   void CancelMainThreadWithBindingAborted() {
723     CancelMainThread(NS_BINDING_ABORTED);
724   }
725 
CancelMainThread(nsresult aCancelResult)726   void CancelMainThread(nsresult aCancelResult) {
727     AssertIsOnMainThread();
728 
729     if (mCanceledMainThread) {
730       return;
731     }
732 
733     mCanceledMainThread = true;
734 
735     if (mCacheCreator) {
736       MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
737       DeleteCache();
738     }
739 
740     // Cancel all the channels that were already opened.
741     for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
742       ScriptLoadInfo& loadInfo = mLoadInfos[index];
743 
744       // If promise or channel is non-null, their failures will lead to
745       // LoadingFinished being called.
746       bool callLoadingFinished = true;
747 
748       if (loadInfo.mCachePromise) {
749         MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
750         loadInfo.mCachePromise->MaybeReject(aCancelResult);
751         loadInfo.mCachePromise = nullptr;
752         callLoadingFinished = false;
753       }
754 
755       if (loadInfo.mChannel) {
756         if (NS_SUCCEEDED(loadInfo.mChannel->Cancel(aCancelResult))) {
757           callLoadingFinished = false;
758         } else {
759           NS_WARNING("Failed to cancel channel!");
760         }
761       }
762 
763       if (callLoadingFinished && !loadInfo.Finished()) {
764         LoadingFinished(index, aCancelResult);
765       }
766     }
767 
768     ExecuteFinishedScripts();
769   }
770 
DeleteCache()771   void DeleteCache() {
772     AssertIsOnMainThread();
773 
774     if (!mCacheCreator) {
775       return;
776     }
777 
778     mCacheCreator->DeleteCache();
779     mCacheCreator = nullptr;
780   }
781 
RunInternal()782   nsresult RunInternal() {
783     AssertIsOnMainThread();
784 
785     if (IsMainWorkerScript() && mWorkerPrivate->IsServiceWorker()) {
786       mWorkerPrivate->SetLoadingWorkerScript(true);
787     }
788 
789     if (!mWorkerPrivate->IsServiceWorker() ||
790         !mWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
791       for (uint32_t index = 0, len = mLoadInfos.Length(); index < len;
792            ++index) {
793         nsresult rv = LoadScript(index);
794         if (NS_WARN_IF(NS_FAILED(rv))) {
795           LoadingFinished(index, rv);
796           return rv;
797         }
798       }
799 
800       return NS_OK;
801     }
802 
803     MOZ_ASSERT(!mCacheCreator);
804     mCacheCreator = new CacheCreator(mWorkerPrivate);
805 
806     for (uint32_t index = 0, len = mLoadInfos.Length(); index < len; ++index) {
807       RefPtr<CacheScriptLoader> loader = new CacheScriptLoader(
808           mWorkerPrivate, mLoadInfos[index], index, IsMainWorkerScript(), this);
809       mCacheCreator->AddLoader(loader);
810     }
811 
812     // The worker may have a null principal on first load, but in that case its
813     // parent definitely will have one.
814     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
815     if (!principal) {
816       WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
817       MOZ_ASSERT(parentWorker, "Must have a parent!");
818       principal = parentWorker->GetPrincipal();
819     }
820 
821     nsresult rv = mCacheCreator->Load(principal);
822     if (NS_WARN_IF(NS_FAILED(rv))) {
823       return rv;
824     }
825 
826     return NS_OK;
827   }
828 
LoadScript(uint32_t aIndex)829   nsresult LoadScript(uint32_t aIndex) {
830     AssertIsOnMainThread();
831     MOZ_ASSERT(aIndex < mLoadInfos.Length());
832     MOZ_ASSERT_IF(IsMainWorkerScript(), mWorkerScriptType != DebuggerScript);
833 
834     WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
835 
836     // For JavaScript debugging, the devtools server must run on the same
837     // thread as the debuggee, indicating the worker uses content principal.
838     // However, in Bug 863246, web content will no longer be able to load
839     // resource:// URIs by default, so we need system principal to load
840     // debugger scripts.
841     nsIPrincipal* principal = (mWorkerScriptType == DebuggerScript)
842                                   ? nsContentUtils::GetSystemPrincipal()
843                                   : mWorkerPrivate->GetPrincipal();
844 
845     nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
846     MOZ_DIAGNOSTIC_ASSERT(principal);
847 
848     NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup, principal),
849                    NS_ERROR_FAILURE);
850 
851     // Figure out our base URI.
852     nsCOMPtr<nsIURI> baseURI = GetBaseURI(mIsMainScript, mWorkerPrivate);
853 
854     // May be null.
855     nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
856 
857     nsCOMPtr<nsIChannel> channel;
858     if (IsMainWorkerScript()) {
859       // May be null.
860       channel = mWorkerPrivate->ForgetWorkerChannel();
861     }
862 
863     nsCOMPtr<nsIIOService> ios(do_GetIOService());
864 
865     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
866     NS_ASSERTION(secMan, "This should never be null!");
867 
868     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
869     nsresult& rv = loadInfo.mLoadResult;
870 
871     nsLoadFlags loadFlags = loadInfo.mLoadFlags;
872 
873     // Get the top-level worker.
874     WorkerPrivate* topWorkerPrivate = mWorkerPrivate;
875     WorkerPrivate* parent = topWorkerPrivate->GetParent();
876     while (parent) {
877       topWorkerPrivate = parent;
878       parent = topWorkerPrivate->GetParent();
879     }
880 
881     // If the top-level worker is a dedicated worker and has a window, and the
882     // window has a docshell, the caching behavior of this worker should match
883     // that of that docshell.
884     if (topWorkerPrivate->IsDedicatedWorker()) {
885       nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
886       if (window) {
887         nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
888         if (docShell) {
889           nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
890           NS_ENSURE_SUCCESS(rv, rv);
891         }
892       }
893     }
894 
895     if (!channel) {
896       // Only top level workers' main script use the document charset for the
897       // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
898       bool useDefaultEncoding = !(!parentWorker && IsMainWorkerScript());
899       rv = ChannelFromScriptURL(
900           principal, baseURI, parentDoc, mWorkerPrivate, loadGroup, ios, secMan,
901           loadInfo.mURL, mClientInfo, mController, IsMainWorkerScript(),
902           mWorkerScriptType, mWorkerPrivate->ContentPolicyType(), loadFlags,
903           useDefaultEncoding, getter_AddRefs(channel));
904       if (NS_WARN_IF(NS_FAILED(rv))) {
905         return rv;
906       }
907     }
908 
909     // We need to know which index we're on in OnStreamComplete so we know
910     // where to put the result.
911     RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
912 
913     // We don't care about progress so just use the simple stream loader for
914     // OnStreamComplete notification only.
915     nsCOMPtr<nsIStreamLoader> loader;
916     rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
917     if (NS_WARN_IF(NS_FAILED(rv))) {
918       return rv;
919     }
920 
921     if (IsMainWorkerScript()) {
922       MOZ_DIAGNOSTIC_ASSERT(loadInfo.mReservedClientInfo.isSome());
923       rv = AddClientChannelHelper(channel, Move(loadInfo.mReservedClientInfo),
924                                   Maybe<ClientInfo>(),
925                                   mWorkerPrivate->HybridEventTarget());
926       if (NS_WARN_IF(NS_FAILED(rv))) {
927         return rv;
928       }
929     }
930 
931     if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
932       rv = channel->AsyncOpen2(loader);
933       if (NS_WARN_IF(NS_FAILED(rv))) {
934         return rv;
935       }
936     } else {
937       nsCOMPtr<nsIOutputStream> writer;
938 
939       // In case we return early.
940       loadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
941 
942       rv = NS_NewPipe(
943           getter_AddRefs(loadInfo.mCacheReadStream), getter_AddRefs(writer), 0,
944           UINT32_MAX,    // unlimited size to avoid writer WOULD_BLOCK case
945           true, false);  // non-blocking reader, blocking writer
946       if (NS_WARN_IF(NS_FAILED(rv))) {
947         return rv;
948       }
949 
950       nsCOMPtr<nsIStreamListenerTee> tee =
951           do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
952       rv = tee->Init(loader, writer, listener);
953       if (NS_WARN_IF(NS_FAILED(rv))) {
954         return rv;
955       }
956 
957       nsresult rv = channel->AsyncOpen2(tee);
958       if (NS_WARN_IF(NS_FAILED(rv))) {
959         return rv;
960       }
961     }
962 
963     loadInfo.mChannel.swap(channel);
964 
965     return NS_OK;
966   }
967 
OnStreamCompleteInternal(nsIStreamLoader * aLoader,nsresult aStatus,uint32_t aStringLen,const uint8_t * aString,ScriptLoadInfo & aLoadInfo)968   nsresult OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsresult aStatus,
969                                     uint32_t aStringLen, const uint8_t* aString,
970                                     ScriptLoadInfo& aLoadInfo) {
971     AssertIsOnMainThread();
972 
973     if (!aLoadInfo.mChannel) {
974       return NS_BINDING_ABORTED;
975     }
976 
977     aLoadInfo.mChannel = nullptr;
978 
979     if (NS_FAILED(aStatus)) {
980       return aStatus;
981     }
982 
983     NS_ASSERTION(aString, "This should never be null!");
984 
985     nsCOMPtr<nsIRequest> request;
986     nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
987     NS_ENSURE_SUCCESS(rv, rv);
988 
989     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
990     MOZ_ASSERT(channel);
991 
992     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
993     NS_ASSERTION(ssm, "Should never be null!");
994 
995     nsCOMPtr<nsIPrincipal> channelPrincipal;
996     rv = ssm->GetChannelResultPrincipal(channel,
997                                         getter_AddRefs(channelPrincipal));
998     if (NS_WARN_IF(NS_FAILED(rv))) {
999       return rv;
1000     }
1001 
1002     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1003     if (!principal) {
1004       WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
1005       MOZ_ASSERT(parentWorker, "Must have a parent!");
1006       principal = parentWorker->GetPrincipal();
1007     }
1008 
1009 #ifdef DEBUG
1010     if (IsMainWorkerScript()) {
1011       nsCOMPtr<nsIPrincipal> loadingPrincipal =
1012           mWorkerPrivate->GetLoadingPrincipal();
1013       // if we are not in a ServiceWorker, and the principal is not null, then
1014       // the loading principal must subsume the worker principal if it is not a
1015       // nullPrincipal (sandbox).
1016       MOZ_ASSERT(!loadingPrincipal || loadingPrincipal->GetIsNullPrincipal() ||
1017                  principal->GetIsNullPrincipal() ||
1018                  loadingPrincipal->Subsumes(principal));
1019     }
1020 #endif
1021 
1022     // We don't mute the main worker script becase we've already done
1023     // same-origin checks on them so we should be able to see their errors.
1024     // Note that for data: url, where we allow it through the same-origin check
1025     // but then give it a different origin.
1026     aLoadInfo.mMutedErrorFlag.emplace(
1027         IsMainWorkerScript() ? false : !principal->Subsumes(channelPrincipal));
1028 
1029     // Make sure we're not seeing the result of a 404 or something by checking
1030     // the 'requestSucceeded' attribute on the http channel.
1031     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
1032     nsAutoCString tCspHeaderValue, tCspROHeaderValue, tRPHeaderCValue;
1033 
1034     if (httpChannel) {
1035       bool requestSucceeded;
1036       rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
1037       NS_ENSURE_SUCCESS(rv, rv);
1038 
1039       if (!requestSucceeded) {
1040         return NS_ERROR_NOT_AVAILABLE;
1041       }
1042 
1043       Unused << httpChannel->GetResponseHeader(
1044           NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue);
1045 
1046       Unused << httpChannel->GetResponseHeader(
1047           NS_LITERAL_CSTRING("content-security-policy-report-only"),
1048           tCspROHeaderValue);
1049 
1050       Unused << httpChannel->GetResponseHeader(
1051           NS_LITERAL_CSTRING("referrer-policy"), tRPHeaderCValue);
1052     }
1053 
1054     // May be null.
1055     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
1056 
1057     // Use the regular ScriptLoader for this grunt work! Should be just fine
1058     // because we're running on the main thread.
1059     // Unlike <script> tags, Worker scripts are always decoded as UTF-8,
1060     // per spec. So we explicitly pass in the charset hint.
1061     rv = ScriptLoader::ConvertToUTF16(
1062         aLoadInfo.mChannel, aString, aStringLen, NS_LITERAL_STRING("UTF-8"),
1063         parentDoc, aLoadInfo.mScriptTextBuf, aLoadInfo.mScriptTextLength);
1064     if (NS_FAILED(rv)) {
1065       return rv;
1066     }
1067 
1068     if (!aLoadInfo.mScriptTextLength && !aLoadInfo.mScriptTextBuf) {
1069       nsContentUtils::ReportToConsole(
1070           nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), parentDoc,
1071           nsContentUtils::eDOM_PROPERTIES, "EmptyWorkerSourceWarning");
1072     } else if (!aLoadInfo.mScriptTextBuf) {
1073       return NS_ERROR_FAILURE;
1074     }
1075 
1076     // Figure out what we actually loaded.
1077     nsCOMPtr<nsIURI> finalURI;
1078     rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
1079     NS_ENSURE_SUCCESS(rv, rv);
1080 
1081     nsCString filename;
1082     rv = finalURI->GetSpec(filename);
1083     NS_ENSURE_SUCCESS(rv, rv);
1084 
1085     if (!filename.IsEmpty()) {
1086       // This will help callers figure out what their script url resolved to in
1087       // case of errors.
1088       aLoadInfo.mURL.Assign(NS_ConvertUTF8toUTF16(filename));
1089     }
1090 
1091     nsCOMPtr<nsILoadInfo> chanLoadInfo = channel->GetLoadInfo();
1092     if (chanLoadInfo && chanLoadInfo->GetEnforceSRI()) {
1093       // importScripts() and the Worker constructor do not support integrity
1094       // metadata
1095       //  (or any fetch options). Until then, we can just block.
1096       //  If we ever have those data in the future, we'll have to the check to
1097       //  by using the SRICheck module
1098       MOZ_LOG(
1099           SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
1100           ("Scriptloader::Load, SRI required but not supported in workers"));
1101       nsCOMPtr<nsIContentSecurityPolicy> wcsp;
1102       chanLoadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(wcsp));
1103       MOZ_ASSERT(wcsp, "We sould have a CSP for the worker here");
1104       if (wcsp) {
1105         wcsp->LogViolationDetails(
1106             nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
1107             aLoadInfo.mURL, EmptyString(), 0, EmptyString(), EmptyString());
1108       }
1109       return NS_ERROR_SRI_CORRUPT;
1110     }
1111 
1112     // Update the principal of the worker and its base URI if we just loaded the
1113     // worker's primary script.
1114     if (IsMainWorkerScript()) {
1115       // Take care of the base URI first.
1116       mWorkerPrivate->SetBaseURI(finalURI);
1117 
1118       // Store the channel info if needed.
1119       mWorkerPrivate->InitChannelInfo(channel);
1120 
1121       // Our final channel principal should match the loading principal
1122       // in terms of the origin.  This used to be an assert, but it seems
1123       // there are some rare cases where this check can fail in practice.
1124       // Perhaps some browser script setting nsIChannel.owner, etc.
1125       NS_ENSURE_TRUE(mWorkerPrivate->FinalChannelPrincipalIsValid(channel),
1126                      NS_ERROR_FAILURE);
1127 
1128       // However, we must still override the principal since the nsIPrincipal
1129       // URL may be different due to same-origin redirects.  Unfortunately this
1130       // URL must exactly match the final worker script URL in order to
1131       // properly set the referrer header on fetch/xhr requests.  If bug 1340694
1132       // is ever fixed this can be removed.
1133       rv = mWorkerPrivate->SetPrincipalFromChannel(channel);
1134       NS_ENSURE_SUCCESS(rv, rv);
1135 
1136       nsCOMPtr<nsIContentSecurityPolicy> csp = mWorkerPrivate->GetCSP();
1137       // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
1138       // should get it from the HTTP headers on the worker script.
1139       if (CSPService::sCSPEnabled) {
1140         if (!csp) {
1141           rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
1142                                                       tCspROHeaderValue);
1143           NS_ENSURE_SUCCESS(rv, rv);
1144         } else {
1145           csp->EnsureEventTarget(mWorkerPrivate->MainThreadEventTarget());
1146         }
1147       }
1148 
1149       mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue);
1150 
1151       WorkerPrivate* parent = mWorkerPrivate->GetParent();
1152       if (parent) {
1153         // XHR Params Allowed
1154         mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
1155       }
1156 
1157       if (chanLoadInfo) {
1158         mController = chanLoadInfo->GetController();
1159       }
1160 
1161       // If we are loading a blob URL we must inherit the controller
1162       // from the parent.  This is a bit odd as the blob URL may have
1163       // been created in a different context with a different controller.
1164       // For now, though, this is what the spec says.  See:
1165       //
1166       // https://github.com/w3c/ServiceWorker/issues/1261
1167       //
1168       if (IsBlobURI(mWorkerPrivate->GetBaseURI())) {
1169         MOZ_DIAGNOSTIC_ASSERT(mController.isNothing());
1170         mController = mWorkerPrivate->GetParentController();
1171       }
1172     }
1173 
1174     return NS_OK;
1175   }
1176 
DataReceivedFromCache(uint32_t aIndex,const uint8_t * aString,uint32_t aStringLen,const mozilla::dom::ChannelInfo & aChannelInfo,UniquePtr<PrincipalInfo> aPrincipalInfo,const nsACString & aCSPHeaderValue,const nsACString & aCSPReportOnlyHeaderValue,const nsACString & aReferrerPolicyHeaderValue)1177   void DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
1178                              uint32_t aStringLen,
1179                              const mozilla::dom::ChannelInfo& aChannelInfo,
1180                              UniquePtr<PrincipalInfo> aPrincipalInfo,
1181                              const nsACString& aCSPHeaderValue,
1182                              const nsACString& aCSPReportOnlyHeaderValue,
1183                              const nsACString& aReferrerPolicyHeaderValue) {
1184     AssertIsOnMainThread();
1185     MOZ_ASSERT(aIndex < mLoadInfos.Length());
1186     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
1187     MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
1188 
1189     nsCOMPtr<nsIPrincipal> responsePrincipal =
1190         PrincipalInfoToPrincipal(*aPrincipalInfo);
1191     MOZ_DIAGNOSTIC_ASSERT(responsePrincipal);
1192 
1193     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1194     if (!principal) {
1195       WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
1196       MOZ_ASSERT(parentWorker, "Must have a parent!");
1197       principal = parentWorker->GetPrincipal();
1198     }
1199 
1200     loadInfo.mMutedErrorFlag.emplace(!principal->Subsumes(responsePrincipal));
1201 
1202     // May be null.
1203     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
1204 
1205     MOZ_ASSERT(!loadInfo.mScriptTextBuf);
1206 
1207     nsresult rv = ScriptLoader::ConvertToUTF16(
1208         nullptr, aString, aStringLen, NS_LITERAL_STRING("UTF-8"), parentDoc,
1209         loadInfo.mScriptTextBuf, loadInfo.mScriptTextLength);
1210     if (NS_SUCCEEDED(rv) && IsMainWorkerScript()) {
1211       nsCOMPtr<nsIURI> finalURI;
1212       rv = NS_NewURI(getter_AddRefs(finalURI), loadInfo.mFullURL, nullptr,
1213                      nullptr);
1214       if (NS_SUCCEEDED(rv)) {
1215         mWorkerPrivate->SetBaseURI(finalURI);
1216       }
1217 
1218 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1219       nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1220       MOZ_DIAGNOSTIC_ASSERT(principal);
1221 
1222       bool equal = false;
1223       MOZ_ALWAYS_SUCCEEDS(responsePrincipal->Equals(principal, &equal));
1224       MOZ_DIAGNOSTIC_ASSERT(equal);
1225 
1226       nsCOMPtr<nsIContentSecurityPolicy> csp;
1227       MOZ_ALWAYS_SUCCEEDS(responsePrincipal->GetCsp(getter_AddRefs(csp)));
1228       MOZ_DIAGNOSTIC_ASSERT(!csp);
1229 #endif
1230 
1231       mWorkerPrivate->InitChannelInfo(aChannelInfo);
1232 
1233       nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
1234       MOZ_DIAGNOSTIC_ASSERT(loadGroup);
1235 
1236       // Override the principal on the WorkerPrivate.  This is only necessary
1237       // in order to get a principal with exactly the correct URL.  The fetch
1238       // referrer logic depends on the WorkerPrivate principal having a URL
1239       // that matches the worker script URL.  If bug 1340694 is ever fixed
1240       // this can be removed.
1241       rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal,
1242                                                     loadGroup);
1243       MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1244 
1245       rv = mWorkerPrivate->SetCSPFromHeaderValues(aCSPHeaderValue,
1246                                                   aCSPReportOnlyHeaderValue);
1247       MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1248 
1249       mWorkerPrivate->SetReferrerPolicyFromHeaderValue(
1250           aReferrerPolicyHeaderValue);
1251     }
1252 
1253     if (NS_SUCCEEDED(rv)) {
1254       DataReceived();
1255     }
1256 
1257     LoadingFinished(aIndex, rv);
1258   }
1259 
DataReceived()1260   void DataReceived() {
1261     if (IsMainWorkerScript()) {
1262       WorkerPrivate* parent = mWorkerPrivate->GetParent();
1263 
1264       if (parent) {
1265         // XHR Params Allowed
1266         mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
1267 
1268         // Set Eval and ContentSecurityPolicy
1269         mWorkerPrivate->SetCSP(parent->GetCSP());
1270         mWorkerPrivate->SetEvalAllowed(parent->IsEvalAllowed());
1271       }
1272     }
1273   }
1274 
ExecuteFinishedScripts()1275   void ExecuteFinishedScripts() {
1276     AssertIsOnMainThread();
1277 
1278     if (IsMainWorkerScript()) {
1279       mWorkerPrivate->WorkerScriptLoaded();
1280     }
1281 
1282     uint32_t firstIndex = UINT32_MAX;
1283     uint32_t lastIndex = UINT32_MAX;
1284 
1285     // Find firstIndex based on whether mExecutionScheduled is unset.
1286     for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
1287       if (!mLoadInfos[index].mExecutionScheduled) {
1288         firstIndex = index;
1289         break;
1290       }
1291     }
1292 
1293     // Find lastIndex based on whether mChannel is set, and update
1294     // mExecutionScheduled on the ones we're about to schedule.
1295     if (firstIndex != UINT32_MAX) {
1296       for (uint32_t index = firstIndex; index < mLoadInfos.Length(); index++) {
1297         ScriptLoadInfo& loadInfo = mLoadInfos[index];
1298 
1299         if (!loadInfo.Finished()) {
1300           break;
1301         }
1302 
1303         // We can execute this one.
1304         loadInfo.mExecutionScheduled = true;
1305 
1306         lastIndex = index;
1307       }
1308     }
1309 
1310     // This is the last index, we can unused things before the exection of the
1311     // script and the stopping of the sync loop.
1312     if (lastIndex == mLoadInfos.Length() - 1) {
1313       mCacheCreator = nullptr;
1314     }
1315 
1316     if (firstIndex != UINT32_MAX && lastIndex != UINT32_MAX) {
1317       RefPtr<ScriptExecutorRunnable> runnable = new ScriptExecutorRunnable(
1318           *this, mSyncLoopTarget, IsMainWorkerScript(), firstIndex, lastIndex);
1319       if (!runnable->Dispatch()) {
1320         MOZ_ASSERT(false, "This should never fail!");
1321       }
1322     }
1323   }
1324 };
1325 
1326 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable, nsINamed)
1327 
1328 class MOZ_STACK_CLASS ScriptLoaderHolder final : public WorkerHolder {
1329   // Raw pointer because this holder object follows the mRunnable life-time.
1330   ScriptLoaderRunnable* mRunnable;
1331 
1332  public:
ScriptLoaderHolder(ScriptLoaderRunnable * aRunnable)1333   explicit ScriptLoaderHolder(ScriptLoaderRunnable* aRunnable)
1334       : WorkerHolder("ScriptLoaderHolder"), mRunnable(aRunnable) {
1335     MOZ_ASSERT(aRunnable);
1336   }
1337 
Notify(WorkerStatus aStatus)1338   virtual bool Notify(WorkerStatus aStatus) override {
1339     mRunnable->Notify(aStatus);
1340     return true;
1341   }
1342 };
1343 
1344 NS_IMETHODIMP
OnStreamComplete(nsIStreamLoader * aLoader,nsISupports * aContext,nsresult aStatus,uint32_t aStringLen,const uint8_t * aString)1345 LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader,
1346                                  nsISupports* aContext, nsresult aStatus,
1347                                  uint32_t aStringLen, const uint8_t* aString) {
1348   return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen,
1349                                      aString);
1350 }
1351 
1352 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest,nsISupports * aContext)1353 LoaderListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) {
1354   return mRunnable->OnStartRequest(aRequest, mIndex);
1355 }
1356 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1357 void CachePromiseHandler::ResolvedCallback(JSContext* aCx,
1358                                            JS::Handle<JS::Value> aValue) {
1359   AssertIsOnMainThread();
1360   // May already have been canceled by CacheScriptLoader::Fail from
1361   // CancelMainThread.
1362   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1363              mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1364   MOZ_ASSERT_IF(mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel,
1365                 !mLoadInfo.mCachePromise);
1366 
1367   if (mLoadInfo.mCachePromise) {
1368     mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1369     mLoadInfo.mCachePromise = nullptr;
1370     mRunnable->MaybeExecuteFinishedScripts(mIndex);
1371   }
1372 }
1373 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1374 void CachePromiseHandler::RejectedCallback(JSContext* aCx,
1375                                            JS::Handle<JS::Value> aValue) {
1376   AssertIsOnMainThread();
1377   // May already have been canceled by CacheScriptLoader::Fail from
1378   // CancelMainThread.
1379   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1380              mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1381   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1382 
1383   mLoadInfo.mCachePromise = nullptr;
1384 
1385   // This will delete the cache object and will call LoadingFinished() with an
1386   // error for each ongoing operation.
1387   mRunnable->DeleteCache();
1388 }
1389 
CreateCacheStorage(nsIPrincipal * aPrincipal)1390 nsresult CacheCreator::CreateCacheStorage(nsIPrincipal* aPrincipal) {
1391   AssertIsOnMainThread();
1392   MOZ_ASSERT(!mCacheStorage);
1393   MOZ_ASSERT(aPrincipal);
1394 
1395   nsIXPConnect* xpc = nsContentUtils::XPConnect();
1396   MOZ_ASSERT(xpc, "This should never be null!");
1397 
1398   mozilla::AutoSafeJSContext cx;
1399   JS::Rooted<JSObject*> sandbox(cx);
1400   nsresult rv = xpc->CreateSandbox(cx, aPrincipal, sandbox.address());
1401   if (NS_WARN_IF(NS_FAILED(rv))) {
1402     return rv;
1403   }
1404 
1405   mSandboxGlobalObject = xpc::NativeGlobal(sandbox);
1406   if (NS_WARN_IF(!mSandboxGlobalObject)) {
1407     return NS_ERROR_FAILURE;
1408   }
1409 
1410   // If we're in private browsing mode, don't even try to create the
1411   // CacheStorage.  Instead, just fail immediately to terminate the
1412   // ServiceWorker load.
1413   if (NS_WARN_IF(mOriginAttributes.mPrivateBrowsingId > 0)) {
1414     return NS_ERROR_DOM_SECURITY_ERR;
1415   }
1416 
1417   // Create a CacheStorage bypassing its trusted origin checks.  The
1418   // ServiceWorker has already performed its own checks before getting
1419   // to this point.
1420   ErrorResult error;
1421   mCacheStorage = CacheStorage::CreateOnMainThread(
1422       mozilla::dom::cache::CHROME_ONLY_NAMESPACE, mSandboxGlobalObject,
1423       aPrincipal, false, /* privateBrowsing can't be true here */
1424       true /* force trusted origin */, error);
1425   if (NS_WARN_IF(error.Failed())) {
1426     return error.StealNSResult();
1427   }
1428 
1429   return NS_OK;
1430 }
1431 
Load(nsIPrincipal * aPrincipal)1432 nsresult CacheCreator::Load(nsIPrincipal* aPrincipal) {
1433   AssertIsOnMainThread();
1434   MOZ_ASSERT(!mLoaders.IsEmpty());
1435 
1436   nsresult rv = CreateCacheStorage(aPrincipal);
1437   if (NS_WARN_IF(NS_FAILED(rv))) {
1438     return rv;
1439   }
1440 
1441   ErrorResult error;
1442   MOZ_ASSERT(!mCacheName.IsEmpty());
1443   RefPtr<Promise> promise = mCacheStorage->Open(mCacheName, error);
1444   if (NS_WARN_IF(error.Failed())) {
1445     return error.StealNSResult();
1446   }
1447 
1448   promise->AppendNativeHandler(this);
1449   return NS_OK;
1450 }
1451 
FailLoaders(nsresult aRv)1452 void CacheCreator::FailLoaders(nsresult aRv) {
1453   AssertIsOnMainThread();
1454 
1455   // Fail() can call LoadingFinished() which may call ExecuteFinishedScripts()
1456   // which sets mCacheCreator to null, so hold a ref.
1457   RefPtr<CacheCreator> kungfuDeathGrip = this;
1458 
1459   for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
1460     mLoaders[i]->Fail(aRv);
1461   }
1462 
1463   mLoaders.Clear();
1464 }
1465 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1466 void CacheCreator::RejectedCallback(JSContext* aCx,
1467                                     JS::Handle<JS::Value> aValue) {
1468   AssertIsOnMainThread();
1469   FailLoaders(NS_ERROR_FAILURE);
1470 }
1471 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1472 void CacheCreator::ResolvedCallback(JSContext* aCx,
1473                                     JS::Handle<JS::Value> aValue) {
1474   AssertIsOnMainThread();
1475 
1476   if (!aValue.isObject()) {
1477     FailLoaders(NS_ERROR_FAILURE);
1478     return;
1479   }
1480 
1481   JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
1482   Cache* cache = nullptr;
1483   nsresult rv = UNWRAP_OBJECT(Cache, &obj, cache);
1484   if (NS_WARN_IF(NS_FAILED(rv))) {
1485     FailLoaders(NS_ERROR_FAILURE);
1486     return;
1487   }
1488 
1489   mCache = cache;
1490   MOZ_DIAGNOSTIC_ASSERT(mCache);
1491 
1492   // If the worker is canceled, CancelMainThread() will have cleared the
1493   // loaders via DeleteCache().
1494   for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
1495     MOZ_DIAGNOSTIC_ASSERT(mLoaders[i]);
1496     mLoaders[i]->Load(cache);
1497   }
1498 }
1499 
DeleteCache()1500 void CacheCreator::DeleteCache() {
1501   AssertIsOnMainThread();
1502 
1503   // This is called when the load is canceled which can occur before
1504   // mCacheStorage is initialized.
1505   if (mCacheStorage) {
1506     // It's safe to do this while Cache::Match() and Cache::Put() calls are
1507     // running.
1508     RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, IgnoreErrors());
1509 
1510     // We don't care to know the result of the promise object.
1511   }
1512 
1513   // Always call this here to ensure the loaders array is cleared.
1514   FailLoaders(NS_ERROR_FAILURE);
1515 }
1516 
Fail(nsresult aRv)1517 void CacheScriptLoader::Fail(nsresult aRv) {
1518   AssertIsOnMainThread();
1519   MOZ_ASSERT(NS_FAILED(aRv));
1520 
1521   if (mFailed) {
1522     return;
1523   }
1524 
1525   mFailed = true;
1526 
1527   if (mPump) {
1528     MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
1529     mPump->Cancel(aRv);
1530     mPump = nullptr;
1531   }
1532 
1533   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1534 
1535   // Stop if the load was aborted on the main thread.
1536   // Can't use Finished() because mCachePromise may still be true.
1537   if (mLoadInfo.mLoadingFinished) {
1538     MOZ_ASSERT(!mLoadInfo.mChannel);
1539     MOZ_ASSERT_IF(mLoadInfo.mCachePromise,
1540                   mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1541                       mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1542     return;
1543   }
1544 
1545   mRunnable->LoadingFinished(mIndex, aRv);
1546 }
1547 
Load(Cache * aCache)1548 void CacheScriptLoader::Load(Cache* aCache) {
1549   AssertIsOnMainThread();
1550   MOZ_ASSERT(aCache);
1551 
1552   nsCOMPtr<nsIURI> uri;
1553   nsresult rv =
1554       NS_NewURI(getter_AddRefs(uri), mLoadInfo.mURL, nullptr, mBaseURI);
1555   if (NS_WARN_IF(NS_FAILED(rv))) {
1556     Fail(rv);
1557     return;
1558   }
1559 
1560   nsAutoCString spec;
1561   rv = uri->GetSpec(spec);
1562   if (NS_WARN_IF(NS_FAILED(rv))) {
1563     Fail(rv);
1564     return;
1565   }
1566 
1567   MOZ_ASSERT(mLoadInfo.mFullURL.IsEmpty());
1568   CopyUTF8toUTF16(spec, mLoadInfo.mFullURL);
1569 
1570   mozilla::dom::RequestOrUSVString request;
1571   request.SetAsUSVString().Rebind(mLoadInfo.mFullURL.Data(),
1572                                   mLoadInfo.mFullURL.Length());
1573 
1574   mozilla::dom::CacheQueryOptions params;
1575 
1576   // This JSContext will not end up executing JS code because here there are
1577   // no ReadableStreams involved.
1578   AutoJSAPI jsapi;
1579   jsapi.Init();
1580 
1581   ErrorResult error;
1582   RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
1583   if (NS_WARN_IF(error.Failed())) {
1584     Fail(error.StealNSResult());
1585     return;
1586   }
1587 
1588   promise->AppendNativeHandler(this);
1589 }
1590 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1591 void CacheScriptLoader::RejectedCallback(JSContext* aCx,
1592                                          JS::Handle<JS::Value> aValue) {
1593   AssertIsOnMainThread();
1594   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
1595   Fail(NS_ERROR_FAILURE);
1596 }
1597 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1598 void CacheScriptLoader::ResolvedCallback(JSContext* aCx,
1599                                          JS::Handle<JS::Value> aValue) {
1600   AssertIsOnMainThread();
1601   // If we have already called 'Fail', we should not proceed.
1602   if (mFailed) {
1603     return;
1604   }
1605 
1606   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
1607 
1608   nsresult rv;
1609 
1610   if (aValue.isUndefined()) {
1611     mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
1612     rv = mRunnable->LoadScript(mIndex);
1613     if (NS_WARN_IF(NS_FAILED(rv))) {
1614       Fail(rv);
1615     }
1616     return;
1617   }
1618 
1619   MOZ_ASSERT(aValue.isObject());
1620 
1621   JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
1622   mozilla::dom::Response* response = nullptr;
1623   rv = UNWRAP_OBJECT(Response, &obj, response);
1624   if (NS_WARN_IF(NS_FAILED(rv))) {
1625     Fail(rv);
1626     return;
1627   }
1628 
1629   InternalHeaders* headers = response->GetInternalHeaders();
1630 
1631   headers->Get(NS_LITERAL_CSTRING("content-security-policy"), mCSPHeaderValue,
1632                IgnoreErrors());
1633   headers->Get(NS_LITERAL_CSTRING("content-security-policy-report-only"),
1634                mCSPReportOnlyHeaderValue, IgnoreErrors());
1635   headers->Get(NS_LITERAL_CSTRING("referrer-policy"),
1636                mReferrerPolicyHeaderValue, IgnoreErrors());
1637 
1638   nsCOMPtr<nsIInputStream> inputStream;
1639   response->GetBody(getter_AddRefs(inputStream));
1640   mChannelInfo = response->GetChannelInfo();
1641   const UniquePtr<PrincipalInfo>& pInfo = response->GetPrincipalInfo();
1642   if (pInfo) {
1643     mPrincipalInfo = mozilla::MakeUnique<PrincipalInfo>(*pInfo);
1644   }
1645 
1646   if (!inputStream) {
1647     mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1648     mRunnable->DataReceivedFromCache(
1649         mIndex, (uint8_t*)"", 0, mChannelInfo, Move(mPrincipalInfo),
1650         mCSPHeaderValue, mCSPReportOnlyHeaderValue, mReferrerPolicyHeaderValue);
1651     return;
1652   }
1653 
1654   MOZ_ASSERT(!mPump);
1655   rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream.forget(),
1656                              0,     /* default segsize */
1657                              0,     /* default segcount */
1658                              false, /* default closeWhenDone */
1659                              mMainThreadEventTarget);
1660   if (NS_WARN_IF(NS_FAILED(rv))) {
1661     Fail(rv);
1662     return;
1663   }
1664 
1665   nsCOMPtr<nsIStreamLoader> loader;
1666   rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
1667   if (NS_WARN_IF(NS_FAILED(rv))) {
1668     Fail(rv);
1669     return;
1670   }
1671 
1672   rv = mPump->AsyncRead(loader, nullptr);
1673   if (NS_WARN_IF(NS_FAILED(rv))) {
1674     mPump = nullptr;
1675     Fail(rv);
1676     return;
1677   }
1678 
1679   nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
1680   if (rr) {
1681     nsCOMPtr<nsIEventTarget> sts =
1682         do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1683     rv = rr->RetargetDeliveryTo(sts);
1684     if (NS_FAILED(rv)) {
1685       NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
1686     }
1687   }
1688 
1689   mLoadInfo.mCacheStatus = ScriptLoadInfo::ReadingFromCache;
1690 }
1691 
1692 NS_IMETHODIMP
OnStreamComplete(nsIStreamLoader * aLoader,nsISupports * aContext,nsresult aStatus,uint32_t aStringLen,const uint8_t * aString)1693 CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
1694                                     nsISupports* aContext, nsresult aStatus,
1695                                     uint32_t aStringLen,
1696                                     const uint8_t* aString) {
1697   AssertIsOnMainThread();
1698 
1699   mPump = nullptr;
1700 
1701   if (NS_FAILED(aStatus)) {
1702     MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache ||
1703                mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1704     Fail(aStatus);
1705     return NS_OK;
1706   }
1707 
1708   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
1709   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1710 
1711   MOZ_ASSERT(mPrincipalInfo);
1712   mRunnable->DataReceivedFromCache(
1713       mIndex, aString, aStringLen, mChannelInfo, Move(mPrincipalInfo),
1714       mCSPHeaderValue, mCSPReportOnlyHeaderValue, mReferrerPolicyHeaderValue);
1715   return NS_OK;
1716 }
1717 
1718 class ChannelGetterRunnable final : public WorkerMainThreadRunnable {
1719   const nsAString& mScriptURL;
1720   const ClientInfo mClientInfo;
1721   WorkerLoadInfo& mLoadInfo;
1722   nsresult mResult;
1723 
1724  public:
ChannelGetterRunnable(WorkerPrivate * aParentWorker,const nsAString & aScriptURL,WorkerLoadInfo & aLoadInfo)1725   ChannelGetterRunnable(WorkerPrivate* aParentWorker,
1726                         const nsAString& aScriptURL, WorkerLoadInfo& aLoadInfo)
1727       : WorkerMainThreadRunnable(
1728             aParentWorker, NS_LITERAL_CSTRING("ScriptLoader :: ChannelGetter")),
1729         mScriptURL(aScriptURL),
1730         mClientInfo(aParentWorker->GetClientInfo()),
1731         mLoadInfo(aLoadInfo),
1732         mResult(NS_ERROR_FAILURE) {
1733     MOZ_ASSERT(aParentWorker);
1734     aParentWorker->AssertIsOnWorkerThread();
1735   }
1736 
MainThreadRun()1737   virtual bool MainThreadRun() override {
1738     AssertIsOnMainThread();
1739 
1740     // Initialize the WorkerLoadInfo principal to our triggering principal
1741     // before doing anything else.  Normally we do this in the WorkerPrivate
1742     // Constructor, but we can't do so off the main thread when creating
1743     // a nested worker.  So do it here instead.
1744     mLoadInfo.mLoadingPrincipal = mWorkerPrivate->GetPrincipal();
1745     MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mLoadingPrincipal);
1746 
1747     mLoadInfo.mPrincipal = mLoadInfo.mLoadingPrincipal;
1748 
1749     // Figure out our base URI.
1750     nsCOMPtr<nsIURI> baseURI = mWorkerPrivate->GetBaseURI();
1751     MOZ_ASSERT(baseURI);
1752 
1753     // May be null.
1754     nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
1755 
1756     mLoadInfo.mLoadGroup = mWorkerPrivate->GetLoadGroup();
1757 
1758     Maybe<ClientInfo> clientInfo;
1759     clientInfo.emplace(mClientInfo);
1760 
1761     nsCOMPtr<nsIChannel> channel;
1762     mResult = workerinternals::ChannelFromScriptURLMainThread(
1763         mLoadInfo.mLoadingPrincipal, baseURI, parentDoc, mLoadInfo.mLoadGroup,
1764         mScriptURL, clientInfo,
1765         // Nested workers are always dedicated.
1766         nsIContentPolicy::TYPE_INTERNAL_WORKER,
1767         // Nested workers use default uri encoding.
1768         true, getter_AddRefs(channel));
1769     NS_ENSURE_SUCCESS(mResult, true);
1770 
1771     mResult = mLoadInfo.SetPrincipalFromChannel(channel);
1772     NS_ENSURE_SUCCESS(mResult, true);
1773 
1774     mLoadInfo.mChannel = channel.forget();
1775     return true;
1776   }
1777 
GetResult() const1778   nsresult GetResult() const { return mResult; }
1779 
1780  private:
~ChannelGetterRunnable()1781   virtual ~ChannelGetterRunnable() {}
1782 };
1783 
ScriptExecutorRunnable(ScriptLoaderRunnable & aScriptLoader,nsIEventTarget * aSyncLoopTarget,bool aIsWorkerScript,uint32_t aFirstIndex,uint32_t aLastIndex)1784 ScriptExecutorRunnable::ScriptExecutorRunnable(
1785     ScriptLoaderRunnable& aScriptLoader, nsIEventTarget* aSyncLoopTarget,
1786     bool aIsWorkerScript, uint32_t aFirstIndex, uint32_t aLastIndex)
1787     : MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate,
1788                                    aSyncLoopTarget),
1789       mScriptLoader(aScriptLoader),
1790       mIsWorkerScript(aIsWorkerScript),
1791       mFirstIndex(aFirstIndex),
1792       mLastIndex(aLastIndex) {
1793   MOZ_ASSERT(aFirstIndex <= aLastIndex);
1794   MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
1795 }
1796 
IsDebuggerRunnable() const1797 bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
1798   // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
1799   // In the latter case, the runnable needs to be dispatched to the debugger
1800   // queue.
1801   return mScriptLoader.mWorkerScriptType == DebuggerScript;
1802 }
1803 
PreRun(WorkerPrivate * aWorkerPrivate)1804 bool ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate) {
1805   aWorkerPrivate->AssertIsOnWorkerThread();
1806 
1807   if (!mIsWorkerScript) {
1808     return true;
1809   }
1810 
1811   if (!aWorkerPrivate->GetJSContext()) {
1812     return false;
1813   }
1814 
1815   MOZ_ASSERT(mFirstIndex == 0);
1816   MOZ_ASSERT(!mScriptLoader.mRv.Failed());
1817 
1818   AutoJSAPI jsapi;
1819   jsapi.Init();
1820 
1821   WorkerGlobalScope* globalScope =
1822       aWorkerPrivate->GetOrCreateGlobalScope(jsapi.cx());
1823   if (NS_WARN_IF(!globalScope)) {
1824     NS_WARNING("Failed to make global!");
1825     // There's no way to report the exception on jsapi right now, because there
1826     // is no way to even enter a compartment on this thread anymore.  Just clear
1827     // the exception.  We'll report some sort of error to our caller in
1828     // ShutdownScriptLoader, but it will get squelched for the same reason we're
1829     // squelching here: all the error reporting machinery relies on being able
1830     // to enter a compartment to report the error.
1831     jsapi.ClearException();
1832     return false;
1833   }
1834 
1835   return true;
1836 }
1837 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1838 bool ScriptExecutorRunnable::WorkerRun(JSContext* aCx,
1839                                        WorkerPrivate* aWorkerPrivate) {
1840   aWorkerPrivate->AssertIsOnWorkerThread();
1841 
1842   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
1843 
1844   // Don't run if something else has already failed.
1845   for (uint32_t index = 0; index < mFirstIndex; index++) {
1846     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
1847 
1848     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
1849     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
1850 
1851     if (!loadInfo.mExecutionResult) {
1852       return true;
1853     }
1854   }
1855 
1856   // If nothing else has failed, our ErrorResult better not be a failure either.
1857   MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
1858 
1859   // Slightly icky action at a distance, but there's no better place to stash
1860   // this value, really.
1861   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
1862   MOZ_ASSERT(global);
1863 
1864   for (uint32_t index = mFirstIndex; index <= mLastIndex; index++) {
1865     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
1866 
1867     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
1868     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
1869     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
1870 
1871     MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
1872     mScriptLoader.mRv.MightThrowJSException();
1873     if (NS_FAILED(loadInfo.mLoadResult)) {
1874       workerinternals::ReportLoadError(mScriptLoader.mRv, loadInfo.mLoadResult,
1875                                        loadInfo.mURL);
1876       // Top level scripts only!
1877       if (mIsWorkerScript) {
1878         aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
1879       }
1880       return true;
1881     }
1882 
1883     // If this is a top level script that succeeded, then mark the
1884     // Client execution ready and possible controlled by a service worker.
1885     if (mIsWorkerScript) {
1886       if (mScriptLoader.mController.isSome()) {
1887         aWorkerPrivate->Control(mScriptLoader.mController.ref());
1888       }
1889       aWorkerPrivate->ExecutionReady();
1890     }
1891 
1892     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
1893 
1894     JS::CompileOptions options(aCx);
1895     options.setFileAndLine(filename.get(), 1).setNoScriptRval(true);
1896 
1897     MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
1898     options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
1899 
1900     JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
1901                                   loadInfo.mScriptTextLength,
1902                                   JS::SourceBufferHolder::GiveOwnership);
1903     loadInfo.mScriptTextBuf = nullptr;
1904     loadInfo.mScriptTextLength = 0;
1905 
1906     // Our ErrorResult still shouldn't be a failure.
1907     MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
1908     JS::Rooted<JS::Value> unused(aCx);
1909     if (!JS::Evaluate(aCx, options, srcBuf, &unused)) {
1910       mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
1911       return true;
1912     }
1913 
1914     loadInfo.mExecutionResult = true;
1915   }
1916 
1917   return true;
1918 }
1919 
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)1920 void ScriptExecutorRunnable::PostRun(JSContext* aCx,
1921                                      WorkerPrivate* aWorkerPrivate,
1922                                      bool aRunResult) {
1923   aWorkerPrivate->AssertIsOnWorkerThread();
1924   MOZ_ASSERT(!JS_IsExceptionPending(aCx), "Who left an exception on there?");
1925 
1926   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
1927 
1928   if (mLastIndex == loadInfos.Length() - 1) {
1929     // All done. If anything failed then return false.
1930     bool result = true;
1931     bool mutedError = false;
1932     for (uint32_t index = 0; index < loadInfos.Length(); index++) {
1933       if (!loadInfos[index].mExecutionResult) {
1934         mutedError = loadInfos[index].mMutedErrorFlag.valueOr(true);
1935         result = false;
1936         break;
1937       }
1938     }
1939 
1940     // The only way we can get here with "result" false but without
1941     // mScriptLoader.mRv being a failure is if we're loading the main worker
1942     // script and GetOrCreateGlobalScope() fails.  In that case we would have
1943     // returned false from WorkerRun, so assert that.
1944     MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(), !aRunResult);
1945     ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
1946   }
1947 }
1948 
Cancel()1949 nsresult ScriptExecutorRunnable::Cancel() {
1950   if (mLastIndex == mScriptLoader.mLoadInfos.Length() - 1) {
1951     ShutdownScriptLoader(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false,
1952                          false);
1953   }
1954   return MainThreadWorkerSyncRunnable::Cancel();
1955 }
1956 
ShutdownScriptLoader(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aResult,bool aMutedError)1957 void ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
1958                                                   WorkerPrivate* aWorkerPrivate,
1959                                                   bool aResult,
1960                                                   bool aMutedError) {
1961   aWorkerPrivate->AssertIsOnWorkerThread();
1962 
1963   MOZ_ASSERT(mLastIndex == mScriptLoader.mLoadInfos.Length() - 1);
1964 
1965   if (mIsWorkerScript && aWorkerPrivate->IsServiceWorker()) {
1966     aWorkerPrivate->SetLoadingWorkerScript(false);
1967   }
1968 
1969   if (!aResult) {
1970     // At this point there are two possibilities:
1971     //
1972     // 1) mScriptLoader.mRv.Failed().  In that case we just want to leave it
1973     //    as-is, except if it has a JS exception and we need to mute JS
1974     //    exceptions.  In that case, we log the exception without firing any
1975     //    events and then replace it on the ErrorResult with a NetworkError,
1976     //    per spec.
1977     //
1978     // 2) mScriptLoader.mRv succeeded.  As far as I can tell, this can only
1979     //    happen when loading the main worker script and
1980     //    GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
1981     //    got called.  Does it matter what we throw in this case?  I'm not
1982     //    sure...
1983     if (mScriptLoader.mRv.Failed()) {
1984       if (aMutedError && mScriptLoader.mRv.IsJSException()) {
1985         LogExceptionToConsole(aCx, aWorkerPrivate);
1986         mScriptLoader.mRv.ThrowWithCustomCleanup(NS_ERROR_DOM_NETWORK_ERR);
1987       }
1988     } else {
1989       mScriptLoader.mRv.ThrowWithCustomCleanup(NS_ERROR_DOM_INVALID_STATE_ERR);
1990     }
1991   }
1992 
1993   aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, aResult);
1994 }
1995 
LogExceptionToConsole(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1996 void ScriptExecutorRunnable::LogExceptionToConsole(
1997     JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1998   aWorkerPrivate->AssertIsOnWorkerThread();
1999 
2000   MOZ_ASSERT(mScriptLoader.mRv.IsJSException());
2001 
2002   JS::Rooted<JS::Value> exn(aCx);
2003   if (!ToJSValue(aCx, mScriptLoader.mRv, &exn)) {
2004     return;
2005   }
2006 
2007   // Now the exception state should all be in exn.
2008   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
2009   MOZ_ASSERT(!mScriptLoader.mRv.Failed());
2010 
2011   js::ErrorReport report(aCx);
2012   if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
2013     JS_ClearPendingException(aCx);
2014     return;
2015   }
2016 
2017   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
2018   xpcReport->Init(report.report(), report.toStringResult().c_str(),
2019                   aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
2020 
2021   RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
2022   NS_DispatchToMainThread(r);
2023 }
2024 
LoadAllScripts(WorkerPrivate * aWorkerPrivate,nsTArray<ScriptLoadInfo> & aLoadInfos,bool aIsMainScript,WorkerScriptType aWorkerScriptType,ErrorResult & aRv)2025 void LoadAllScripts(WorkerPrivate* aWorkerPrivate,
2026                     nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsMainScript,
2027                     WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
2028   aWorkerPrivate->AssertIsOnWorkerThread();
2029   NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
2030 
2031   AutoSyncLoopHolder syncLoop(aWorkerPrivate, Terminating);
2032   nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget();
2033   if (!syncLoopTarget) {
2034     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2035     return;
2036   }
2037 
2038   Maybe<ClientInfo> clientInfo;
2039   Maybe<ServiceWorkerDescriptor> controller;
2040   if (!aIsMainScript) {
2041     clientInfo.emplace(aWorkerPrivate->GetClientInfo());
2042     controller = aWorkerPrivate->GetController();
2043   }
2044 
2045   RefPtr<ScriptLoaderRunnable> loader = new ScriptLoaderRunnable(
2046       aWorkerPrivate, syncLoopTarget, aLoadInfos, clientInfo, controller,
2047       aIsMainScript, aWorkerScriptType, aRv);
2048 
2049   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
2050 
2051   ScriptLoaderHolder workerHolder(loader);
2052 
2053   if (NS_WARN_IF(!workerHolder.HoldWorker(aWorkerPrivate, Terminating))) {
2054     aRv.Throw(NS_ERROR_FAILURE);
2055     return;
2056   }
2057 
2058   if (NS_FAILED(NS_DispatchToMainThread(loader))) {
2059     NS_ERROR("Failed to dispatch!");
2060     aRv.Throw(NS_ERROR_FAILURE);
2061     return;
2062   }
2063 
2064   syncLoop.Run();
2065 }
2066 
2067 } /* anonymous namespace */
2068 
2069 namespace workerinternals {
2070 
ChannelFromScriptURLMainThread(nsIPrincipal * aPrincipal,nsIURI * aBaseURI,nsIDocument * aParentDoc,nsILoadGroup * aLoadGroup,const nsAString & aScriptURL,const Maybe<ClientInfo> & aClientInfo,nsContentPolicyType aMainScriptContentPolicyType,bool aDefaultURIEncoding,nsIChannel ** aChannel)2071 nsresult ChannelFromScriptURLMainThread(
2072     nsIPrincipal* aPrincipal, nsIURI* aBaseURI, nsIDocument* aParentDoc,
2073     nsILoadGroup* aLoadGroup, const nsAString& aScriptURL,
2074     const Maybe<ClientInfo>& aClientInfo,
2075     nsContentPolicyType aMainScriptContentPolicyType, bool aDefaultURIEncoding,
2076     nsIChannel** aChannel) {
2077   AssertIsOnMainThread();
2078 
2079   nsCOMPtr<nsIIOService> ios(do_GetIOService());
2080 
2081   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
2082   NS_ASSERTION(secMan, "This should never be null!");
2083 
2084   return ChannelFromScriptURL(
2085       aPrincipal, aBaseURI, aParentDoc, nullptr, aLoadGroup, ios, secMan,
2086       aScriptURL, aClientInfo, Maybe<ServiceWorkerDescriptor>(), true,
2087       WorkerScript, aMainScriptContentPolicyType, nsIRequest::LOAD_NORMAL,
2088       aDefaultURIEncoding, aChannel);
2089 }
2090 
ChannelFromScriptURLWorkerThread(JSContext * aCx,WorkerPrivate * aParent,const nsAString & aScriptURL,WorkerLoadInfo & aLoadInfo)2091 nsresult ChannelFromScriptURLWorkerThread(JSContext* aCx,
2092                                           WorkerPrivate* aParent,
2093                                           const nsAString& aScriptURL,
2094                                           WorkerLoadInfo& aLoadInfo) {
2095   aParent->AssertIsOnWorkerThread();
2096 
2097   RefPtr<ChannelGetterRunnable> getter =
2098       new ChannelGetterRunnable(aParent, aScriptURL, aLoadInfo);
2099 
2100   ErrorResult rv;
2101   getter->Dispatch(Terminating, rv);
2102   if (rv.Failed()) {
2103     NS_ERROR("Failed to dispatch!");
2104     return rv.StealNSResult();
2105   }
2106 
2107   return getter->GetResult();
2108 }
2109 
ReportLoadError(ErrorResult & aRv,nsresult aLoadResult,const nsAString & aScriptURL)2110 void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
2111                      const nsAString& aScriptURL) {
2112   MOZ_ASSERT(!aRv.Failed());
2113 
2114   switch (aLoadResult) {
2115     case NS_ERROR_FILE_NOT_FOUND:
2116     case NS_ERROR_NOT_AVAILABLE:
2117       aLoadResult = NS_ERROR_DOM_NETWORK_ERR;
2118       break;
2119 
2120     case NS_ERROR_MALFORMED_URI:
2121       aLoadResult = NS_ERROR_DOM_SYNTAX_ERR;
2122       break;
2123 
2124     case NS_BINDING_ABORTED:
2125       // Note: we used to pretend like we didn't set an exception for
2126       // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway.  The
2127       // other callsite, in WorkerPrivate::Constructor, never passed in
2128       // NS_BINDING_ABORTED.  So just throw it directly here.  Consumers will
2129       // deal as needed.  But note that we do NOT want to ThrowDOMException()
2130       // for this case, because that will make it impossible for consumers to
2131       // realize that our error was NS_BINDING_ABORTED.
2132       aRv.Throw(aLoadResult);
2133       return;
2134 
2135     case NS_ERROR_DOM_SECURITY_ERR:
2136     case NS_ERROR_DOM_SYNTAX_ERR:
2137       break;
2138 
2139     case NS_ERROR_DOM_BAD_URI:
2140       // This is actually a security error.
2141       aLoadResult = NS_ERROR_DOM_SECURITY_ERR;
2142       break;
2143 
2144     default:
2145       // For lack of anything better, go ahead and throw a NetworkError here.
2146       // We don't want to throw a JS exception, because for toplevel script
2147       // loads that would get squelched.
2148       aRv.ThrowDOMException(
2149           NS_ERROR_DOM_NETWORK_ERR,
2150           nsPrintfCString(
2151               "Failed to load worker script at %s (nsresult = 0x%" PRIx32 ")",
2152               NS_ConvertUTF16toUTF8(aScriptURL).get(),
2153               static_cast<uint32_t>(aLoadResult)));
2154       return;
2155   }
2156 
2157   aRv.ThrowDOMException(
2158       aLoadResult, NS_LITERAL_CSTRING("Failed to load worker script at \"") +
2159                        NS_ConvertUTF16toUTF8(aScriptURL) +
2160                        NS_LITERAL_CSTRING("\""));
2161 }
2162 
LoadMainScript(WorkerPrivate * aWorkerPrivate,const nsAString & aScriptURL,WorkerScriptType aWorkerScriptType,ErrorResult & aRv)2163 void LoadMainScript(WorkerPrivate* aWorkerPrivate, const nsAString& aScriptURL,
2164                     WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
2165   nsTArray<ScriptLoadInfo> loadInfos;
2166 
2167   ScriptLoadInfo* info = loadInfos.AppendElement();
2168   info->mURL = aScriptURL;
2169   info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
2170 
2171   // We are loading the main script, so the worker's Client must be
2172   // reserved.
2173   info->mReservedClientInfo.emplace(aWorkerPrivate->GetClientInfo());
2174 
2175   LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
2176 }
2177 
Load(WorkerPrivate * aWorkerPrivate,const nsTArray<nsString> & aScriptURLs,WorkerScriptType aWorkerScriptType,ErrorResult & aRv)2178 void Load(WorkerPrivate* aWorkerPrivate, const nsTArray<nsString>& aScriptURLs,
2179           WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
2180   const uint32_t urlCount = aScriptURLs.Length();
2181 
2182   if (!urlCount) {
2183     return;
2184   }
2185 
2186   if (urlCount > MAX_CONCURRENT_SCRIPTS) {
2187     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2188     return;
2189   }
2190 
2191   nsTArray<ScriptLoadInfo> loadInfos;
2192   loadInfos.SetLength(urlCount);
2193 
2194   for (uint32_t index = 0; index < urlCount; index++) {
2195     loadInfos[index].mURL = aScriptURLs[index];
2196     loadInfos[index].mLoadFlags = aWorkerPrivate->GetLoadFlags();
2197   }
2198 
2199   LoadAllScripts(aWorkerPrivate, loadInfos, false, aWorkerScriptType, aRv);
2200 }
2201 
2202 }  // namespace workerinternals
2203 
2204 }  // namespace dom
2205 }  // namespace mozilla
2206