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