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