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