tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ScriptLoader.cpp (69871B)


      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 <algorithm>
     10 
     11 #include "WorkerRunnable.h"
     12 #include "WorkerScope.h"
     13 #include "js/CompilationAndEvaluation.h"
     14 #include "js/Exception.h"
     15 #include "js/SourceText.h"
     16 #include "js/TypeDecls.h"
     17 #include "js/loader/ModuleLoadRequest.h"
     18 #include "jsapi.h"
     19 #include "jsfriendapi.h"
     20 #include "mozilla/AntiTrackingUtils.h"
     21 #include "mozilla/ArrayAlgorithm.h"
     22 #include "mozilla/Assertions.h"
     23 #include "mozilla/Encoding.h"
     24 #include "mozilla/LoadContext.h"
     25 #include "mozilla/Maybe.h"
     26 #include "mozilla/StaticPrefs_browser.h"
     27 #include "mozilla/UniquePtr.h"
     28 #include "mozilla/dom/ClientChannelHelper.h"
     29 #include "mozilla/dom/ClientInfo.h"
     30 #include "mozilla/dom/Exceptions.h"
     31 #include "mozilla/dom/PerformanceStorage.h"
     32 #include "mozilla/dom/ReferrerInfo.h"
     33 #include "mozilla/dom/RequestBinding.h"
     34 #include "mozilla/dom/Response.h"
     35 #include "mozilla/dom/ScriptSettings.h"
     36 #include "mozilla/dom/SerializedStackHolder.h"
     37 #include "mozilla/dom/nsCSPService.h"
     38 #include "mozilla/dom/nsCSPUtils.h"
     39 #include "mozilla/dom/workerinternals/CacheLoadHandler.h"
     40 #include "mozilla/dom/workerinternals/NetworkLoadHandler.h"
     41 #include "mozilla/dom/workerinternals/ScriptResponseHeaderProcessor.h"
     42 #include "mozilla/ipc/BackgroundUtils.h"
     43 #include "nsComponentManagerUtils.h"
     44 #include "nsContentPolicyUtils.h"
     45 #include "nsContentSecurityManager.h"
     46 #include "nsContentUtils.h"
     47 #include "nsDocShellCID.h"
     48 #include "nsError.h"
     49 #include "nsIChannel.h"
     50 #include "nsIContentPolicy.h"
     51 #include "nsIContentSecurityPolicy.h"
     52 #include "nsICookieJarSettings.h"
     53 #include "nsIDocShell.h"
     54 #include "nsIHttpChannel.h"
     55 #include "nsIHttpChannelInternal.h"
     56 #include "nsIIOService.h"
     57 #include "nsIOService.h"
     58 #include "nsIOutputStream.h"
     59 #include "nsIPipe.h"
     60 #include "nsIPrincipal.h"
     61 #include "nsIProtocolHandler.h"
     62 #include "nsIScriptError.h"
     63 #include "nsIScriptSecurityManager.h"
     64 #include "nsIStreamListenerTee.h"
     65 #include "nsIThreadRetargetableRequest.h"
     66 #include "nsIURI.h"
     67 #include "nsIXPConnect.h"
     68 #include "nsJSEnvironment.h"
     69 #include "nsNetUtil.h"
     70 #include "nsPrintfCString.h"
     71 #include "nsString.h"
     72 #include "nsTArray.h"
     73 #include "nsThreadUtils.h"
     74 #include "nsXPCOM.h"
     75 #include "xpcpublic.h"
     76 
     77 #define MAX_CONCURRENT_SCRIPTS 1000
     78 
     79 using JS::loader::ParserMetadata;
     80 using JS::loader::ScriptKind;
     81 using JS::loader::ScriptLoadRequest;
     82 using mozilla::ipc::PrincipalInfo;
     83 
     84 namespace mozilla::dom::workerinternals {
     85 namespace {
     86 
     87 nsresult ConstructURI(const nsAString& aScriptURL, nsIURI* baseURI,
     88                      const mozilla::Encoding* aDocumentEncoding,
     89                      nsIURI** aResult) {
     90  nsresult rv;
     91  // Only top level workers' main script use the document charset for the
     92  // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
     93  if (aDocumentEncoding) {
     94    nsAutoCString charset;
     95    aDocumentEncoding->Name(charset);
     96    rv = NS_NewURI(aResult, aScriptURL, charset.get(), baseURI);
     97  } else {
     98    rv = NS_NewURI(aResult, aScriptURL, nullptr, baseURI);
     99  }
    100 
    101  if (NS_FAILED(rv)) {
    102    return NS_ERROR_DOM_SYNTAX_ERR;
    103  }
    104  return NS_OK;
    105 }
    106 
    107 nsresult ChannelFromScriptURL(
    108    nsIPrincipal* principal, Document* parentDoc, WorkerPrivate* aWorkerPrivate,
    109    nsILoadGroup* loadGroup, nsIIOService* ios,
    110    nsIScriptSecurityManager* secMan, nsIURI* aScriptURL,
    111    const Maybe<ClientInfo>& aClientInfo,
    112    const Maybe<ServiceWorkerDescriptor>& aController, bool aIsMainScript,
    113    WorkerScriptType aWorkerScriptType, nsContentPolicyType aContentPolicyType,
    114    nsLoadFlags aLoadFlags, uint32_t aSecFlags,
    115    nsICookieJarSettings* aCookieJarSettings, nsIReferrerInfo* aReferrerInfo,
    116    nsIChannel** aChannel) {
    117  AssertIsOnMainThread();
    118 
    119  nsresult rv;
    120  nsCOMPtr<nsIURI> uri = aScriptURL;
    121 
    122  // Only use the document when its principal matches the principal of the
    123  // current request. This means scripts fetched using the Workers' own
    124  // principal won't inherit properties of the document, in particular the CSP.
    125  if (parentDoc && parentDoc->NodePrincipal() != principal) {
    126    parentDoc = nullptr;
    127  }
    128 
    129  // The main service worker script should never be loaded over the network
    130  // in this path.  It should always be offlined by ServiceWorkerScriptCache.
    131  // We assert here since this error should also be caught by the runtime
    132  // check in CacheLoadHandler.
    133  //
    134  // Note, if we ever allow service worker scripts to be loaded from network
    135  // here we need to configure the channel properly.  For example, it must
    136  // not allow redirects.
    137  MOZ_DIAGNOSTIC_ASSERT(aContentPolicyType !=
    138                        nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER);
    139 
    140  nsCOMPtr<nsIChannel> channel;
    141  if (parentDoc) {
    142    // This is the path for top level dedicated worker scripts with a document
    143    rv = NS_NewChannel(getter_AddRefs(channel), uri, parentDoc, aSecFlags,
    144                       aContentPolicyType,
    145                       nullptr,  // aPerformanceStorage
    146                       loadGroup,
    147                       nullptr,  // aCallbacks
    148                       aLoadFlags, ios);
    149    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
    150  } else {
    151    // This branch is used in the following cases:
    152    //    * Shared and ServiceWorkers (who do not have a doc)
    153    //    * Static Module Imports
    154    //    * ImportScripts
    155 
    156    // We must have a loadGroup with a load context for the principal to
    157    // traverse the channel correctly.
    158 
    159    MOZ_ASSERT(loadGroup);
    160    MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
    161 
    162    RefPtr<PerformanceStorage> performanceStorage;
    163    nsCOMPtr<nsICSPEventListener> cspEventListener;
    164    if (aWorkerPrivate && !aIsMainScript) {
    165      performanceStorage = aWorkerPrivate->GetPerformanceStorage();
    166      cspEventListener = aWorkerPrivate->CSPEventListener();
    167    }
    168 
    169    if (aClientInfo.isSome()) {
    170      // If we have an existing clientInfo (true for all modules and
    171      // importScripts), we will use this branch
    172      rv = NS_NewChannel(getter_AddRefs(channel), uri, principal,
    173                         aClientInfo.ref(), aController, aSecFlags,
    174                         aContentPolicyType, aCookieJarSettings,
    175                         performanceStorage, loadGroup, nullptr,  // aCallbacks
    176                         aLoadFlags, ios);
    177    } else {
    178      rv = NS_NewChannel(getter_AddRefs(channel), uri, principal, aSecFlags,
    179                         aContentPolicyType, aCookieJarSettings,
    180                         performanceStorage, loadGroup, nullptr,  // aCallbacks
    181                         aLoadFlags, ios);
    182    }
    183 
    184    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
    185 
    186    if (cspEventListener) {
    187      nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    188      rv = loadInfo->SetCspEventListener(cspEventListener);
    189      NS_ENSURE_SUCCESS(rv, rv);
    190    }
    191  }
    192 
    193  if (aReferrerInfo) {
    194    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
    195    if (httpChannel) {
    196      rv = httpChannel->SetReferrerInfo(aReferrerInfo);
    197      if (NS_WARN_IF(NS_FAILED(rv))) {
    198        return rv;
    199      }
    200    }
    201  }
    202 
    203  channel.forget(aChannel);
    204  return rv;
    205 }
    206 
    207 void LoadAllScripts(WorkerPrivate* aWorkerPrivate,
    208                    UniquePtr<SerializedStackHolder> aOriginStack,
    209                    const nsTArray<nsString>& aScriptURLs, bool aIsMainScript,
    210                    WorkerScriptType aWorkerScriptType, ErrorResult& aRv,
    211                    const mozilla::Encoding* aDocumentEncoding = nullptr) {
    212  aWorkerPrivate->AssertIsOnWorkerThread();
    213  NS_ASSERTION(!aScriptURLs.IsEmpty(), "Bad arguments!");
    214 
    215  AutoSyncLoopHolder syncLoop(aWorkerPrivate, Canceling);
    216  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    217      syncLoop.GetSerialEventTarget();
    218  if (!syncLoopTarget) {
    219    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    220    return;
    221  }
    222 
    223  RefPtr<loader::WorkerScriptLoader> loader =
    224      loader::WorkerScriptLoader::Create(
    225          aWorkerPrivate, std::move(aOriginStack), syncLoopTarget,
    226          aWorkerScriptType, aRv);
    227 
    228  if (NS_WARN_IF(aRv.Failed())) {
    229    return;
    230  }
    231 
    232  bool ok = loader->CreateScriptRequests(aScriptURLs, aDocumentEncoding,
    233                                         aIsMainScript);
    234 
    235  if (!ok) {
    236    return;
    237  }
    238  // Bug 1817259 - For now, we force loading the debugger script as Classic,
    239  // even if the debugged worker is a Module.
    240  if (aWorkerPrivate->WorkerType() == WorkerType::Module &&
    241      aWorkerScriptType != DebuggerScript) {
    242    MOZ_ASSERT(aIsMainScript);
    243    // Module Load
    244    RefPtr<JS::loader::ScriptLoadRequest> mainScript = loader->GetMainScript();
    245    if (mainScript && mainScript->IsModuleRequest()) {
    246      if (NS_FAILED(mainScript->AsModuleRequest()->StartModuleLoad())) {
    247        return;
    248      }
    249      syncLoop.Run();
    250      return;
    251    }
    252  }
    253 
    254  if (loader->DispatchLoadScripts()) {
    255    syncLoop.Run();
    256  }
    257 }
    258 
    259 class ChannelGetterRunnable final : public WorkerMainThreadRunnable {
    260  const nsAString& mScriptURL;
    261  const WorkerType& mWorkerType;
    262  const RequestCredentials& mCredentials;
    263  const ClientInfo mClientInfo;
    264  WorkerLoadInfo& mLoadInfo;
    265  nsresult mResult;
    266 
    267 public:
    268  ChannelGetterRunnable(WorkerPrivate* aParentWorker,
    269                        const nsAString& aScriptURL,
    270                        const WorkerType& aWorkerType,
    271                        const RequestCredentials& aCredentials,
    272                        WorkerLoadInfo& aLoadInfo)
    273      : WorkerMainThreadRunnable(aParentWorker,
    274                                 "ScriptLoader :: ChannelGetter"_ns),
    275        mScriptURL(aScriptURL)
    276        // ClientInfo should always be present since this should not be called
    277        // if parent's status is greater than Running.
    278        ,
    279        mWorkerType(aWorkerType),
    280        mCredentials(aCredentials),
    281        mClientInfo(aParentWorker->GlobalScope()->GetClientInfo().ref()),
    282        mLoadInfo(aLoadInfo),
    283        mResult(NS_ERROR_FAILURE) {
    284    MOZ_ASSERT(aParentWorker);
    285    aParentWorker->AssertIsOnWorkerThread();
    286  }
    287 
    288  virtual bool MainThreadRun() override {
    289    AssertIsOnMainThread();
    290    MOZ_ASSERT(mWorkerRef);
    291 
    292    WorkerPrivate* workerPrivate = mWorkerRef->Private();
    293 
    294    // Initialize the WorkerLoadInfo principal to our triggering principal
    295    // before doing anything else.  Normally we do this in the WorkerPrivate
    296    // Constructor, but we can't do so off the main thread when creating
    297    // a nested worker.  So do it here instead.
    298    mLoadInfo.mLoadingPrincipal = workerPrivate->GetPrincipal();
    299    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mLoadingPrincipal);
    300 
    301    mLoadInfo.mPrincipal = mLoadInfo.mLoadingPrincipal;
    302 
    303    // Figure out our base URI.
    304    nsCOMPtr<nsIURI> baseURI = workerPrivate->GetBaseURI();
    305    MOZ_ASSERT(baseURI);
    306 
    307    // May be null.
    308    nsCOMPtr<Document> parentDoc = workerPrivate->GetDocument();
    309 
    310    mLoadInfo.mLoadGroup = workerPrivate->GetLoadGroup();
    311    mLoadInfo.mCookieJarSettings = workerPrivate->CookieJarSettings();
    312 
    313    // Nested workers use default uri encoding.
    314    nsCOMPtr<nsIURI> url;
    315    mResult = ConstructURI(mScriptURL, baseURI, nullptr, getter_AddRefs(url));
    316    NS_ENSURE_SUCCESS(mResult, true);
    317 
    318    Maybe<ClientInfo> clientInfo;
    319    clientInfo.emplace(mClientInfo);
    320 
    321    nsCOMPtr<nsIChannel> channel;
    322    nsCOMPtr<nsIReferrerInfo> referrerInfo =
    323        ReferrerInfo::CreateForFetch(mLoadInfo.mLoadingPrincipal, nullptr);
    324    mLoadInfo.mReferrerInfo =
    325        static_cast<ReferrerInfo*>(referrerInfo.get())
    326            ->CloneWithNewPolicy(workerPrivate->GetReferrerPolicy());
    327 
    328    mResult = workerinternals::ChannelFromScriptURLMainThread(
    329        mLoadInfo.mLoadingPrincipal, parentDoc, mLoadInfo.mLoadGroup, url,
    330        mWorkerType, mCredentials, clientInfo,
    331        // Nested workers are always dedicated.
    332        nsIContentPolicy::TYPE_INTERNAL_WORKER, mLoadInfo.mCookieJarSettings,
    333        mLoadInfo.mReferrerInfo, getter_AddRefs(channel));
    334    NS_ENSURE_SUCCESS(mResult, true);
    335 
    336    mResult = mLoadInfo.SetPrincipalsAndCSPFromChannel(channel);
    337    NS_ENSURE_SUCCESS(mResult, true);
    338 
    339    mLoadInfo.mChannel = std::move(channel);
    340    return true;
    341  }
    342 
    343  nsresult GetResult() const { return mResult; }
    344 
    345 private:
    346  virtual ~ChannelGetterRunnable() = default;
    347 };
    348 
    349 nsresult GetCommonSecFlags(bool aIsMainScript, nsIURI* uri,
    350                           nsIPrincipal* principal,
    351                           WorkerScriptType aWorkerScriptType,
    352                           uint32_t& secFlags) {
    353  bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
    354      principal, uri, true /* aInheritForAboutBlank */,
    355      false /* aForceInherit */);
    356 
    357  bool isData = uri->SchemeIs("data");
    358  if (inheritAttrs && !isData) {
    359    secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
    360  }
    361 
    362  if (aWorkerScriptType == DebuggerScript) {
    363    // A DebuggerScript needs to be a local resource like chrome: or resource:
    364    bool isUIResource = false;
    365    nsresult rv = NS_URIChainHasFlags(
    366        uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUIResource);
    367    if (NS_WARN_IF(NS_FAILED(rv))) {
    368      return rv;
    369    }
    370 
    371    if (!isUIResource) {
    372      return NS_ERROR_DOM_SECURITY_ERR;
    373    }
    374 
    375    secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
    376  }
    377 
    378  // Note: this is for backwards compatibility and goes against spec.
    379  // We should find a better solution.
    380  if (aIsMainScript && isData) {
    381    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
    382  }
    383 
    384  return NS_OK;
    385 }
    386 
    387 nsresult GetModuleSecFlags(bool aIsTopLevel, nsIPrincipal* principal,
    388                           WorkerScriptType aWorkerScriptType, nsIURI* aURI,
    389                           RequestCredentials aCredentials,
    390                           uint32_t& secFlags) {
    391  // Implements "To fetch a single module script,"
    392  // Step 9. If destination is "worker", "sharedworker", or "serviceworker",
    393  //         and the top-level module fetch flag is set, then set request's
    394  //         mode to "same-origin".
    395 
    396  // Step 8. Let request be a new request whose [...] mode is "cors" [...]
    397  secFlags = aIsTopLevel ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
    398                         : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
    399 
    400  // This implements the same Cookie settings as  nsContentSecurityManager's
    401  // ComputeSecurityFlags. The main difference is the line above, Step 9,
    402  // setting to same origin.
    403 
    404  if (aCredentials == RequestCredentials::Include) {
    405    secFlags |= nsILoadInfo::nsILoadInfo::SEC_COOKIES_INCLUDE;
    406  } else if (aCredentials == RequestCredentials::Same_origin) {
    407    secFlags |= nsILoadInfo::nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
    408  } else if (aCredentials == RequestCredentials::Omit) {
    409    secFlags |= nsILoadInfo::nsILoadInfo::SEC_COOKIES_OMIT;
    410  }
    411 
    412  return GetCommonSecFlags(aIsTopLevel, aURI, principal, aWorkerScriptType,
    413                           secFlags);
    414 }
    415 
    416 nsresult GetClassicSecFlags(bool aIsMainScript, nsIURI* uri,
    417                            nsIPrincipal* principal,
    418                            WorkerScriptType aWorkerScriptType,
    419                            uint32_t& secFlags) {
    420  secFlags = aIsMainScript
    421                 ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
    422                 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
    423 
    424  return GetCommonSecFlags(aIsMainScript, uri, principal, aWorkerScriptType,
    425                           secFlags);
    426 }
    427 
    428 }  //  anonymous namespace
    429 
    430 namespace loader {
    431 
    432 class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable {
    433  RefPtr<WorkerScriptLoader> mScriptLoader;
    434  const Span<RefPtr<ThreadSafeRequestHandle>> mLoadedRequests;
    435 
    436 public:
    437  ScriptExecutorRunnable(WorkerScriptLoader* aScriptLoader,
    438                         WorkerPrivate* aWorkerPrivate,
    439                         nsISerialEventTarget* aSyncLoopTarget,
    440                         Span<RefPtr<ThreadSafeRequestHandle>> aLoadedRequests);
    441 
    442 private:
    443  ~ScriptExecutorRunnable() = default;
    444 
    445  virtual bool IsDebuggerRunnable() const override;
    446 
    447  virtual bool PreRun(WorkerPrivate* aWorkerPrivate) override;
    448 
    449  bool ProcessModuleScript(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
    450 
    451  bool ProcessClassicScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
    452 
    453  virtual bool WorkerRun(JSContext* aCx,
    454                         WorkerPrivate* aWorkerPrivate) override;
    455 
    456  nsresult Cancel() override;
    457 };
    458 
    459 static bool EvaluateSourceBuffer(JSContext* aCx, JS::Handle<JSScript*> aScript,
    460                                 JS::loader::ClassicScript* aClassicScript) {
    461  if (aClassicScript) {
    462    aClassicScript->AssociateWithScript(aScript);
    463  }
    464 
    465  JS::Rooted<JS::Value> unused(aCx);
    466  return JS_ExecuteScript(aCx, aScript, &unused);
    467 }
    468 
    469 WorkerScriptLoader::WorkerScriptLoader(
    470    UniquePtr<SerializedStackHolder> aOriginStack,
    471    nsISerialEventTarget* aSyncLoopTarget, WorkerScriptType aWorkerScriptType,
    472    ErrorResult& aRv)
    473    : mOriginStack(std::move(aOriginStack)),
    474      mSyncLoopTarget(aSyncLoopTarget),
    475      mWorkerScriptType(aWorkerScriptType),
    476      mRv(aRv),
    477      mLoadingModuleRequestCount(0),
    478      mCleanedUp(false),
    479      mCleanUpLock("cleanUpLock") {}
    480 
    481 already_AddRefed<WorkerScriptLoader> WorkerScriptLoader::Create(
    482    WorkerPrivate* aWorkerPrivate,
    483    UniquePtr<SerializedStackHolder> aOriginStack,
    484    nsISerialEventTarget* aSyncLoopTarget, WorkerScriptType aWorkerScriptType,
    485    ErrorResult& aRv) {
    486  aWorkerPrivate->AssertIsOnWorkerThread();
    487 
    488  RefPtr<WorkerScriptLoader> self = new WorkerScriptLoader(
    489      std::move(aOriginStack), aSyncLoopTarget, aWorkerScriptType, aRv);
    490 
    491  RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
    492      aWorkerPrivate, "WorkerScriptLoader::Create", [self]() {
    493        // Requests that are in flight are covered by the worker references
    494        // in DispatchLoadScript(s), so we do not need to do additional
    495        // cleanup, but just in case we are ready/aborted we can try to
    496        // shutdown here, too.
    497        self->TryShutdown();
    498      });
    499 
    500  if (workerRef) {
    501    self->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
    502  } else {
    503    self->mRv.Throw(NS_ERROR_FAILURE);
    504    return nullptr;
    505  }
    506 
    507  nsIGlobalObject* global = self->GetGlobal();
    508  self->mController = global->GetController();
    509 
    510  // Set up the module loader, if it has not been initialzied yet.
    511  self->InitModuleLoader();
    512 
    513  return self.forget();
    514 }
    515 
    516 ScriptLoadRequest* WorkerScriptLoader::GetMainScript() {
    517  mWorkerRef->Private()->AssertIsOnWorkerThread();
    518  ScriptLoadRequest* request = mLoadingRequests.getFirst();
    519  if (request->GetWorkerLoadContext()->IsTopLevel()) {
    520    return request;
    521  }
    522  return nullptr;
    523 }
    524 
    525 void WorkerScriptLoader::InitModuleLoader() {
    526  mWorkerRef->Private()->AssertIsOnWorkerThread();
    527  if (GetGlobal()->GetModuleLoader(nullptr)) {
    528    return;
    529  }
    530  RefPtr<WorkerModuleLoader> moduleLoader =
    531      new WorkerModuleLoader(this, GetGlobal());
    532  if (mWorkerScriptType == WorkerScript) {
    533    mWorkerRef->Private()->GlobalScope()->InitModuleLoader(moduleLoader);
    534    return;
    535  }
    536  mWorkerRef->Private()->DebuggerGlobalScope()->InitModuleLoader(moduleLoader);
    537 }
    538 
    539 bool WorkerScriptLoader::CreateScriptRequests(
    540    const nsTArray<nsString>& aScriptURLs,
    541    const mozilla::Encoding* aDocumentEncoding, bool aIsMainScript) {
    542  mWorkerRef->Private()->AssertIsOnWorkerThread();
    543  // If a worker has been loaded as a module worker, ImportScripts calls are
    544  // disallowed -- then the operation is invalid.
    545  //
    546  // 10.3.1 Importing scripts and libraries.
    547  // Step 1. If worker global scope's type is "module", throw a TypeError
    548  //         exception.
    549  //
    550  // Also, for now, the debugger script is always loaded as Classic,
    551  // even if the debugged worker is a Module. We still want to allow
    552  // it to use importScripts.
    553  if (mWorkerRef->Private()->WorkerType() == WorkerType::Module &&
    554      !aIsMainScript && !IsDebuggerScript()) {
    555    // This should only run for non-main scripts, as only these are
    556    // importScripts
    557    mRv.ThrowTypeError(
    558        "Using `ImportScripts` inside a Module Worker is "
    559        "disallowed.");
    560    return false;
    561  }
    562  for (const nsString& scriptURL : aScriptURLs) {
    563    nsresult rv = NS_OK;
    564    RefPtr<ScriptLoadRequest> request = CreateScriptLoadRequest(
    565        scriptURL, aDocumentEncoding, aIsMainScript, &rv);
    566    if (!request) {
    567      mLoadingRequests.CancelRequestsAndClear();
    568      workerinternals::ReportLoadError(mRv, rv, scriptURL);
    569      return false;
    570    }
    571    mLoadingRequests.AppendElement(request);
    572  }
    573 
    574  return true;
    575 }
    576 
    577 nsTArray<RefPtr<ThreadSafeRequestHandle>> WorkerScriptLoader::GetLoadingList() {
    578  mWorkerRef->Private()->AssertIsOnWorkerThread();
    579  nsTArray<RefPtr<ThreadSafeRequestHandle>> list;
    580  for (ScriptLoadRequest* req = mLoadingRequests.getFirst(); req;
    581       req = req->getNext()) {
    582    RefPtr<ThreadSafeRequestHandle> handle =
    583        new ThreadSafeRequestHandle(req, mSyncLoopTarget.get());
    584    list.AppendElement(handle.forget());
    585  }
    586  return list;
    587 }
    588 
    589 bool WorkerScriptLoader::IsDynamicImport(ScriptLoadRequest* aRequest) {
    590  return aRequest->IsModuleRequest() &&
    591         aRequest->AsModuleRequest()->IsDynamicImport();
    592 }
    593 
    594 nsContentPolicyType WorkerScriptLoader::GetContentPolicyType(
    595    ScriptLoadRequest* aRequest) {
    596  if (aRequest->GetWorkerLoadContext()->IsTopLevel()) {
    597    // Implements https://html.spec.whatwg.org/#worker-processing-model
    598    // Step 13: Let destination be "sharedworker" if is shared is true, and
    599    // "worker" otherwise.
    600    return mWorkerRef->Private()->ContentPolicyType();
    601  }
    602  if (aRequest->IsModuleRequest()) {
    603    if (aRequest->AsModuleRequest()->IsDynamicImport()) {
    604      return aRequest->AsModuleRequest()->mModuleType ==
    605                     JS::ModuleType::JavaScript
    606                 ? nsIContentPolicy::TYPE_INTERNAL_MODULE
    607                 : nsIContentPolicy::TYPE_JSON;
    608    }
    609 
    610    // Implements the destination for Step 14 in
    611    // https://html.spec.whatwg.org/#worker-processing-model
    612    //
    613    // We need a special subresource type in order to correctly implement
    614    // the graph fetch, where the destination is set to "worker" or
    615    // "sharedworker".
    616    return nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE;
    617  }
    618  // For script imported in worker's importScripts().
    619  return nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
    620 }
    621 
    622 already_AddRefed<ScriptLoadRequest> WorkerScriptLoader::CreateScriptLoadRequest(
    623    const nsString& aScriptURL, const mozilla::Encoding* aDocumentEncoding,
    624    bool aIsMainScript, nsresult* aRv) {
    625  mWorkerRef->Private()->AssertIsOnWorkerThread();
    626  WorkerLoadContext::Kind kind =
    627      WorkerLoadContext::GetKind(aIsMainScript, IsDebuggerScript());
    628 
    629  Maybe<ClientInfo> clientInfo = GetGlobal()->GetClientInfo();
    630 
    631  // (For non-serviceworkers, this variable does not matter, but false best
    632  // captures their behavior.)
    633  bool onlyExistingCachedResourcesAllowed = false;
    634  if (mWorkerRef->Private()->IsServiceWorker()) {
    635    // https://w3c.github.io/ServiceWorker/#importscripts step 4:
    636    // > 4. If serviceWorker’s state is not "parsed" or "installing":
    637    // >    1. Return map[url] if it exists and a network error otherwise.
    638    //
    639    // So if our state is beyond installing, it's too late to make a request
    640    // that would perform a new fetch which would be cached.
    641    onlyExistingCachedResourcesAllowed =
    642        mWorkerRef->Private()->GetServiceWorkerDescriptor().State() >
    643        ServiceWorkerState::Installing;
    644  }
    645  RefPtr<WorkerLoadContext> loadContext = new WorkerLoadContext(
    646      kind, clientInfo, this, onlyExistingCachedResourcesAllowed);
    647 
    648  // Create ScriptLoadRequests for this WorkerScriptLoader
    649  ReferrerPolicy referrerPolicy = mWorkerRef->Private()->GetReferrerPolicy();
    650 
    651  // Only top level workers' main script use the document charset for the
    652  // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
    653  MOZ_ASSERT_IF(bool(aDocumentEncoding),
    654                aIsMainScript && !mWorkerRef->Private()->GetParent());
    655  nsCOMPtr<nsIURI> baseURI = aIsMainScript ? GetInitialBaseURI() : GetBaseURI();
    656  nsCOMPtr<nsIURI> uri;
    657  nsresult rv =
    658      ConstructURI(aScriptURL, baseURI, aDocumentEncoding, getter_AddRefs(uri));
    659  // If we failed to construct the URI, handle it in the LoadContext so it is
    660  // thrown in the right order.
    661  if (NS_WARN_IF(NS_FAILED(rv))) {
    662    // This function is used by the following:
    663    //   * Worker constructor
    664    //   * WorkerGlobalScope.importScripts
    665    //   * WorkerDebuggerGlobalScope.loadSubScript
    666    //
    667    // Worker constructor validates the URL in WorkerPrivate::Constructor,
    668    // and this branch shouldn't taken.
    669    //
    670    // importScripts is available only to classic scripts.
    671    //
    672    // loadSubScript is available only to privileged scripts, and we don't
    673    // care any invalid URLs.
    674    //
    675    // Module imports should use WorkerModuleLoader instead.
    676    MOZ_ASSERT(mWorkerRef->Private()->WorkerType() == WorkerType::Classic);
    677 
    678    *aRv = rv;
    679    return nullptr;
    680  }
    681 
    682  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-worker-script
    683  // Step 2.5. Let script be the result [...] and the default classic script
    684  // fetch options.
    685  //
    686  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
    687  // Step 1. Let options be a script fetch options whose cryptographic nonce is
    688  // the empty string, integrity metadata is the empty string, parser metadata
    689  // is "not-parser-inserted", credentials mode is credentials mode, referrer
    690  // policy is the empty string, and fetch priority is "auto".
    691  RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
    692      CORSMode::CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
    693      ParserMetadata::NotParserInserted, nullptr);
    694 
    695  RefPtr<ScriptLoadRequest> request = nullptr;
    696  // Bug 1817259 - For now the debugger scripts are always loaded a Classic.
    697  if (mWorkerRef->Private()->WorkerType() == WorkerType::Classic ||
    698      IsDebuggerScript()) {
    699    request = new ScriptLoadRequest(ScriptKind::eClassic, SRIMetadata(),
    700                                    nullptr,  // mReferrer
    701                                    loadContext);
    702  } else {
    703    // Implements part of "To fetch a worklet/module worker script graph"
    704    // including, setting up the request with a credentials mode,
    705    // destination.
    706 
    707    // Step 1. Let options be a script fetch options.
    708    // We currently don't track credentials in our ScriptFetchOptions
    709    // implementation, so we are defaulting the fetchOptions object defined
    710    // above. This behavior is handled fully in GetModuleSecFlags.
    711 
    712    RefPtr<WorkerModuleLoader::ModuleLoaderBase> moduleLoader =
    713        GetGlobal()->GetModuleLoader(nullptr);
    714 
    715    // Implements the referrer for "To fetch a single module script"
    716    // Our implementation does not have a "client" as a referrer.
    717    // However, when client is resolved (per 8.3. Determine request’s
    718    // Referrer in
    719    // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer)
    720    // This should result in the referrer source being the creation URL.
    721    //
    722    // In subresource modules, the referrer is the importing script.
    723    nsCOMPtr<nsIURI> referrer =
    724        mWorkerRef->Private()->GetReferrerInfo()->GetOriginalReferrer();
    725 
    726    // Part of Step 2. This sets the Top-level flag to true
    727    request = new ModuleLoadRequest(
    728        JS::ModuleType::JavaScript, SRIMetadata(), referrer, loadContext,
    729        ModuleLoadRequest::Kind::TopLevel, moduleLoader, nullptr);
    730  }
    731 
    732  // Set the mURL, it will be used for error handling and debugging.
    733  request->mURL = NS_ConvertUTF16toUTF8(aScriptURL);
    734 
    735  request->NoCacheEntryFound(referrerPolicy, fetchOptions, uri);
    736 
    737  return request.forget();
    738 }
    739 
    740 bool WorkerScriptLoader::DispatchLoadScript(ScriptLoadRequest* aRequest) {
    741  mWorkerRef->Private()->AssertIsOnWorkerThread();
    742 
    743  IncreaseLoadingModuleRequestCount();
    744 
    745  nsTArray<RefPtr<ThreadSafeRequestHandle>> scriptLoadList;
    746  RefPtr<ThreadSafeRequestHandle> handle =
    747      new ThreadSafeRequestHandle(aRequest, mSyncLoopTarget.get());
    748  scriptLoadList.AppendElement(handle.forget());
    749 
    750  return DispatchLoadScripts(std::move(scriptLoadList));
    751 }
    752 
    753 bool WorkerScriptLoader::DispatchLoadScripts(
    754    nsTArray<RefPtr<ThreadSafeRequestHandle>>&& aLoadingList) {
    755  MOZ_ASSERT(mWorkerRef->Private()->IsOnWorkerThread());
    756 
    757  nsTArray<RefPtr<ThreadSafeRequestHandle>> scriptLoadList =
    758      std::move(aLoadingList);
    759  if (!scriptLoadList.Length()) {
    760    // Try to get a loading list if we were not passed one explcitly.
    761    scriptLoadList = GetLoadingList();
    762  }
    763 
    764  RefPtr<ScriptLoaderRunnable> runnable =
    765      new ScriptLoaderRunnable(this, std::move(scriptLoadList));
    766 
    767  RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
    768      mWorkerRef->Private(), "WorkerScriptLoader::DispatchLoadScripts",
    769      [runnable]() {
    770        NS_DispatchToMainThread(NewRunnableMethod(
    771            "ScriptLoaderRunnable::CancelMainThreadWithBindingAborted",
    772            runnable,
    773            &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted));
    774      });
    775 
    776  if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
    777    NS_ERROR("Failed to dispatch!");
    778    mRv.Throw(NS_ERROR_FAILURE);
    779    return false;
    780  }
    781  return true;
    782 }
    783 
    784 nsIURI* WorkerScriptLoader::GetInitialBaseURI() {
    785  MOZ_ASSERT(mWorkerRef->Private());
    786  nsIURI* baseURI;
    787  WorkerPrivate* parentWorker = mWorkerRef->Private()->GetParent();
    788  if (parentWorker) {
    789    baseURI = parentWorker->GetBaseURI();
    790  } else {
    791    // May be null.
    792    baseURI = mWorkerRef->Private()->GetBaseURI();
    793  }
    794 
    795  return baseURI;
    796 }
    797 
    798 nsIURI* WorkerScriptLoader::GetBaseURI() const {
    799  MOZ_ASSERT(mWorkerRef);
    800  nsIURI* baseURI;
    801  baseURI = mWorkerRef->Private()->GetBaseURI();
    802  NS_ASSERTION(baseURI, "Should have been set already!");
    803 
    804  return baseURI;
    805 }
    806 
    807 nsIGlobalObject* WorkerScriptLoader::GetGlobal() {
    808  mWorkerRef->Private()->AssertIsOnWorkerThread();
    809  return mWorkerScriptType == WorkerScript
    810             ? static_cast<nsIGlobalObject*>(
    811                   mWorkerRef->Private()->GlobalScope())
    812             : mWorkerRef->Private()->DebuggerGlobalScope();
    813 }
    814 
    815 void WorkerScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest* aRequest) {
    816  mWorkerRef->Private()->AssertIsOnWorkerThread();
    817  // Only set to ready for regular scripts. Module loader will set the script to
    818  // ready if it is a Module Request.
    819  if (!aRequest->IsModuleRequest()) {
    820    aRequest->SetReady();
    821  }
    822 
    823  // If the request is not in a list, we are in an illegal state.
    824  MOZ_RELEASE_ASSERT(aRequest->isInList());
    825 
    826  while (!mLoadingRequests.isEmpty()) {
    827    ScriptLoadRequest* request = mLoadingRequests.getFirst();
    828    // We need to move requests in post order. If prior requests have not
    829    // completed, delay execution.
    830    if (!request->IsFinished()) {
    831      break;
    832    }
    833 
    834    RefPtr<ScriptLoadRequest> req = mLoadingRequests.Steal(request);
    835    mLoadedRequests.AppendElement(req);
    836  }
    837 }
    838 
    839 bool WorkerScriptLoader::StoreCSP() {
    840  // We must be on the same worker as we started on.
    841  mWorkerRef->Private()->AssertIsOnWorkerThread();
    842 
    843  if (!mWorkerRef->Private()->GetJSContext()) {
    844    return false;
    845  }
    846 
    847  MOZ_ASSERT(!mRv.Failed());
    848 
    849  // Move the CSP from the workerLoadInfo in the corresponding Client
    850  // where the CSP code expects it!
    851  mWorkerRef->Private()->StoreCSPOnClient();
    852  return true;
    853 }
    854 
    855 bool WorkerScriptLoader::ProcessPendingRequests(JSContext* aCx) {
    856  mWorkerRef->Private()->AssertIsOnWorkerThread();
    857  // Don't run if something else has already failed.
    858  if (mExecutionAborted) {
    859    mLoadedRequests.CancelRequestsAndClear();
    860    TryShutdown();
    861    return true;
    862  }
    863 
    864  // If nothing else has failed, our ErrorResult better not be a failure
    865  // either.
    866  MOZ_ASSERT(!mRv.Failed(), "Who failed it and why?");
    867 
    868  // Slightly icky action at a distance, but there's no better place to stash
    869  // this value, really.
    870  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
    871  MOZ_ASSERT(global);
    872 
    873  while (!mLoadedRequests.isEmpty()) {
    874    RefPtr<ScriptLoadRequest> req = mLoadedRequests.StealFirst();
    875    // We don't have a ProcessRequest method (like we do on the DOM), as there
    876    // isn't much processing that we need to do per request that isn't related
    877    // to evaluation (the processsing done for the DOM is handled in
    878    // DataRecievedFrom{Cache,Network} for workers.
    879    // So, this inner loop calls EvaluateScript directly. This will change
    880    // once modules are introduced as we will have some extra work to do.
    881    if (!EvaluateScript(aCx, req)) {
    882      req->Cancel();
    883      mExecutionAborted = true;
    884      WorkerLoadContext* loadContext = req->GetWorkerLoadContext();
    885      mMutedErrorFlag = loadContext->mMutedErrorFlag.valueOr(true);
    886      mLoadedRequests.CancelRequestsAndClear();
    887      break;
    888    }
    889  }
    890 
    891  TryShutdown();
    892  return true;
    893 }
    894 
    895 nsresult WorkerScriptLoader::LoadScript(
    896    ThreadSafeRequestHandle* aRequestHandle) {
    897  AssertIsOnMainThread();
    898 
    899  WorkerLoadContext* loadContext = aRequestHandle->GetContext();
    900  ScriptLoadRequest* request = aRequestHandle->GetRequest();
    901  MOZ_ASSERT_IF(loadContext->IsTopLevel(), !IsDebuggerScript());
    902 
    903  // The URL passed to us for loading was invalid, stop loading at this point.
    904  if (loadContext->mLoadResult != NS_ERROR_NOT_INITIALIZED) {
    905    return loadContext->mLoadResult;
    906  }
    907 
    908  WorkerPrivate* parentWorker = mWorkerRef->Private()->GetParent();
    909 
    910  // For JavaScript debugging, the devtools server must run on the same
    911  // thread as the debuggee, indicating the worker uses content principal.
    912  // However, in Bug 863246, web content will no longer be able to load
    913  // resource:// URIs by default, so we need system principal to load
    914  // debugger scripts.
    915  nsIPrincipal* principal = (IsDebuggerScript())
    916                                ? nsContentUtils::GetSystemPrincipal()
    917                                : mWorkerRef->Private()->GetPrincipal();
    918 
    919  nsCOMPtr<nsILoadGroup> loadGroup = mWorkerRef->Private()->GetLoadGroup();
    920  MOZ_DIAGNOSTIC_ASSERT(principal);
    921 
    922  NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup, principal),
    923                 NS_ERROR_FAILURE);
    924 
    925  // May be null.
    926  nsCOMPtr<Document> parentDoc = mWorkerRef->Private()->GetDocument();
    927 
    928  nsCOMPtr<nsIChannel> channel;
    929  if (loadContext->IsTopLevel()) {
    930    // May be null.
    931    channel = mWorkerRef->Private()->ForgetWorkerChannel();
    932  }
    933 
    934  nsCOMPtr<nsIIOService> ios(do_GetIOService());
    935 
    936  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    937  NS_ASSERTION(secMan, "This should never be null!");
    938 
    939  nsresult& rv = loadContext->mLoadResult;
    940 
    941  nsLoadFlags loadFlags = mWorkerRef->Private()->GetLoadFlags();
    942 
    943  // Get the top-level worker.
    944  WorkerPrivate* topWorkerPrivate = mWorkerRef->Private();
    945  WorkerPrivate* parent = topWorkerPrivate->GetParent();
    946  while (parent) {
    947    topWorkerPrivate = parent;
    948    parent = topWorkerPrivate->GetParent();
    949  }
    950 
    951  // If the top-level worker is a dedicated worker and has a window, and the
    952  // window has a docshell, the caching behavior of this worker should match
    953  // that of that docshell.
    954  if (topWorkerPrivate->IsDedicatedWorker()) {
    955    nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
    956    if (window) {
    957      nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
    958      if (docShell) {
    959        nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
    960        NS_ENSURE_SUCCESS(rv, rv);
    961      }
    962    }
    963  }
    964 
    965  if (!channel) {
    966    nsCOMPtr<nsIReferrerInfo> referrerInfo;
    967    uint32_t secFlags;
    968    if (request->IsModuleRequest()) {
    969      // https://fetch.spec.whatwg.org/#concept-main-fetch
    970      // Step 8. If request’s referrer policy is the empty string, then set
    971      //         request’s referrer policy to request’s policy container’s
    972      //         referrer policy.
    973      ReferrerPolicy policy =
    974          request->ReferrerPolicy() == ReferrerPolicy::_empty
    975              ? mWorkerRef->Private()->GetReferrerPolicy()
    976              : request->ReferrerPolicy();
    977 
    978      referrerInfo = new ReferrerInfo(request->mReferrer, policy);
    979 
    980      // https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
    981      // The default classic script fetch options are a script fetch options
    982      // whose ... credentials mode is "same-origin", ....
    983      RequestCredentials credentials =
    984          mWorkerRef->Private()->WorkerType() == WorkerType::Classic
    985              ? RequestCredentials::Same_origin
    986              : mWorkerRef->Private()->WorkerCredentials();
    987 
    988      rv = GetModuleSecFlags(loadContext->IsTopLevel(), principal,
    989                             mWorkerScriptType, request->URI(), credentials,
    990                             secFlags);
    991    } else {
    992      referrerInfo = ReferrerInfo::CreateForFetch(principal, nullptr);
    993      if (parentWorker && !loadContext->IsTopLevel()) {
    994        referrerInfo =
    995            static_cast<ReferrerInfo*>(referrerInfo.get())
    996                ->CloneWithNewPolicy(parentWorker->GetReferrerPolicy());
    997      }
    998      rv = GetClassicSecFlags(loadContext->IsTopLevel(), request->URI(),
    999                              principal, mWorkerScriptType, secFlags);
   1000    }
   1001 
   1002    if (NS_WARN_IF(NS_FAILED(rv))) {
   1003      return rv;
   1004    }
   1005 
   1006    nsContentPolicyType contentPolicyType = GetContentPolicyType(request);
   1007 
   1008    rv = ChannelFromScriptURL(
   1009        principal, parentDoc, mWorkerRef->Private(), loadGroup, ios, secMan,
   1010        request->URI(), loadContext->mClientInfo, mController,
   1011        loadContext->IsTopLevel(), mWorkerScriptType, contentPolicyType,
   1012        loadFlags, secFlags, mWorkerRef->Private()->CookieJarSettings(),
   1013        referrerInfo, getter_AddRefs(channel));
   1014    if (NS_WARN_IF(NS_FAILED(rv))) {
   1015      return rv;
   1016    }
   1017 
   1018    // Set the IsInThirdPartyContext for the channel's loadInfo according to the
   1019    // partitionKey of the principal. The worker is foreign if it's using
   1020    // partitioned principal, i.e. the partitionKey is not empty. In this case,
   1021    // we need to set the bit to the channel's loadInfo.
   1022    if (!principal->OriginAttributesRef().mPartitionKey.IsEmpty()) {
   1023      nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   1024      rv = loadInfo->SetIsInThirdPartyContext(true);
   1025      if (NS_WARN_IF(NS_FAILED(rv))) {
   1026        return rv;
   1027      }
   1028    }
   1029  }
   1030 
   1031  // Associate any originating stack with the channel.
   1032  if (!mOriginStackJSON.IsEmpty()) {
   1033    NotifyNetworkMonitorAlternateStack(channel, mOriginStackJSON);
   1034  }
   1035 
   1036  // We need to know which index we're on in OnStreamComplete so we know
   1037  // where to put the result.
   1038  RefPtr<NetworkLoadHandler> listener =
   1039      new NetworkLoadHandler(this, aRequestHandle);
   1040 
   1041  RefPtr<ScriptResponseHeaderProcessor> headerProcessor = nullptr;
   1042 
   1043  // For each debugger script, a non-debugger script load of the same script
   1044  // should have occured prior that processed the headers.
   1045  if (!IsDebuggerScript()) {
   1046    JS::ModuleType moduleType = request->IsModuleRequest()
   1047                                    ? request->AsModuleRequest()->mModuleType
   1048                                    : JS::ModuleType::JavaScript;
   1049 
   1050    bool requiresStrictMimeCheck =
   1051        GetContentPolicyType(request) ==
   1052            nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS ||
   1053        request->IsModuleRequest();
   1054 
   1055    headerProcessor = MakeRefPtr<ScriptResponseHeaderProcessor>(
   1056        mWorkerRef, loadContext->IsTopLevel() && !IsDynamicImport(request),
   1057        requiresStrictMimeCheck, moduleType);
   1058  }
   1059 
   1060  nsCOMPtr<nsIStreamLoader> loader;
   1061  rv = NS_NewStreamLoader(getter_AddRefs(loader), listener, headerProcessor);
   1062  if (NS_WARN_IF(NS_FAILED(rv))) {
   1063    return rv;
   1064  }
   1065 
   1066  if (loadContext->IsTopLevel()) {
   1067    MOZ_DIAGNOSTIC_ASSERT(loadContext->mClientInfo.isSome());
   1068 
   1069    // In order to get the correct foreign partitioned prinicpal, we need to
   1070    // set the `IsThirdPartyContextToTopWindow` to the channel's loadInfo.
   1071    // This flag reflects the fact that if the worker is created under a
   1072    // third-party context.
   1073    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   1074    loadInfo->SetIsInThirdPartyContext(
   1075        mWorkerRef->Private()->IsThirdPartyContext());
   1076 
   1077    Maybe<ClientInfo> clientInfo;
   1078    clientInfo.emplace(loadContext->mClientInfo.ref());
   1079    rv = AddClientChannelHelper(channel, std::move(clientInfo),
   1080                                Maybe<ClientInfo>(),
   1081                                mWorkerRef->Private()->HybridEventTarget());
   1082    if (NS_WARN_IF(NS_FAILED(rv))) {
   1083      return rv;
   1084    }
   1085  }
   1086 
   1087  if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   1088    nsILoadInfo::CrossOriginEmbedderPolicy respectedCOEP =
   1089        mWorkerRef->Private()->GetEmbedderPolicy();
   1090    if (mWorkerRef->Private()->IsDedicatedWorker() &&
   1091        respectedCOEP == nsILoadInfo::EMBEDDER_POLICY_NULL) {
   1092      respectedCOEP = mWorkerRef->Private()->GetOwnerEmbedderPolicy();
   1093    }
   1094 
   1095    nsCOMPtr<nsILoadInfo> channelLoadInfo = channel->LoadInfo();
   1096    channelLoadInfo->SetLoadingEmbedderPolicy(respectedCOEP);
   1097  }
   1098 
   1099  if (loadContext->mCacheStatus != WorkerLoadContext::ToBeCached) {
   1100    rv = channel->AsyncOpen(loader);
   1101    if (NS_WARN_IF(NS_FAILED(rv))) {
   1102      return rv;
   1103    }
   1104  } else {
   1105    nsCOMPtr<nsIOutputStream> writer;
   1106 
   1107    // In case we return early.
   1108    loadContext->mCacheStatus = WorkerLoadContext::Cancel;
   1109 
   1110    NS_NewPipe(getter_AddRefs(loadContext->mCacheReadStream),
   1111               getter_AddRefs(writer), 0,
   1112               UINT32_MAX,    // unlimited size to avoid writer WOULD_BLOCK case
   1113               true, false);  // non-blocking reader, blocking writer
   1114 
   1115    nsCOMPtr<nsIStreamListenerTee> tee =
   1116        do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
   1117    rv = tee->Init(loader, writer, listener);
   1118    if (NS_WARN_IF(NS_FAILED(rv))) {
   1119      return rv;
   1120    }
   1121 
   1122    nsresult rv = channel->AsyncOpen(tee);
   1123    if (NS_WARN_IF(NS_FAILED(rv))) {
   1124      return rv;
   1125    }
   1126  }
   1127 
   1128  loadContext->mChannel.swap(channel);
   1129 
   1130  return NS_OK;
   1131 }
   1132 
   1133 nsresult WorkerScriptLoader::FillCompileOptionsForRequest(
   1134    JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions,
   1135    JS::MutableHandle<JSScript*> aIntroductionScript) {
   1136  // The full URL shouldn't be exposed to the debugger. See Bug 1634872
   1137  aOptions->setFileAndLine(aRequest->mURL.get(), 1);
   1138  aOptions->setNoScriptRval(true);
   1139 
   1140  aOptions->setMutedErrors(
   1141      aRequest->GetWorkerLoadContext()->mMutedErrorFlag.value());
   1142 
   1143  if (aRequest->HasSourceMapURL()) {
   1144    aOptions->setSourceMapURL(aRequest->GetSourceMapURL().get());
   1145  }
   1146 
   1147  // disable top-level await for sw module scripts
   1148  const auto* workerPrivate = GetCurrentThreadWorkerPrivate();
   1149  if (workerPrivate && workerPrivate->IsServiceWorker() &&
   1150      aRequest->IsModuleRequest()) {
   1151    aOptions->topLevelAwait = false;
   1152  }
   1153 
   1154  return NS_OK;
   1155 }
   1156 
   1157 bool WorkerScriptLoader::EvaluateScript(JSContext* aCx,
   1158                                        ScriptLoadRequest* aRequest) {
   1159  mWorkerRef->Private()->AssertIsOnWorkerThread();
   1160  MOZ_ASSERT(!IsDynamicImport(aRequest));
   1161 
   1162  WorkerLoadContext* loadContext = aRequest->GetWorkerLoadContext();
   1163 
   1164  NS_ASSERTION(!loadContext->mChannel, "Should no longer have a channel!");
   1165  NS_ASSERTION(aRequest->IsFinished(), "Should be scheduled!");
   1166 
   1167  MOZ_ASSERT(!mRv.Failed(), "Who failed it and why?");
   1168  mRv.MightThrowJSException();
   1169  if (NS_FAILED(loadContext->mLoadResult)) {
   1170    ReportErrorToConsole(aRequest, loadContext->mLoadResult);
   1171    return false;
   1172  }
   1173 
   1174  // If this is a top level script that succeeded, then mark the
   1175  // Client execution ready and possible controlled by a service worker.
   1176  if (loadContext->IsTopLevel()) {
   1177    if (mController.isSome()) {
   1178      MOZ_ASSERT(mWorkerScriptType == WorkerScript,
   1179                 "Debugger clients can't be controlled.");
   1180      mWorkerRef->Private()->GlobalScope()->Control(mController.ref());
   1181    }
   1182    mWorkerRef->Private()->ExecutionReady();
   1183  }
   1184 
   1185  if (aRequest->IsModuleRequest()) {
   1186    // Only the top level module of the module graph will be executed from here,
   1187    // the rest will be executed from SpiderMonkey as part of the execution of
   1188    // the module graph.
   1189    MOZ_ASSERT(aRequest->IsTopLevel());
   1190    ModuleLoadRequest* request = aRequest->AsModuleRequest();
   1191    if (!request->mModuleScript) {
   1192      return false;
   1193    }
   1194 
   1195    // https://html.spec.whatwg.org/#run-a-worker
   1196    // if script's error to rethrow is non-null, then:
   1197    //    Queue a global task on the DOM manipulation task source given worker's
   1198    //    relevant global object to fire an event named error at worker.
   1199    //
   1200    // The event will be dispatched in CompileScriptRunnable.
   1201    if (request->mModuleScript->HasParseError() ||
   1202        request->mModuleScript->HasErrorToRethrow()) {
   1203      // Here we assign an error code that is not a JS Exception, so
   1204      // CompileRunnable can dispatch the event.
   1205      mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1206      return false;
   1207    }
   1208 
   1209    // Implements To fetch a worklet/module worker script graph
   1210    // Step 5. Fetch the descendants of and link result.
   1211    if (!request->InstantiateModuleGraph()) {
   1212      return false;
   1213    }
   1214 
   1215    if (request->mModuleScript->HasErrorToRethrow()) {
   1216      // See the comments when we check HasParseError() above.
   1217      mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1218      return false;
   1219    }
   1220 
   1221    nsresult rv = request->EvaluateModule();
   1222    return NS_SUCCEEDED(rv);
   1223  }
   1224 
   1225  JS::CompileOptions options(aCx);
   1226  // The introduction script is used by the DOM script loader as a way
   1227  // to fill the Debugger Metadata for the JS Execution context. We don't use
   1228  // the JS Execution context as we are not making use of async compilation
   1229  // (delegation to another worker to produce bytecode or compile a string to a
   1230  // JSScript), so it is not used in this context.
   1231  JS::Rooted<JSScript*> unusedIntroductionScript(aCx);
   1232  nsresult rv = FillCompileOptionsForRequest(aCx, aRequest, &options,
   1233                                             &unusedIntroductionScript);
   1234 
   1235  MOZ_ASSERT(NS_SUCCEEDED(rv), "Filling compile options should not fail");
   1236 
   1237  // Our ErrorResult still shouldn't be a failure.
   1238  MOZ_ASSERT(!mRv.Failed(), "Who failed it and why?");
   1239 
   1240  // Get the source text.
   1241  ScriptLoadRequest::MaybeSourceText maybeSource;
   1242  rv = aRequest->GetScriptSource(aCx, &maybeSource,
   1243                                 aRequest->mLoadContext.get());
   1244  if (NS_FAILED(rv)) {
   1245    mRv.StealExceptionFromJSContext(aCx);
   1246    return false;
   1247  }
   1248 
   1249  RefPtr<JS::loader::ClassicScript> classicScript = nullptr;
   1250  if (!mWorkerRef->Private()->IsServiceWorker()) {
   1251    // We need a LoadedScript to be associated with the JSScript in order to
   1252    // correctly resolve the referencing private for dynamic imports. In turn
   1253    // this allows us to correctly resolve the BaseURL.
   1254    //
   1255    // Dynamic import is disallowed on service workers.  Additionally, causes
   1256    // crashes because the life cycle isn't completed for service workers.  To
   1257    // keep things simple, we don't create a classic script for ServiceWorkers.
   1258    // If this changes then we will need to ensure that the reference that is
   1259    // held is released appropriately.
   1260    nsCOMPtr<nsIURI> requestBaseURI;
   1261    if (loadContext->mMutedErrorFlag.valueOr(false)) {
   1262      NS_NewURI(getter_AddRefs(requestBaseURI), "about:blank"_ns);
   1263    } else {
   1264      requestBaseURI = aRequest->BaseURL();
   1265    }
   1266    MOZ_ASSERT(aRequest->mLoadedScript->IsClassicScript());
   1267    aRequest->mLoadedScript->SetBaseURL(requestBaseURI);
   1268    classicScript = aRequest->mLoadedScript->AsClassicScript();
   1269  }
   1270 
   1271  JS::Rooted<JSScript*> script(aCx);
   1272  script = aRequest->IsUTF8Text()
   1273               ? JS::Compile(aCx, options,
   1274                             maybeSource.ref<JS::SourceText<Utf8Unit>>())
   1275               : JS::Compile(aCx, options,
   1276                             maybeSource.ref<JS::SourceText<char16_t>>());
   1277  if (!script) {
   1278    if (loadContext->IsTopLevel()) {
   1279      // This is a top-level worker script,
   1280      //
   1281      // https://html.spec.whatwg.org/#run-a-worker
   1282      // If script is null or if script's error to rethrow is non-null, then:
   1283      //   Queue a global task on the DOM manipulation task source given
   1284      //   worker's relevant global object to fire an event named error at
   1285      //   worker.
   1286      JS_ClearPendingException(aCx);
   1287      mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1288    } else {
   1289      // This is a script which is loaded by importScripts().
   1290      //
   1291      // https://html.spec.whatwg.org/#import-scripts-into-worker-global-scope
   1292      // For each url in the resulting URL records:
   1293      //   Fetch a classic worker-imported script given url and settings object,
   1294      //   passing along performFetch if provided. If this succeeds, let script
   1295      //   be the result. Otherwise, rethrow the exception.
   1296      mRv.StealExceptionFromJSContext(aCx);
   1297    }
   1298 
   1299    return false;
   1300  }
   1301 
   1302  bool successfullyEvaluated = EvaluateSourceBuffer(aCx, script, classicScript);
   1303  if (aRequest->IsCanceled()) {
   1304    return false;
   1305  }
   1306  if (!successfullyEvaluated) {
   1307    mRv.StealExceptionFromJSContext(aCx);
   1308    return false;
   1309  }
   1310  // steal the loadContext so that the cycle is broken and cycle collector can
   1311  // collect the scriptLoadRequest.
   1312  return true;
   1313 }
   1314 
   1315 void WorkerScriptLoader::TryShutdown() {
   1316  {
   1317    MutexAutoLock lock(CleanUpLock());
   1318    if (CleanedUp()) {
   1319      return;
   1320    }
   1321  }
   1322 
   1323  if (AllScriptsExecuted() && AllModuleRequestsLoaded()) {
   1324    ShutdownScriptLoader(!mExecutionAborted, mMutedErrorFlag);
   1325  }
   1326 }
   1327 
   1328 void WorkerScriptLoader::ShutdownScriptLoader(bool aResult, bool aMutedError) {
   1329  MOZ_ASSERT(AllScriptsExecuted());
   1330  MOZ_ASSERT(AllModuleRequestsLoaded());
   1331  mWorkerRef->Private()->AssertIsOnWorkerThread();
   1332 
   1333  if (!aResult) {
   1334    // At this point there are two possibilities:
   1335    //
   1336    // 1) mRv.Failed().  In that case we just want to leave it
   1337    //    as-is, except if it has a JS exception and we need to mute JS
   1338    //    exceptions.  In that case, we log the exception without firing any
   1339    //    events and then replace it on the ErrorResult with a NetworkError,
   1340    //    per spec.
   1341    //
   1342    // 2) mRv succeeded.  As far as I can tell, this can only
   1343    //    happen when loading the main worker script and
   1344    //    GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
   1345    //    got called.  Does it matter what we throw in this case?  I'm not
   1346    //    sure...
   1347    if (mRv.Failed()) {
   1348      if (aMutedError && mRv.IsJSException()) {
   1349        LogExceptionToConsole(mWorkerRef->Private()->GetJSContext(),
   1350                              mWorkerRef->Private());
   1351        mRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
   1352      }
   1353    } else {
   1354      mRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1355    }
   1356  }
   1357 
   1358  // Lock, shutdown, and cleanup state. After this the Loader is closed.
   1359  {
   1360    MutexAutoLock lock(CleanUpLock());
   1361 
   1362    if (CleanedUp()) {
   1363      return;
   1364    }
   1365 
   1366    mWorkerRef->Private()->AssertIsOnWorkerThread();
   1367    // Module loader doesn't use sync loop for dynamic import
   1368    if (mSyncLoopTarget) {
   1369      mWorkerRef->Private()->MaybeStopSyncLoop(
   1370          mSyncLoopTarget, aResult ? NS_OK : NS_ERROR_FAILURE);
   1371      mSyncLoopTarget = nullptr;
   1372    }
   1373 
   1374    // Signal cleanup
   1375    mCleanedUp = true;
   1376 
   1377    // Allow worker shutdown.
   1378    mWorkerRef = nullptr;
   1379  }
   1380 }
   1381 
   1382 void WorkerScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
   1383                                              nsresult aResult) const {
   1384  nsAutoString url = NS_ConvertUTF8toUTF16(aRequest->mURL);
   1385  workerinternals::ReportLoadError(mRv, aResult, url);
   1386 }
   1387 
   1388 void WorkerScriptLoader::LogExceptionToConsole(JSContext* aCx,
   1389                                               WorkerPrivate* aWorkerPrivate) {
   1390  aWorkerPrivate->AssertIsOnWorkerThread();
   1391 
   1392  MOZ_ASSERT(mRv.IsJSException());
   1393 
   1394  JS::Rooted<JS::Value> exn(aCx);
   1395  if (!ToJSValue(aCx, std::move(mRv), &exn)) {
   1396    return;
   1397  }
   1398 
   1399  // Now the exception state should all be in exn.
   1400  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
   1401  MOZ_ASSERT(!mRv.Failed());
   1402 
   1403  JS::ExceptionStack exnStack(aCx, exn, nullptr);
   1404  JS::ErrorReportBuilder report(aCx);
   1405  if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
   1406    JS_ClearPendingException(aCx);
   1407    return;
   1408  }
   1409 
   1410  RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
   1411  xpcReport->Init(report.report(), report.toStringResult().c_str(),
   1412                  aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
   1413 
   1414  RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
   1415  NS_DispatchToMainThread(r);
   1416 }
   1417 
   1418 bool WorkerScriptLoader::AllModuleRequestsLoaded() const {
   1419  mWorkerRef->Private()->AssertIsOnWorkerThread();
   1420  return mLoadingModuleRequestCount == 0;
   1421 }
   1422 
   1423 void WorkerScriptLoader::IncreaseLoadingModuleRequestCount() {
   1424  mWorkerRef->Private()->AssertIsOnWorkerThread();
   1425  ++mLoadingModuleRequestCount;
   1426 }
   1427 
   1428 void WorkerScriptLoader::DecreaseLoadingModuleRequestCount() {
   1429  mWorkerRef->Private()->AssertIsOnWorkerThread();
   1430  --mLoadingModuleRequestCount;
   1431 }
   1432 
   1433 NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable, nsINamed)
   1434 
   1435 NS_IMPL_ISUPPORTS(WorkerScriptLoader, nsINamed)
   1436 
   1437 ScriptLoaderRunnable::ScriptLoaderRunnable(
   1438    WorkerScriptLoader* aScriptLoader,
   1439    nsTArray<RefPtr<ThreadSafeRequestHandle>> aLoadingRequests)
   1440    : mScriptLoader(aScriptLoader),
   1441      mWorkerRef(aScriptLoader->mWorkerRef),
   1442      mLoadingRequests(std::move(aLoadingRequests)),
   1443      mCancelMainThread(Nothing()) {
   1444  MOZ_ASSERT(aScriptLoader);
   1445 }
   1446 
   1447 nsresult ScriptLoaderRunnable::Run() {
   1448  AssertIsOnMainThread();
   1449 
   1450  // Convert the origin stack to JSON (which must be done on the main
   1451  // thread) explicitly, so that we can use the stack to notify the net
   1452  // monitor about every script we load. We do this, rather than pass
   1453  // the stack directly to the netmonitor, in order to be able to use this
   1454  // for all subsequent scripts.
   1455  if (mScriptLoader->mOriginStack &&
   1456      mScriptLoader->mOriginStackJSON.IsEmpty()) {
   1457    ConvertSerializedStackToJSON(std::move(mScriptLoader->mOriginStack),
   1458                                 mScriptLoader->mOriginStackJSON);
   1459  }
   1460 
   1461  if (!mWorkerRef->Private()->IsServiceWorker() ||
   1462      mScriptLoader->IsDebuggerScript()) {
   1463    for (ThreadSafeRequestHandle* handle : mLoadingRequests) {
   1464      handle->mRunnable = this;
   1465    }
   1466 
   1467    for (ThreadSafeRequestHandle* handle : mLoadingRequests) {
   1468      nsresult rv = mScriptLoader->LoadScript(handle);
   1469      if (NS_WARN_IF(NS_FAILED(rv))) {
   1470        LoadingFinished(handle, rv);
   1471        CancelMainThread(rv);
   1472        return rv;
   1473      }
   1474    }
   1475 
   1476    return NS_OK;
   1477  }
   1478 
   1479  MOZ_ASSERT(!mCacheCreator);
   1480  mCacheCreator = new CacheCreator(mWorkerRef->Private());
   1481 
   1482  for (ThreadSafeRequestHandle* handle : mLoadingRequests) {
   1483    handle->mRunnable = this;
   1484    WorkerLoadContext* loadContext = handle->GetContext();
   1485    mCacheCreator->AddLoader(MakeNotNull<RefPtr<CacheLoadHandler>>(
   1486        mWorkerRef, handle, loadContext->IsTopLevel(),
   1487        loadContext->mOnlyExistingCachedResourcesAllowed, mScriptLoader));
   1488  }
   1489 
   1490  // The worker may have a null principal on first load, but in that case its
   1491  // parent definitely will have one.
   1492  nsIPrincipal* principal = mWorkerRef->Private()->GetPrincipal();
   1493  if (!principal) {
   1494    WorkerPrivate* parentWorker = mWorkerRef->Private()->GetParent();
   1495    MOZ_ASSERT(parentWorker, "Must have a parent!");
   1496    principal = parentWorker->GetPrincipal();
   1497  }
   1498 
   1499  nsresult rv = mCacheCreator->Load(principal);
   1500  if (NS_WARN_IF(NS_FAILED(rv))) {
   1501    CancelMainThread(rv);
   1502    return rv;
   1503  }
   1504 
   1505  return NS_OK;
   1506 }
   1507 
   1508 nsresult ScriptLoaderRunnable::OnStreamComplete(
   1509    ThreadSafeRequestHandle* aRequestHandle, nsresult aStatus) {
   1510  AssertIsOnMainThread();
   1511 
   1512  LoadingFinished(aRequestHandle, aStatus);
   1513  return NS_OK;
   1514 }
   1515 
   1516 void ScriptLoaderRunnable::LoadingFinished(
   1517    ThreadSafeRequestHandle* aRequestHandle, nsresult aRv) {
   1518  AssertIsOnMainThread();
   1519 
   1520  WorkerLoadContext* loadContext = aRequestHandle->GetContext();
   1521 
   1522  loadContext->mLoadResult = aRv;
   1523  MOZ_ASSERT(!loadContext->mLoadingFinished);
   1524  loadContext->mLoadingFinished = true;
   1525 
   1526  if (loadContext->IsTopLevel() && NS_SUCCEEDED(aRv)) {
   1527    MOZ_DIAGNOSTIC_ASSERT(
   1528        mWorkerRef->Private()->PrincipalURIMatchesScriptURL());
   1529  }
   1530 
   1531  MaybeExecuteFinishedScripts(aRequestHandle);
   1532 }
   1533 
   1534 void ScriptLoaderRunnable::MaybeExecuteFinishedScripts(
   1535    ThreadSafeRequestHandle* aRequestHandle) {
   1536  AssertIsOnMainThread();
   1537 
   1538  // We execute the last step if we don't have a pending operation with the
   1539  // cache and the loading is completed.
   1540  WorkerLoadContext* loadContext = aRequestHandle->GetContext();
   1541  if (!loadContext->IsAwaitingPromise()) {
   1542    if (aRequestHandle->GetContext()->IsTopLevel()) {
   1543      mWorkerRef->Private()->WorkerScriptLoaded();
   1544    }
   1545    DispatchProcessPendingRequests();
   1546  }
   1547 }
   1548 
   1549 void ScriptLoaderRunnable::CancelMainThreadWithBindingAborted() {
   1550  AssertIsOnMainThread();
   1551  CancelMainThread(NS_BINDING_ABORTED);
   1552 }
   1553 
   1554 void ScriptLoaderRunnable::CancelMainThread(nsresult aCancelResult) {
   1555  AssertIsOnMainThread();
   1556  if (IsCancelled()) {
   1557    return;
   1558  }
   1559 
   1560  {
   1561    MutexAutoLock lock(mScriptLoader->CleanUpLock());
   1562 
   1563    // Check if we have already cancelled, or if the worker has been killed
   1564    // before we cancel.
   1565    if (mScriptLoader->CleanedUp()) {
   1566      return;
   1567    }
   1568 
   1569    mCancelMainThread = Some(aCancelResult);
   1570 
   1571    for (ThreadSafeRequestHandle* handle : mLoadingRequests) {
   1572      if (handle->IsEmpty()) {
   1573        continue;
   1574      }
   1575 
   1576      bool callLoadingFinished = true;
   1577 
   1578      WorkerLoadContext* loadContext = handle->GetContext();
   1579      if (!loadContext) {
   1580        continue;
   1581      }
   1582 
   1583      if (loadContext->IsAwaitingPromise()) {
   1584        MOZ_ASSERT(mWorkerRef->Private()->IsServiceWorker());
   1585        loadContext->mCachePromise->MaybeReject(NS_BINDING_ABORTED);
   1586        loadContext->mCachePromise = nullptr;
   1587        callLoadingFinished = false;
   1588      }
   1589      if (loadContext->mChannel) {
   1590        if (NS_SUCCEEDED(loadContext->mChannel->Cancel(aCancelResult))) {
   1591          callLoadingFinished = false;
   1592        } else {
   1593          NS_WARNING("Failed to cancel channel!");
   1594        }
   1595      }
   1596      if (callLoadingFinished && !loadContext->mLoadingFinished) {
   1597        LoadingFinished(handle, aCancelResult);
   1598      }
   1599    }
   1600    DispatchProcessPendingRequests();
   1601  }
   1602 }
   1603 
   1604 void ScriptLoaderRunnable::DispatchProcessPendingRequests() {
   1605  AssertIsOnMainThread();
   1606 
   1607  const auto begin = mLoadingRequests.begin();
   1608  const auto end = mLoadingRequests.end();
   1609  using Iterator = decltype(begin);
   1610  const auto maybeRangeToExecute =
   1611      [begin, end]() -> Maybe<std::pair<Iterator, Iterator>> {
   1612    // firstItToExecute is the first loadInfo where mExecutionScheduled is
   1613    // unset.
   1614    auto firstItToExecute = std::find_if(
   1615        begin, end, [](const RefPtr<ThreadSafeRequestHandle>& requestHandle) {
   1616          return !requestHandle->mExecutionScheduled;
   1617        });
   1618 
   1619    if (firstItToExecute == end) {
   1620      return Nothing();
   1621    }
   1622 
   1623    // firstItUnexecutable is the first loadInfo that is not yet finished.
   1624    // Update mExecutionScheduled on the ones we're about to schedule for
   1625    // execution.
   1626    const auto firstItUnexecutable =
   1627        std::find_if(firstItToExecute, end,
   1628                     [](RefPtr<ThreadSafeRequestHandle>& requestHandle) {
   1629                       MOZ_ASSERT(!requestHandle->IsEmpty());
   1630                       if (!requestHandle->Finished()) {
   1631                         return true;
   1632                       }
   1633 
   1634                       // We can execute this one.
   1635                       requestHandle->mExecutionScheduled = true;
   1636 
   1637                       return false;
   1638                     });
   1639 
   1640    return firstItUnexecutable == firstItToExecute
   1641               ? Nothing()
   1642               : Some(std::pair(firstItToExecute, firstItUnexecutable));
   1643  }();
   1644 
   1645  // If there are no unexecutable load infos, we can unuse things before the
   1646  // execution of the scripts and the stopping of the sync loop.
   1647  if (maybeRangeToExecute) {
   1648    if (maybeRangeToExecute->second == end) {
   1649      mCacheCreator = nullptr;
   1650    }
   1651 
   1652    RefPtr<ScriptExecutorRunnable> runnable = new ScriptExecutorRunnable(
   1653        mScriptLoader, mWorkerRef->Private(), mScriptLoader->mSyncLoopTarget,
   1654        Span<RefPtr<ThreadSafeRequestHandle>>{maybeRangeToExecute->first,
   1655                                              maybeRangeToExecute->second});
   1656 
   1657    if (!runnable->Dispatch(mWorkerRef->Private()) &&
   1658        mScriptLoader->mSyncLoopTarget) {
   1659      MOZ_ASSERT(false, "This should never fail!");
   1660    }
   1661  }
   1662 }
   1663 
   1664 ScriptExecutorRunnable::ScriptExecutorRunnable(
   1665    WorkerScriptLoader* aScriptLoader, WorkerPrivate* aWorkerPrivate,
   1666    nsISerialEventTarget* aSyncLoopTarget,
   1667    Span<RefPtr<ThreadSafeRequestHandle>> aLoadedRequests)
   1668    : MainThreadWorkerSyncRunnable(aSyncLoopTarget, "ScriptExecutorRunnable"),
   1669      mScriptLoader(aScriptLoader),
   1670      mLoadedRequests(aLoadedRequests) {}
   1671 
   1672 bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
   1673  // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
   1674  // In the latter case, the runnable needs to be dispatched to the debugger
   1675  // queue.
   1676  return mScriptLoader->IsDebuggerScript();
   1677 }
   1678 
   1679 bool ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate) {
   1680  aWorkerPrivate->AssertIsOnWorkerThread();
   1681 
   1682  // We must be on the same worker as we started on.
   1683  MOZ_ASSERT(
   1684      mScriptLoader->mSyncLoopTarget == mSyncLoopTarget,
   1685      "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
   1686 
   1687  {
   1688    // There is a possibility that we cleaned up while this task was waiting to
   1689    // run. If this has happened, return and exit.
   1690    MutexAutoLock lock(mScriptLoader->CleanUpLock());
   1691    if (mScriptLoader->CleanedUp()) {
   1692      return true;
   1693    }
   1694 
   1695    const auto& requestHandle = mLoadedRequests[0];
   1696    // Check if the request is still valid.
   1697    if (requestHandle->IsEmpty() ||
   1698        !requestHandle->GetContext()->IsTopLevel()) {
   1699      return true;
   1700    }
   1701  }
   1702 
   1703  return mScriptLoader->StoreCSP();
   1704 }
   1705 
   1706 bool ScriptExecutorRunnable::ProcessModuleScript(
   1707    JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
   1708  // We should only ever have one script when processing modules
   1709  MOZ_ASSERT(mLoadedRequests.Length() == 1);
   1710  RefPtr<ScriptLoadRequest> request;
   1711  {
   1712    // There is a possibility that we cleaned up while this task was waiting to
   1713    // run. If this has happened, return and exit.
   1714    MutexAutoLock lock(mScriptLoader->CleanUpLock());
   1715    if (mScriptLoader->CleanedUp()) {
   1716      return true;
   1717    }
   1718 
   1719    MOZ_ASSERT(mLoadedRequests.Length() == 1);
   1720    const auto& requestHandle = mLoadedRequests[0];
   1721    // The request must be valid.
   1722    MOZ_ASSERT(!requestHandle->IsEmpty());
   1723 
   1724    // Release the request to the worker. From this point on, the Request Handle
   1725    // is empty.
   1726    request = requestHandle->ReleaseRequest();
   1727 
   1728    // release lock. We will need it later if we cleanup.
   1729  }
   1730 
   1731  MOZ_ASSERT(request->IsModuleRequest());
   1732 
   1733  WorkerLoadContext* loadContext = request->GetWorkerLoadContext();
   1734  ModuleLoadRequest* moduleRequest = request->AsModuleRequest();
   1735  if (aWorkerPrivate->GetReferrerPolicy() != ReferrerPolicy::_empty) {
   1736    moduleRequest->UpdateReferrerPolicy(aWorkerPrivate->GetReferrerPolicy());
   1737  }
   1738 
   1739  // DecreaseLoadingModuleRequestCount must be called before OnFetchComplete.
   1740  // OnFetchComplete will call ProcessPendingRequests, and in
   1741  // ProcessPendingRequests it will try to shutdown if
   1742  // AllModuleRequestsLoaded() returns true.
   1743  mScriptLoader->DecreaseLoadingModuleRequestCount();
   1744  moduleRequest->OnFetchComplete(loadContext->mLoadResult);
   1745 
   1746  if (NS_FAILED(loadContext->mLoadResult)) {
   1747    if (moduleRequest->IsDynamicImport() || !moduleRequest->IsTopLevel()) {
   1748      mScriptLoader->TryShutdown();
   1749    }
   1750  }
   1751  return true;
   1752 }
   1753 
   1754 bool ScriptExecutorRunnable::ProcessClassicScripts(
   1755    JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
   1756  // There is a possibility that we cleaned up while this task was waiting to
   1757  // run. If this has happened, return and exit.
   1758  {
   1759    MutexAutoLock lock(mScriptLoader->CleanUpLock());
   1760    if (mScriptLoader->CleanedUp()) {
   1761      return true;
   1762    }
   1763 
   1764    for (const auto& requestHandle : mLoadedRequests) {
   1765      // The request must be valid.
   1766      MOZ_ASSERT(!requestHandle->IsEmpty());
   1767 
   1768      // Release the request to the worker. From this point on, the Request
   1769      // Handle is empty.
   1770      RefPtr<ScriptLoadRequest> request = requestHandle->ReleaseRequest();
   1771      mScriptLoader->MaybeMoveToLoadedList(request);
   1772    }
   1773  }
   1774  return mScriptLoader->ProcessPendingRequests(aCx);
   1775 }
   1776 
   1777 bool ScriptExecutorRunnable::WorkerRun(JSContext* aCx,
   1778                                       WorkerPrivate* aWorkerPrivate) {
   1779  aWorkerPrivate->AssertIsOnWorkerThread();
   1780 
   1781  // We must be on the same worker as we started on.
   1782  MOZ_ASSERT(
   1783      mScriptLoader->mSyncLoopTarget == mSyncLoopTarget,
   1784      "Unexpected SyncLoopTarget. Check if the sync loop was closed early");
   1785 
   1786  if (mLoadedRequests.begin()->get()->GetRequest()->IsModuleRequest()) {
   1787    return ProcessModuleScript(aCx, aWorkerPrivate);
   1788  }
   1789 
   1790  return ProcessClassicScripts(aCx, aWorkerPrivate);
   1791 }
   1792 
   1793 nsresult ScriptExecutorRunnable::Cancel() {
   1794  if (mScriptLoader->AllScriptsExecuted() &&
   1795      mScriptLoader->AllModuleRequestsLoaded()) {
   1796    mScriptLoader->ShutdownScriptLoader(false, false);
   1797  }
   1798  return NS_OK;
   1799 }
   1800 
   1801 } /* namespace loader */
   1802 
   1803 nsresult ChannelFromScriptURLMainThread(
   1804    nsIPrincipal* aPrincipal, Document* aParentDoc, nsILoadGroup* aLoadGroup,
   1805    nsIURI* aScriptURL, const WorkerType& aWorkerType,
   1806    const RequestCredentials& aCredentials,
   1807    const Maybe<ClientInfo>& aClientInfo,
   1808    nsContentPolicyType aMainScriptContentPolicyType,
   1809    nsICookieJarSettings* aCookieJarSettings, nsIReferrerInfo* aReferrerInfo,
   1810    nsIChannel** aChannel) {
   1811  AssertIsOnMainThread();
   1812 
   1813  nsCOMPtr<nsIIOService> ios(do_GetIOService());
   1814 
   1815  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   1816  NS_ASSERTION(secMan, "This should never be null!");
   1817 
   1818  uint32_t secFlags;
   1819  nsresult rv;
   1820  if (aWorkerType == WorkerType::Module) {
   1821    rv = GetModuleSecFlags(true, aPrincipal, WorkerScript, aScriptURL,
   1822                           aCredentials, secFlags);
   1823  } else {
   1824    rv = GetClassicSecFlags(true, aScriptURL, aPrincipal, WorkerScript,
   1825                            secFlags);
   1826  }
   1827  if (NS_FAILED(rv)) {
   1828    return rv;
   1829  }
   1830 
   1831  return ChannelFromScriptURL(
   1832      aPrincipal, aParentDoc, nullptr, aLoadGroup, ios, secMan, aScriptURL,
   1833      aClientInfo, Maybe<ServiceWorkerDescriptor>(), true, WorkerScript,
   1834      aMainScriptContentPolicyType, nsIRequest::LOAD_NORMAL, secFlags,
   1835      aCookieJarSettings, aReferrerInfo, aChannel);
   1836 }
   1837 
   1838 nsresult ChannelFromScriptURLWorkerThread(
   1839    JSContext* aCx, WorkerPrivate* aParent, const nsAString& aScriptURL,
   1840    const WorkerType& aWorkerType, const RequestCredentials& aCredentials,
   1841    WorkerLoadInfo& aLoadInfo) {
   1842  aParent->AssertIsOnWorkerThread();
   1843 
   1844  RefPtr<ChannelGetterRunnable> getter = new ChannelGetterRunnable(
   1845      aParent, aScriptURL, aWorkerType, aCredentials, aLoadInfo);
   1846 
   1847  ErrorResult rv;
   1848  getter->Dispatch(aParent, Canceling, rv);
   1849  if (rv.Failed()) {
   1850    NS_ERROR("Failed to dispatch!");
   1851    return rv.StealNSResult();
   1852  }
   1853 
   1854  return getter->GetResult();
   1855 }
   1856 
   1857 void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
   1858                     const nsAString& aScriptURL) {
   1859  MOZ_ASSERT(!aRv.Failed());
   1860 
   1861  nsPrintfCString err("Failed to load worker script at \"%s\"",
   1862                      NS_ConvertUTF16toUTF8(aScriptURL).get());
   1863 
   1864  switch (aLoadResult) {
   1865    case NS_ERROR_MALFORMED_URI:
   1866    case NS_ERROR_DOM_SYNTAX_ERR:
   1867      aRv.ThrowSyntaxError(err);
   1868      break;
   1869 
   1870    case NS_BINDING_ABORTED:
   1871      // Note: we used to pretend like we didn't set an exception for
   1872      // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway.  The
   1873      // other callsite, in WorkerPrivate::Constructor, never passed in
   1874      // NS_BINDING_ABORTED.  So just throw it directly here.  Consumers will
   1875      // deal as needed.  But note that we do NOT want to use one of the
   1876      // Throw*Error() methods on ErrorResult for this case, because that will
   1877      // make it impossible for consumers to realize that our error was
   1878      // NS_BINDING_ABORTED.
   1879      aRv.Throw(aLoadResult);
   1880      break;
   1881 
   1882    case NS_ERROR_DOM_BAD_URI:
   1883      // This is actually a security error.
   1884    case NS_ERROR_DOM_SECURITY_ERR:
   1885      aRv.ThrowSecurityError(err);
   1886      break;
   1887 
   1888    case NS_ERROR_FILE_NOT_FOUND:
   1889    case NS_ERROR_NOT_AVAILABLE:
   1890    case NS_ERROR_CORRUPTED_CONTENT:
   1891    case NS_ERROR_DOM_NETWORK_ERR:
   1892    // For lack of anything better, go ahead and throw a NetworkError here.
   1893    // We don't want to throw a JS exception, because for toplevel script
   1894    // loads that would get squelched.
   1895    default:
   1896      aRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
   1897      break;
   1898  }
   1899 }
   1900 
   1901 void LoadMainScript(WorkerPrivate* aWorkerPrivate,
   1902                    UniquePtr<SerializedStackHolder> aOriginStack,
   1903                    const nsAString& aScriptURL,
   1904                    WorkerScriptType aWorkerScriptType, ErrorResult& aRv,
   1905                    const mozilla::Encoding* aDocumentEncoding) {
   1906  nsTArray<nsString> scriptURLs;
   1907 
   1908  scriptURLs.AppendElement(aScriptURL);
   1909 
   1910  LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), scriptURLs, true,
   1911                 aWorkerScriptType, aRv, aDocumentEncoding);
   1912 }
   1913 
   1914 void Load(WorkerPrivate* aWorkerPrivate,
   1915          UniquePtr<SerializedStackHolder> aOriginStack,
   1916          const nsTArray<nsString>& aScriptURLs,
   1917          WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
   1918  const uint32_t urlCount = aScriptURLs.Length();
   1919 
   1920  if (!urlCount) {
   1921    return;
   1922  }
   1923 
   1924  if (urlCount > MAX_CONCURRENT_SCRIPTS) {
   1925    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1926    return;
   1927  }
   1928 
   1929  LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), aScriptURLs, false,
   1930                 aWorkerScriptType, aRv);
   1931 }
   1932 
   1933 }  // namespace mozilla::dom::workerinternals