LCOV - code coverage report
Current view: top level - dom/workers - ScriptLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 181 960 18.9 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13-14-ga5dd952