tor-browser

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

WorkerPrivate.cpp (225608B)


      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 "WorkerPrivate.h"
      8 
      9 #include <utility>
     10 
     11 #include "MessageEventRunnable.h"
     12 #include "RuntimeService.h"
     13 #include "ScriptLoader.h"
     14 #include "WorkerCSPEventListener.h"
     15 #include "WorkerDebugger.h"
     16 #include "WorkerDebuggerManager.h"
     17 #include "WorkerError.h"
     18 #include "WorkerEventTarget.h"
     19 #include "WorkerNavigator.h"
     20 #include "WorkerRef.h"
     21 #include "WorkerRunnable.h"
     22 #include "WorkerThread.h"
     23 #include "js/CallAndConstruct.h"  // JS_CallFunctionValue
     24 #include "js/CompilationAndEvaluation.h"
     25 #include "js/ContextOptions.h"
     26 #include "js/Exception.h"
     27 #include "js/LocaleSensitive.h"
     28 #include "js/MemoryMetrics.h"
     29 #include "js/SourceText.h"
     30 #include "js/friend/ErrorMessages.h"  // JSMSG_OUT_OF_MEMORY
     31 #include "js/friend/MicroTask.h"
     32 #include "mozilla/AntiTrackingUtils.h"
     33 #include "mozilla/BasePrincipal.h"
     34 #include "mozilla/CycleCollectedJSContext.h"
     35 #include "mozilla/ExtensionPolicyService.h"
     36 #include "mozilla/Mutex.h"
     37 #include "mozilla/ProfilerLabels.h"
     38 #include "mozilla/ProfilerMarkers.h"
     39 #include "mozilla/Result.h"
     40 #include "mozilla/ScopeExit.h"
     41 #include "mozilla/StaticPrefs_browser.h"
     42 #include "mozilla/StaticPrefs_dom.h"
     43 #include "mozilla/StaticPrefs_javascript.h"
     44 #include "mozilla/StorageAccess.h"
     45 #include "mozilla/StoragePrincipalHelper.h"
     46 #include "mozilla/ThreadEventQueue.h"
     47 #include "mozilla/ThreadSafety.h"
     48 #include "mozilla/ThrottledEventQueue.h"
     49 #include "mozilla/dom/BrowsingContextGroup.h"
     50 #include "mozilla/dom/CallbackDebuggerNotification.h"
     51 #include "mozilla/dom/ClientManager.h"
     52 #include "mozilla/dom/ClientState.h"
     53 #include "mozilla/dom/Console.h"
     54 #include "mozilla/dom/ContentChild.h"
     55 #include "mozilla/dom/DOMTypes.h"
     56 #include "mozilla/dom/DocGroup.h"
     57 #include "mozilla/dom/Document.h"
     58 #include "mozilla/dom/DocumentInlines.h"
     59 #include "mozilla/dom/Event.h"
     60 #include "mozilla/dom/Exceptions.h"
     61 #include "mozilla/dom/FunctionBinding.h"
     62 #include "mozilla/dom/IndexedDatabaseManager.h"
     63 #include "mozilla/dom/JSExecutionManager.h"
     64 #include "mozilla/dom/MessageEvent.h"
     65 #include "mozilla/dom/MessageEventBinding.h"
     66 #include "mozilla/dom/MessagePort.h"
     67 #include "mozilla/dom/MessagePortBinding.h"
     68 #include "mozilla/dom/PRemoteWorkerDebuggerParent.h"
     69 #include "mozilla/dom/Performance.h"
     70 #include "mozilla/dom/PerformanceStorageWorker.h"
     71 #include "mozilla/dom/PolicyContainer.h"
     72 #include "mozilla/dom/PromiseDebugging.h"
     73 #include "mozilla/dom/ReferrerInfo.h"
     74 #include "mozilla/dom/RemoteWorkerChild.h"
     75 #include "mozilla/dom/RemoteWorkerDebuggerChild.h"
     76 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h"
     77 #include "mozilla/dom/RemoteWorkerService.h"
     78 #include "mozilla/dom/RootedDictionary.h"
     79 #include "mozilla/dom/ServiceWorkerEvents.h"
     80 #include "mozilla/dom/ServiceWorkerManager.h"
     81 #include "mozilla/dom/SimpleGlobalObject.h"
     82 #include "mozilla/dom/TimeoutHandler.h"
     83 #include "mozilla/dom/TimeoutManager.h"
     84 #include "mozilla/dom/UseCounterMetrics.h"
     85 #include "mozilla/dom/WebTaskScheduler.h"
     86 #include "mozilla/dom/WindowContext.h"
     87 #include "mozilla/dom/WorkerBinding.h"
     88 #include "mozilla/dom/WorkerScope.h"
     89 #include "mozilla/dom/WorkerStatus.h"
     90 #include "mozilla/dom/nsCSPContext.h"
     91 #include "mozilla/dom/nsCSPUtils.h"
     92 #include "mozilla/extensions/ExtensionBrowser.h"  // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable
     93 #include "mozilla/extensions/WebExtensionPolicy.h"
     94 #include "mozilla/glean/DomUseCounterMetrics.h"
     95 #include "mozilla/ipc/BackgroundChild.h"
     96 #include "mozilla/ipc/PBackgroundChild.h"
     97 #include "mozilla/net/CookieJarSettings.h"
     98 #include "nsContentSecurityManager.h"
     99 #include "nsCycleCollector.h"
    100 #include "nsGlobalWindowInner.h"
    101 #include "nsIDUtils.h"
    102 #include "nsIEventTarget.h"
    103 #include "nsIFile.h"
    104 #include "nsIMemoryReporter.h"
    105 #include "nsIPermissionManager.h"
    106 #include "nsIProtocolHandler.h"
    107 #include "nsIScriptError.h"
    108 #include "nsIURI.h"
    109 #include "nsIURL.h"
    110 #include "nsIUUIDGenerator.h"
    111 #include "nsNetUtil.h"
    112 #include "nsPresContext.h"
    113 #include "nsPrintfCString.h"
    114 #include "nsProxyRelease.h"
    115 #include "nsQueryObject.h"
    116 #include "nsRFPService.h"
    117 #include "nsSandboxFlags.h"
    118 #include "nsThreadManager.h"
    119 #include "nsThreadUtils.h"
    120 #include "nsUTF8Utils.h"
    121 
    122 #ifdef XP_WIN
    123 #  undef PostMessage
    124 #endif
    125 
    126 // JS_MaybeGC will run once every second during normal execution.
    127 #define PERIODIC_GC_TIMER_DELAY_SEC 1
    128 
    129 // A shrinking GC will run five seconds after the last event is processed.
    130 #define IDLE_GC_TIMER_DELAY_SEC 5
    131 
    132 // Arbitrary short grace period for the currently running task to finish.
    133 // There isn't an advantage for us to immediately interrupt JS in the middle of
    134 // execution that might yield soon, especially when there is so much async
    135 // variability in the data flow prior to us deciding to trigger the interrupt.
    136 #define DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS 250
    137 
    138 static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
    139 static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
    140 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
    141 
    142 mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; }
    143 
    144 mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; }
    145 
    146 #ifdef LOG
    147 #  undef LOG
    148 #endif
    149 #ifdef LOGV
    150 #  undef LOGV
    151 #endif
    152 #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
    153 #define LOGV(args) MOZ_LOG(sWorkerPrivateLog, LogLevel::Verbose, args);
    154 
    155 namespace mozilla {
    156 
    157 using namespace ipc;
    158 
    159 namespace dom {
    160 
    161 using namespace workerinternals;
    162 
    163 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
    164 
    165 namespace {
    166 
    167 #ifdef DEBUG
    168 
    169 const nsIID kDEBUGWorkerEventTargetIID = {
    170    0xccaba3fa,
    171    0x5be2,
    172    0x4de2,
    173    {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};
    174 
    175 #endif
    176 
    177 template <class T>
    178 class UniquePtrComparator {
    179  using A = UniquePtr<T>;
    180  using B = T*;
    181 
    182 public:
    183  bool Equals(const A& a, const A& b) const {
    184    return (a && b) ? (*a == *b) : (!a && !b);
    185  }
    186  bool LessThan(const A& a, const A& b) const {
    187    return (a && b) ? (*a < *b) : !!b;
    188  }
    189 };
    190 
    191 template <class T>
    192 inline UniquePtrComparator<T> GetUniquePtrComparator(
    193    const nsTArray<UniquePtr<T>>&) {
    194  return UniquePtrComparator<T>();
    195 }
    196 
    197 // This class is used to wrap any runnables that the worker receives via the
    198 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
    199 // from the worker's EventTarget).
    200 class ExternalRunnableWrapper final : public WorkerThreadRunnable {
    201  nsCOMPtr<nsIRunnable> mWrappedRunnable;
    202 
    203 public:
    204  ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
    205                          nsIRunnable* aWrappedRunnable)
    206      : WorkerThreadRunnable("ExternalRunnableWrapper"),
    207        mWrappedRunnable(aWrappedRunnable) {
    208    MOZ_ASSERT(aWorkerPrivate);
    209    MOZ_ASSERT(aWrappedRunnable);
    210  }
    211 
    212  NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper,
    213                                       WorkerThreadRunnable)
    214 
    215 private:
    216  ~ExternalRunnableWrapper() = default;
    217 
    218  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    219    // Silence bad assertions.
    220    return true;
    221  }
    222 
    223  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    224                            bool aDispatchResult) override {
    225    // Silence bad assertions.
    226  }
    227 
    228  virtual bool WorkerRun(JSContext* aCx,
    229                         WorkerPrivate* aWorkerPrivate) override {
    230    nsresult rv = mWrappedRunnable->Run();
    231    mWrappedRunnable = nullptr;
    232    if (NS_FAILED(rv)) {
    233      if (!JS_IsExceptionPending(aCx)) {
    234        Throw(aCx, rv);
    235      }
    236      return false;
    237    }
    238    return true;
    239  }
    240 
    241  nsresult Cancel() override {
    242    nsCOMPtr<nsIDiscardableRunnable> doomed =
    243        do_QueryInterface(mWrappedRunnable);
    244    if (doomed) {
    245      doomed->OnDiscard();
    246    }
    247    mWrappedRunnable = nullptr;
    248    return NS_OK;
    249  }
    250 
    251 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
    252  NS_IMETHOD GetName(nsACString& aName) override {
    253    aName.AssignLiteral("ExternalRunnableWrapper(");
    254    if (nsCOMPtr<nsINamed> named = do_QueryInterface(mWrappedRunnable)) {
    255      nsAutoCString containedName;
    256      named->GetName(containedName);
    257      aName.Append(containedName);
    258    } else {
    259      aName.AppendLiteral("?");
    260    }
    261    aName.AppendLiteral(")");
    262    return NS_OK;
    263  }
    264 #endif
    265 };
    266 
    267 struct WindowAction {
    268  nsPIDOMWindowInner* mWindow;
    269  bool mDefaultAction;
    270 
    271  MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
    272      : mWindow(aWindow), mDefaultAction(true) {}
    273 
    274  bool operator==(const WindowAction& aOther) const {
    275    return mWindow == aOther.mWindow;
    276  }
    277 };
    278 
    279 class WorkerFinishedRunnable final : public WorkerControlRunnable {
    280  WorkerPrivate* mFinishedWorker;
    281 
    282 public:
    283  WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
    284                         WorkerPrivate* aFinishedWorker)
    285      : WorkerControlRunnable("WorkerFinishedRunnable"),
    286        mFinishedWorker(aFinishedWorker) {
    287    aFinishedWorker->IncreaseWorkerFinishedRunnableCount();
    288  }
    289 
    290 private:
    291  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    292    // Silence bad assertions.
    293    return true;
    294  }
    295 
    296  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    297                            bool aDispatchResult) override {
    298    // Silence bad assertions.
    299  }
    300 
    301  virtual bool WorkerRun(JSContext* aCx,
    302                         WorkerPrivate* aWorkerPrivate) override {
    303    // This may block on the main thread.
    304    AutoYieldJSThreadExecution yield;
    305 
    306    mFinishedWorker->DecreaseWorkerFinishedRunnableCount();
    307 
    308    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
    309      NS_WARNING("Failed to dispatch, going to leak!");
    310    }
    311 
    312    RuntimeService* runtime = RuntimeService::GetService();
    313    NS_ASSERTION(runtime, "This should never be null!");
    314 
    315    mFinishedWorker->DisableDebugger();
    316 
    317    runtime->UnregisterWorker(*mFinishedWorker);
    318 
    319    mFinishedWorker->ClearSelfAndParentEventTargetRef();
    320    return true;
    321  }
    322 };
    323 
    324 class TopLevelWorkerFinishedRunnable final : public Runnable {
    325  WorkerPrivate* mFinishedWorker;
    326 
    327 public:
    328  explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
    329      : mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
    330        mFinishedWorker(aFinishedWorker) {
    331    aFinishedWorker->AssertIsOnWorkerThread();
    332    aFinishedWorker->IncreaseTopLevelWorkerFinishedRunnableCount();
    333  }
    334 
    335  NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
    336 
    337 private:
    338  ~TopLevelWorkerFinishedRunnable() = default;
    339 
    340  NS_IMETHOD
    341  Run() override {
    342    AssertIsOnMainThread();
    343 
    344    mFinishedWorker->DecreaseTopLevelWorkerFinishedRunnableCount();
    345 
    346    RuntimeService* runtime = RuntimeService::GetService();
    347    MOZ_ASSERT(runtime);
    348 
    349    mFinishedWorker->DisableDebugger();
    350 
    351    runtime->UnregisterWorker(*mFinishedWorker);
    352 
    353    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
    354      NS_WARNING("Failed to dispatch, going to leak!");
    355    }
    356 
    357    mFinishedWorker->ClearSelfAndParentEventTargetRef();
    358    return NS_OK;
    359  }
    360 };
    361 
    362 class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
    363  nsString mScriptURL;
    364  const mozilla::Encoding* mDocumentEncoding;
    365  UniquePtr<SerializedStackHolder> mOriginStack;
    366 
    367 public:
    368  explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
    369                                 UniquePtr<SerializedStackHolder> aOriginStack,
    370                                 const nsAString& aScriptURL,
    371                                 const mozilla::Encoding* aDocumentEncoding)
    372      : WorkerDebuggeeRunnable("CompileScriptRunnable"),
    373        mScriptURL(aScriptURL),
    374        mDocumentEncoding(aDocumentEncoding),
    375        mOriginStack(aOriginStack.release()) {}
    376 
    377 private:
    378  // We can't implement PreRun effectively, because at the point when that would
    379  // run we have not yet done our load so don't know things like our final
    380  // principal and whatnot.
    381 
    382  virtual bool WorkerRun(JSContext* aCx,
    383                         WorkerPrivate* aWorkerPrivate) override {
    384    aWorkerPrivate->AssertIsOnWorkerThread();
    385 
    386    WorkerGlobalScope* globalScope =
    387        aWorkerPrivate->GetOrCreateGlobalScope(aCx);
    388    if (NS_WARN_IF(!globalScope)) {
    389      return false;
    390    }
    391 
    392    if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
    393      return false;
    394    }
    395 
    396    ErrorResult rv;
    397    workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
    398                                    mScriptURL, WorkerScript, rv,
    399                                    mDocumentEncoding);
    400 
    401    if (aWorkerPrivate->ExtensionAPIAllowed()) {
    402      MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    403      RefPtr<Runnable> extWorkerRunnable =
    404          extensions::CreateWorkerLoadedRunnable(
    405              aWorkerPrivate->ServiceWorkerID(), aWorkerPrivate->GetBaseURI());
    406      // Dispatch as a low priority runnable.
    407      if (NS_FAILED(aWorkerPrivate->DispatchToMainThreadForMessaging(
    408              extWorkerRunnable.forget()))) {
    409        NS_WARNING(
    410            "Failed to dispatch runnable to notify extensions worker loaded");
    411      }
    412    }
    413 
    414    rv.WouldReportJSException();
    415    // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
    416    // return false and don't SetWorkerScriptExecutedSuccessfully() in that
    417    // case, but don't throw anything on aCx.  The idea is to not dispatch error
    418    // events if our load is canceled with that error code.
    419    if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
    420      rv.SuppressException();
    421      return false;
    422    }
    423 
    424    // Make sure to propagate exceptions from rv onto aCx, so that they will get
    425    // reported after we return.  We want to propagate just JS exceptions,
    426    // because all the other errors are handled when the script is loaded.
    427    // See: https://dom.spec.whatwg.org/#concept-event-fire
    428    if (rv.Failed() && !rv.IsJSException()) {
    429      WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
    430          aWorkerPrivate);
    431      rv.SuppressException();
    432      return false;
    433    }
    434 
    435    // This is a little dumb, but aCx is in the null realm here because we
    436    // set it up that way in our Run(), since we had not created the global at
    437    // that point yet.  So we need to enter the realm of our global,
    438    // because setting a pending exception on aCx involves wrapping into its
    439    // current compartment.  Luckily we have a global now.
    440    JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
    441    if (rv.MaybeSetPendingException(aCx)) {
    442      // In the event of an uncaught exception, the worker should still keep
    443      // running (return true) but should not be marked as having executed
    444      // successfully (which will cause ServiceWorker installation to fail).
    445      // In previous error handling cases in this method, we return false (to
    446      // trigger CloseInternal) because the global is not in an operable
    447      // state at all.
    448      //
    449      // For ServiceWorkers, this would correspond to the "Run Service Worker"
    450      // algorithm returning an "abrupt completion" and _not_ failure.
    451      //
    452      // For DedicatedWorkers and SharedWorkers, this would correspond to the
    453      // "run a worker" algorithm disregarding the return value of "run the
    454      // classic script"/"run the module script" in step 24:
    455      //
    456      // "If script is a classic script, then run the classic script script.
    457      // Otherwise, it is a module script; run the module script script."
    458      return true;
    459    }
    460 
    461    aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
    462    return true;
    463  }
    464 
    465  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    466               bool aRunResult) override {
    467    if (!aRunResult) {
    468      aWorkerPrivate->CloseInternal();
    469    }
    470    WorkerThreadRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
    471  }
    472 };
    473 
    474 class NotifyRunnable final : public WorkerControlRunnable {
    475  WorkerStatus mStatus;
    476 
    477 public:
    478  NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
    479      : WorkerControlRunnable("NotifyRunnable"), mStatus(aStatus) {
    480    MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
    481               aStatus == Killing);
    482  }
    483 
    484 private:
    485  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    486    aWorkerPrivate->AssertIsOnParentThread();
    487    return true;
    488  }
    489 
    490  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    491                            bool aDispatchResult) override {
    492    aWorkerPrivate->AssertIsOnParentThread();
    493  }
    494 
    495  virtual bool WorkerRun(JSContext* aCx,
    496                         WorkerPrivate* aWorkerPrivate) override {
    497    return aWorkerPrivate->NotifyInternal(mStatus);
    498  }
    499 
    500  virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    501                       bool aRunResult) override {}
    502 };
    503 
    504 class FreezeRunnable final : public WorkerControlRunnable {
    505 public:
    506  explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
    507      : WorkerControlRunnable("FreezeRunnable") {}
    508 
    509 private:
    510  virtual bool WorkerRun(JSContext* aCx,
    511                         WorkerPrivate* aWorkerPrivate) override {
    512    return aWorkerPrivate->FreezeInternal();
    513  }
    514 };
    515 
    516 class ThawRunnable final : public WorkerControlRunnable {
    517 public:
    518  explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
    519      : WorkerControlRunnable("ThawRunnable") {}
    520 
    521 private:
    522  virtual bool WorkerRun(JSContext* aCx,
    523                         WorkerPrivate* aWorkerPrivate) override {
    524    return aWorkerPrivate->ThawInternal();
    525  }
    526 };
    527 
    528 class ChangeBackgroundStateRunnable final : public WorkerControlRunnable {
    529 public:
    530  ChangeBackgroundStateRunnable() = delete;
    531  explicit ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate) =
    532      delete;
    533  ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate,
    534                                bool aIsBackground)
    535      : WorkerControlRunnable("ChangeBackgroundStateRunnable"),
    536        mIsBackground(aIsBackground) {}
    537 
    538 private:
    539  bool mIsBackground = false;
    540  virtual bool WorkerRun(JSContext* aCx,
    541                         WorkerPrivate* aWorkerPrivate) override {
    542    return aWorkerPrivate->ChangeBackgroundStateInternal(mIsBackground);
    543  }
    544 };
    545 
    546 class ChangePlaybackStateRunnable final : public WorkerControlRunnable {
    547 public:
    548  ChangePlaybackStateRunnable() = delete;
    549  explicit ChangePlaybackStateRunnable(WorkerPrivate* aWorkerPrivate) = delete;
    550  ChangePlaybackStateRunnable(WorkerPrivate* aWorkerPrivate,
    551                              bool aIsPlayingAudio)
    552      : WorkerControlRunnable("ChangePlaybackStateRunnable"),
    553        mIsPlayingAudio(aIsPlayingAudio) {}
    554 
    555 private:
    556  virtual bool WorkerRun(JSContext* aCx,
    557                         WorkerPrivate* aWorkerPrivate) override {
    558    return aWorkerPrivate->ChangePlaybackStateInternal(mIsPlayingAudio);
    559  }
    560  bool mIsPlayingAudio = false;
    561 };
    562 
    563 class ChangeActivePeerConnectionsRunnable final : public WorkerControlRunnable {
    564 public:
    565  ChangeActivePeerConnectionsRunnable() = delete;
    566  explicit ChangeActivePeerConnectionsRunnable(WorkerPrivate* aWorkerPrivate) =
    567      delete;
    568  ChangeActivePeerConnectionsRunnable(WorkerPrivate* aWorkerPrivate,
    569                                      bool aHasPeerConnections)
    570      : WorkerControlRunnable("ChangeActivePeerConnectionsRunnable"),
    571        mConnections(aHasPeerConnections) {}
    572 
    573 private:
    574  virtual bool WorkerRun(JSContext* aCx,
    575                         WorkerPrivate* aWorkerPrivate) override {
    576    return aWorkerPrivate->ChangePeerConnectionsInternal(mConnections);
    577  }
    578  bool mConnections = false;
    579 };
    580 
    581 class PropagateStorageAccessPermissionGrantedRunnable final
    582    : public WorkerControlRunnable {
    583 public:
    584  explicit PropagateStorageAccessPermissionGrantedRunnable(
    585      WorkerPrivate* aWorkerPrivate)
    586      : WorkerControlRunnable(
    587            "PropagateStorageAccessPermissionGrantedRunnable") {}
    588 
    589 private:
    590  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    591    aWorkerPrivate->PropagateStorageAccessPermissionGrantedInternal();
    592    return true;
    593  }
    594 };
    595 
    596 class ReportErrorToConsoleRunnable final : public WorkerParentThreadRunnable {
    597 public:
    598  // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
    599  static void Report(WorkerPrivate* aWorkerPrivate, uint32_t aErrorFlags,
    600                     const nsCString& aCategory,
    601                     nsContentUtils::PropertiesFile aFile,
    602                     const nsCString& aMessageName,
    603                     const nsTArray<nsString>& aParams,
    604                     const mozilla::SourceLocation& aLocation) {
    605    if (aWorkerPrivate) {
    606      aWorkerPrivate->AssertIsOnWorkerThread();
    607    } else {
    608      AssertIsOnMainThread();
    609    }
    610 
    611    // Now fire a runnable to do the same on the parent's thread if we can.
    612    if (aWorkerPrivate) {
    613      RefPtr<ReportErrorToConsoleRunnable> runnable =
    614          new ReportErrorToConsoleRunnable(aWorkerPrivate, aErrorFlags,
    615                                           aCategory, aFile, aMessageName,
    616                                           aParams, aLocation);
    617      runnable->Dispatch(aWorkerPrivate);
    618      return;
    619    }
    620 
    621    // Log a warning to the console.
    622    nsContentUtils::ReportToConsole(aErrorFlags, aCategory, nullptr, aFile,
    623                                    aMessageName.get(), aParams, aLocation);
    624  }
    625 
    626 private:
    627  ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
    628                               uint32_t aErrorFlags, const nsCString& aCategory,
    629                               nsContentUtils::PropertiesFile aFile,
    630                               const nsCString& aMessageName,
    631                               const nsTArray<nsString>& aParams,
    632                               const mozilla::SourceLocation& aLocation)
    633      : WorkerParentThreadRunnable("ReportErrorToConsoleRunnable"),
    634        mErrorFlags(aErrorFlags),
    635        mCategory(aCategory),
    636        mFile(aFile),
    637        mMessageName(aMessageName),
    638        mParams(aParams.Clone()),
    639        mLocation(aLocation) {}
    640 
    641  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    642                            bool aDispatchResult) override {
    643    aWorkerPrivate->AssertIsOnWorkerThread();
    644 
    645    // Dispatch may fail if the worker was canceled, no need to report that as
    646    // an error, so don't call base class PostDispatch.
    647  }
    648 
    649  virtual bool WorkerRun(JSContext* aCx,
    650                         WorkerPrivate* aWorkerPrivate) override {
    651    WorkerPrivate* parent = aWorkerPrivate->GetParent();
    652    MOZ_ASSERT_IF(!parent, NS_IsMainThread());
    653    Report(parent, mErrorFlags, mCategory, mFile, mMessageName, mParams,
    654           mLocation);
    655    return true;
    656  }
    657 
    658  const uint32_t mErrorFlags;
    659  const nsCString mCategory;
    660  const nsContentUtils::PropertiesFile mFile;
    661  const nsCString mMessageName;
    662  const nsTArray<nsString> mParams;
    663  const mozilla::SourceLocation mLocation;
    664 };
    665 
    666 class DebuggerImmediateRunnable final : public WorkerThreadRunnable {
    667  RefPtr<dom::Function> mHandler;
    668 
    669 public:
    670  explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
    671                                     dom::Function& aHandler)
    672      : WorkerThreadRunnable("DebuggerImmediateRunnable"),
    673        mHandler(&aHandler) {}
    674 
    675 private:
    676  virtual bool IsDebuggerRunnable() const override { return true; }
    677 
    678  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    679    // Silence bad assertions.
    680    return true;
    681  }
    682 
    683  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    684                            bool aDispatchResult) override {
    685    // Silence bad assertions.
    686  }
    687 
    688  // Make as MOZ_CAN_RUN_SCRIPT_BOUNDARY for calling mHandler->Call();
    689  // Since WorkerRunnable::WorkerRun has not to be MOZ_CAN_RUN_SCRIPT, but
    690  // DebuggerImmediateRunnable is a special case that must to call the function
    691  // defined in the debugger script.
    692  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    693  virtual bool WorkerRun(JSContext* aCx,
    694                         WorkerPrivate* aWorkerPrivate) override {
    695    JS::Rooted<JS::Value> rval(aCx);
    696    IgnoredErrorResult rv;
    697    MOZ_KnownLive(mHandler)->Call({}, &rval, rv);
    698 
    699    return !rv.Failed();
    700  }
    701 };
    702 
    703 // GetJSContext() is safe on the worker thread
    704 void PeriodicGCTimerCallback(nsITimer* aTimer,
    705                             void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS {
    706  auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure);
    707  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
    708  workerPrivate->AssertIsOnWorkerThread();
    709  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
    710                                        false /* shrinking */,
    711                                        false /* collect children */);
    712  LOG(WorkerLog(), ("Worker %p run periodic GC\n", workerPrivate));
    713 }
    714 
    715 void IdleGCTimerCallback(nsITimer* aTimer,
    716                         void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS {
    717  auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure);
    718  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
    719  workerPrivate->AssertIsOnWorkerThread();
    720  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
    721                                        true /* shrinking */,
    722                                        false /* collect children */);
    723  LOG(WorkerLog(), ("Worker %p run idle GC\n", workerPrivate));
    724 
    725  // After running idle GC we can cancel the current timers.
    726  workerPrivate->CancelGCTimers();
    727 }
    728 
    729 class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
    730  JS::ContextOptions mContextOptions;
    731 
    732 public:
    733  UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
    734                               const JS::ContextOptions& aContextOptions)
    735      : WorkerControlRunnable("UpdateContextOptionsRunnable"),
    736        mContextOptions(aContextOptions) {}
    737 
    738 private:
    739  virtual bool WorkerRun(JSContext* aCx,
    740                         WorkerPrivate* aWorkerPrivate) override {
    741    aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
    742    return true;
    743  }
    744 };
    745 
    746 class UpdateLanguagesRunnable final : public WorkerThreadRunnable {
    747  nsTArray<nsString> mLanguages;
    748 
    749 public:
    750  UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
    751                          const nsTArray<nsString>& aLanguages)
    752      : WorkerThreadRunnable("UpdateLanguagesRunnable"),
    753        mLanguages(aLanguages.Clone()) {}
    754 
    755  virtual bool WorkerRun(JSContext* aCx,
    756                         WorkerPrivate* aWorkerPrivate) override {
    757    aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
    758    return true;
    759  }
    760 };
    761 
    762 class UpdateJSWorkerMemoryParameterRunnable final
    763    : public WorkerControlRunnable {
    764  Maybe<uint32_t> mValue;
    765  JSGCParamKey mKey;
    766 
    767 public:
    768  UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
    769                                        JSGCParamKey aKey,
    770                                        Maybe<uint32_t> aValue)
    771      : WorkerControlRunnable("UpdateJSWorkerMemoryParameterRunnable"),
    772        mValue(aValue),
    773        mKey(aKey) {}
    774 
    775 private:
    776  virtual bool WorkerRun(JSContext* aCx,
    777                         WorkerPrivate* aWorkerPrivate) override {
    778    aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
    779    return true;
    780  }
    781 };
    782 
    783 #ifdef JS_GC_ZEAL
    784 class UpdateGCZealRunnable final : public WorkerControlRunnable {
    785  uint8_t mGCZeal;
    786  uint32_t mFrequency;
    787 
    788 public:
    789  UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
    790                       uint32_t aFrequency)
    791      : WorkerControlRunnable("UpdateGCZealRunnable"),
    792        mGCZeal(aGCZeal),
    793        mFrequency(aFrequency) {}
    794 
    795 private:
    796  virtual bool WorkerRun(JSContext* aCx,
    797                         WorkerPrivate* aWorkerPrivate) override {
    798    aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
    799    return true;
    800  }
    801 };
    802 #endif
    803 
    804 class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
    805  bool mState;
    806 
    807 public:
    808  SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
    809      : WorkerControlRunnable("SetLowMemoryStateRunnable"), mState(aState) {}
    810 
    811 private:
    812  virtual bool WorkerRun(JSContext* aCx,
    813                         WorkerPrivate* aWorkerPrivate) override {
    814    aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState);
    815    return true;
    816  }
    817 };
    818 
    819 class GarbageCollectRunnable final : public WorkerControlRunnable {
    820  bool mShrinking;
    821  bool mCollectChildren;
    822 
    823 public:
    824  GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
    825                         bool aCollectChildren)
    826      : WorkerControlRunnable("GarbageCollectRunnable"),
    827        mShrinking(aShrinking),
    828        mCollectChildren(aCollectChildren) {}
    829 
    830 private:
    831  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    832    // Silence bad assertions, this can be dispatched from either the main
    833    // thread or the timer thread..
    834    return true;
    835  }
    836 
    837  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    838                            bool aDispatchResult) override {
    839    // Silence bad assertions, this can be dispatched from either the main
    840    // thread or the timer thread..
    841  }
    842 
    843  virtual bool WorkerRun(JSContext* aCx,
    844                         WorkerPrivate* aWorkerPrivate) override {
    845    aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
    846    if (mShrinking) {
    847      // Either we've run the idle GC or explicit GC call from the parent,
    848      // we can cancel the current timers.
    849      aWorkerPrivate->CancelGCTimers();
    850    }
    851    return true;
    852  }
    853 };
    854 
    855 class CycleCollectRunnable final : public WorkerControlRunnable {
    856  bool mCollectChildren;
    857 
    858 public:
    859  CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
    860      : WorkerControlRunnable("CycleCollectRunnable"),
    861        mCollectChildren(aCollectChildren) {}
    862 
    863  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    864    aWorkerPrivate->CycleCollectInternal(mCollectChildren);
    865    return true;
    866  }
    867 };
    868 
    869 class OfflineStatusChangeRunnable final : public WorkerThreadRunnable {
    870 public:
    871  OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
    872      : WorkerThreadRunnable("OfflineStatusChangeRunnable"),
    873        mIsOffline(aIsOffline) {}
    874 
    875  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    876    aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
    877    return true;
    878  }
    879 
    880 private:
    881  bool mIsOffline;
    882 };
    883 
    884 class MemoryPressureRunnable final : public WorkerControlRunnable {
    885 public:
    886  explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
    887      : WorkerControlRunnable("MemoryPressureRunnable") {}
    888 
    889  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    890    aWorkerPrivate->MemoryPressureInternal();
    891    return true;
    892  }
    893 };
    894 
    895 class DisableRemoteDebuggerRunnable final : public WorkerControlRunnable {
    896 public:
    897  explicit DisableRemoteDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
    898      : WorkerControlRunnable("DisableRemoteDebuggerRunnable") {}
    899 
    900  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    901    aWorkerPrivate->DisableRemoteDebuggerOnWorkerThread();
    902    return true;
    903  }
    904 };
    905 
    906 #ifdef DEBUG
    907 static bool StartsWithExplicit(nsACString& s) {
    908  return StringBeginsWith(s, "explicit/"_ns);
    909 }
    910 #endif
    911 
    912 PRThread* PRThreadFromThread(nsIThread* aThread) {
    913  MOZ_ASSERT(aThread);
    914 
    915  PRThread* result;
    916  MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
    917  MOZ_ASSERT(result);
    918 
    919  return result;
    920 }
    921 
    922 // A runnable to cancel the worker from the parent thread when self.close() is
    923 // called. This runnable is executed on the parent process in order to cancel
    924 // the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be
    925 // sure that all the pending WorkerDebuggeeRunnables are executed before this.
    926 class CancelingOnParentRunnable final : public WorkerParentDebuggeeRunnable {
    927 public:
    928  explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
    929      : WorkerParentDebuggeeRunnable("CancelingOnParentRunnable") {}
    930 
    931  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    932    aWorkerPrivate->Cancel();
    933    return true;
    934  }
    935 };
    936 
    937 // A runnable to cancel the worker from the parent process.
    938 class CancelingWithTimeoutOnParentRunnable final
    939    : public WorkerParentControlRunnable {
    940 public:
    941  explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
    942      : WorkerParentControlRunnable("CancelingWithTimeoutOnParentRunnable") {}
    943 
    944  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    945    aWorkerPrivate->AssertIsOnParentThread();
    946    aWorkerPrivate->StartCancelingTimer();
    947    return true;
    948  }
    949 };
    950 
    951 class CancelingTimerCallback final : public nsITimerCallback {
    952 public:
    953  NS_DECL_ISUPPORTS
    954 
    955  explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
    956      : mWorkerPrivate(aWorkerPrivate) {}
    957 
    958  NS_IMETHOD
    959  Notify(nsITimer* aTimer) override {
    960    mWorkerPrivate->AssertIsOnParentThread();
    961    mWorkerPrivate->Cancel();
    962    return NS_OK;
    963  }
    964 
    965 private:
    966  ~CancelingTimerCallback() = default;
    967 
    968  // Raw pointer here is OK because the timer is canceled during the shutdown
    969  // steps.
    970  WorkerPrivate* mWorkerPrivate;
    971 };
    972 
    973 NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
    974 
    975 // This runnable starts the canceling of a worker after a self.close().
    976 class CancelingRunnable final : public Runnable {
    977 public:
    978  CancelingRunnable() : Runnable("CancelingRunnable") {}
    979 
    980  NS_IMETHOD
    981  Run() override {
    982    LOG(WorkerLog(), ("CancelingRunnable::Run [%p]", this));
    983    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    984    MOZ_ASSERT(workerPrivate);
    985    workerPrivate->AssertIsOnWorkerThread();
    986 
    987    // Now we can cancel the this worker from the parent process.
    988    RefPtr<CancelingOnParentRunnable> r =
    989        new CancelingOnParentRunnable(workerPrivate);
    990    r->Dispatch(workerPrivate);
    991 
    992    return NS_OK;
    993  }
    994 };
    995 
    996 } /* anonymous namespace */
    997 
    998 nsString ComputeWorkerPrivateId() {
    999  nsID uuid = nsID::GenerateUUID();
   1000  return NSID_TrimBracketsUTF16(uuid);
   1001 }
   1002 
   1003 class WorkerPrivate::EventTarget final : public nsISerialEventTarget {
   1004  // This mutex protects mWorkerPrivate and must be acquired *before* the
   1005  // WorkerPrivate's mutex whenever they must both be held.
   1006  mozilla::Mutex mMutex;
   1007  WorkerPrivate* mWorkerPrivate MOZ_GUARDED_BY(mMutex);
   1008  nsCOMPtr<nsIEventTarget> mNestedEventTarget MOZ_GUARDED_BY(mMutex);
   1009  bool mDisabled MOZ_GUARDED_BY(mMutex);
   1010  bool mShutdown MOZ_GUARDED_BY(mMutex);
   1011 
   1012 public:
   1013  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
   1014      : mMutex("WorkerPrivate::EventTarget::mMutex"),
   1015        mWorkerPrivate(aWorkerPrivate),
   1016        mNestedEventTarget(aNestedEventTarget),
   1017        mDisabled(false),
   1018        mShutdown(false) {
   1019    MOZ_ASSERT(aWorkerPrivate);
   1020    MOZ_ASSERT(aNestedEventTarget);
   1021  }
   1022 
   1023  void Disable() {
   1024    {
   1025      MutexAutoLock lock(mMutex);
   1026 
   1027      // Note, Disable() can be called more than once safely.
   1028      mDisabled = true;
   1029    }
   1030  }
   1031 
   1032  void Shutdown() {
   1033    nsCOMPtr<nsIEventTarget> nestedEventTarget;
   1034    {
   1035      MutexAutoLock lock(mMutex);
   1036 
   1037      mWorkerPrivate = nullptr;
   1038      mNestedEventTarget.swap(nestedEventTarget);
   1039      MOZ_ASSERT(mDisabled);
   1040      mShutdown = true;
   1041    }
   1042  }
   1043 
   1044  RefPtr<nsIEventTarget> GetNestedEventTarget() {
   1045    RefPtr<nsIEventTarget> nestedEventTarget = nullptr;
   1046    {
   1047      MutexAutoLock lock(mMutex);
   1048      if (mWorkerPrivate) {
   1049        mWorkerPrivate->AssertIsOnWorkerThread();
   1050        nestedEventTarget = mNestedEventTarget.get();
   1051      }
   1052    }
   1053    return nestedEventTarget;
   1054  }
   1055 
   1056  NS_DECL_THREADSAFE_ISUPPORTS
   1057  NS_DECL_NSIEVENTTARGET_FULL
   1058 
   1059 private:
   1060  ~EventTarget() = default;
   1061 };
   1062 
   1063 class WorkerJSContextStats final : public JS::RuntimeStats {
   1064  const nsCString mRtPath;
   1065 
   1066 public:
   1067  explicit WorkerJSContextStats(const nsACString& aRtPath)
   1068      : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {}
   1069 
   1070  ~WorkerJSContextStats() {
   1071    for (JS::ZoneStats& stats : zoneStatsVector) {
   1072      delete static_cast<xpc::ZoneStatsExtras*>(stats.extra);
   1073    }
   1074 
   1075    for (JS::RealmStats& stats : realmStatsVector) {
   1076      delete static_cast<xpc::RealmStatsExtras*>(stats.extra);
   1077    }
   1078  }
   1079 
   1080  const nsCString& Path() const { return mRtPath; }
   1081 
   1082  virtual void initExtraZoneStats(JS::Zone* aZone, JS::ZoneStats* aZoneStats,
   1083                                  const JS::AutoRequireNoGC& nogc) override {
   1084    MOZ_ASSERT(!aZoneStats->extra);
   1085 
   1086    // ReportJSRuntimeExplicitTreeStats expects that
   1087    // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
   1088    xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
   1089    extras->pathPrefix = mRtPath;
   1090    extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone);
   1091 
   1092    MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
   1093 
   1094    aZoneStats->extra = extras;
   1095  }
   1096 
   1097  virtual void initExtraRealmStats(JS::Realm* aRealm,
   1098                                   JS::RealmStats* aRealmStats,
   1099                                   const JS::AutoRequireNoGC& nogc) override {
   1100    MOZ_ASSERT(!aRealmStats->extra);
   1101 
   1102    // ReportJSRuntimeExplicitTreeStats expects that
   1103    // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
   1104    xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
   1105 
   1106    // This is the |jsPathPrefix|.  Each worker has exactly one realm.
   1107    extras->jsPathPrefix.Assign(mRtPath);
   1108    extras->jsPathPrefix +=
   1109        nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm));
   1110    extras->jsPathPrefix += "realm(web-worker)/"_ns;
   1111 
   1112    // This should never be used when reporting with workers (hence the "?!").
   1113    extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
   1114 
   1115    MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
   1116    MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
   1117 
   1118    extras->location = nullptr;
   1119 
   1120    aRealmStats->extra = extras;
   1121  }
   1122 };
   1123 
   1124 class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter {
   1125  NS_DECL_THREADSAFE_ISUPPORTS
   1126 
   1127  friend class WorkerPrivate;
   1128 
   1129  SharedMutex mMutex;
   1130  WorkerPrivate* mWorkerPrivate;
   1131 
   1132 public:
   1133  explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
   1134      : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) {
   1135    aWorkerPrivate->AssertIsOnWorkerThread();
   1136  }
   1137 
   1138  NS_IMETHOD
   1139  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
   1140                 bool aAnonymize) override;
   1141 
   1142 private:
   1143  class FinishCollectRunnable;
   1144 
   1145  class CollectReportsRunnable final : public MainThreadWorkerControlRunnable {
   1146    RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
   1147    const bool mAnonymize;
   1148 
   1149   public:
   1150    CollectReportsRunnable(WorkerPrivate* aWorkerPrivate,
   1151                           nsIHandleReportCallback* aHandleReport,
   1152                           nsISupports* aHandlerData, bool aAnonymize,
   1153                           const nsACString& aPath);
   1154 
   1155   private:
   1156    bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
   1157 
   1158    ~CollectReportsRunnable() {
   1159      if (NS_IsMainThread()) {
   1160        mFinishCollectRunnable->Run();
   1161        return;
   1162      }
   1163 
   1164      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1165      MOZ_ASSERT(workerPrivate);
   1166      MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging(
   1167          mFinishCollectRunnable.forget()));
   1168    }
   1169  };
   1170 
   1171  class FinishCollectRunnable final : public Runnable {
   1172    nsCOMPtr<nsIHandleReportCallback> mHandleReport;
   1173    nsCOMPtr<nsISupports> mHandlerData;
   1174    size_t mPerformanceUserEntries;
   1175    size_t mPerformanceResourceEntries;
   1176    const bool mAnonymize;
   1177    bool mSuccess;
   1178 
   1179   public:
   1180    WorkerJSContextStats mCxStats;
   1181 
   1182    explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport,
   1183                                   nsISupports* aHandlerData, bool aAnonymize,
   1184                                   const nsACString& aPath);
   1185 
   1186    NS_IMETHOD Run() override;
   1187 
   1188    void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) {
   1189      mPerformanceUserEntries = userEntries;
   1190      mPerformanceResourceEntries = resourceEntries;
   1191    }
   1192 
   1193    void SetSuccess(bool success) { mSuccess = success; }
   1194 
   1195    FinishCollectRunnable(const FinishCollectRunnable&) = delete;
   1196    FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
   1197    FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
   1198 
   1199   private:
   1200    ~FinishCollectRunnable() {
   1201      // mHandleReport and mHandlerData are released on the main thread.
   1202      AssertIsOnMainThread();
   1203    }
   1204  };
   1205 
   1206  ~MemoryReporter() = default;
   1207 
   1208  void Disable() {
   1209    // Called from WorkerPrivate::DisableMemoryReporter.
   1210    mMutex.AssertCurrentThreadOwns();
   1211 
   1212    NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
   1213    mWorkerPrivate = nullptr;
   1214  }
   1215 };
   1216 
   1217 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
   1218 
   1219 NS_IMETHODIMP
   1220 WorkerPrivate::MemoryReporter::CollectReports(
   1221    nsIHandleReportCallback* aHandleReport, nsISupports* aData,
   1222    bool aAnonymize) {
   1223  AssertIsOnMainThread();
   1224 
   1225  RefPtr<CollectReportsRunnable> runnable;
   1226 
   1227  {
   1228    MutexAutoLock lock(mMutex);
   1229 
   1230    if (!mWorkerPrivate) {
   1231      // This will effectively report 0 memory.
   1232      nsCOMPtr<nsIMemoryReporterManager> manager =
   1233          do_GetService("@mozilla.org/memory-reporter-manager;1");
   1234      if (manager) {
   1235        manager->EndReport();
   1236      }
   1237      return NS_OK;
   1238    }
   1239 
   1240    nsAutoCString path;
   1241    path.AppendLiteral("explicit/workers/workers(");
   1242    if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
   1243      path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
   1244    } else {
   1245      nsAutoCString escapedDomain(mWorkerPrivate->Domain());
   1246      if (escapedDomain.IsEmpty()) {
   1247        escapedDomain += "chrome";
   1248      } else {
   1249        escapedDomain.ReplaceChar('/', '\\');
   1250      }
   1251      path.Append(escapedDomain);
   1252      path.AppendLiteral(")/worker(");
   1253      NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
   1254      escapedURL.ReplaceChar('/', '\\');
   1255      path.Append(escapedURL);
   1256    }
   1257    path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
   1258 
   1259    runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData,
   1260                                          aAnonymize, path);
   1261  }
   1262 
   1263  if (!runnable->Dispatch(mWorkerPrivate)) {
   1264    return NS_ERROR_UNEXPECTED;
   1265  }
   1266 
   1267  return NS_OK;
   1268 }
   1269 
   1270 WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
   1271    WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport,
   1272    nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath)
   1273    : MainThreadWorkerControlRunnable("CollectReportsRunnable"),
   1274      mFinishCollectRunnable(new FinishCollectRunnable(
   1275          aHandleReport, aHandlerData, aAnonymize, aPath)),
   1276      mAnonymize(aAnonymize) {}
   1277 
   1278 bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(
   1279    JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
   1280  aWorkerPrivate->AssertIsOnWorkerThread();
   1281 
   1282  RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
   1283  RefPtr<Performance> performance =
   1284      scope ? scope->GetPerformanceIfExists() : nullptr;
   1285  if (performance) {
   1286    size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
   1287    size_t resourceEntries =
   1288        performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
   1289    mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
   1290  }
   1291 
   1292  mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(
   1293      &mFinishCollectRunnable->mCxStats, mAnonymize));
   1294 
   1295  return true;
   1296 }
   1297 
   1298 WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
   1299    nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData,
   1300    bool aAnonymize, const nsACString& aPath)
   1301    : mozilla::Runnable(
   1302          "dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),
   1303      mHandleReport(aHandleReport),
   1304      mHandlerData(aHandlerData),
   1305      mPerformanceUserEntries(0),
   1306      mPerformanceResourceEntries(0),
   1307      mAnonymize(aAnonymize),
   1308      mSuccess(false),
   1309      mCxStats(aPath) {}
   1310 
   1311 NS_IMETHODIMP
   1312 WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() {
   1313  AssertIsOnMainThread();
   1314 
   1315  nsCOMPtr<nsIMemoryReporterManager> manager =
   1316      do_GetService("@mozilla.org/memory-reporter-manager;1");
   1317 
   1318  if (!manager) return NS_OK;
   1319 
   1320  if (mSuccess) {
   1321    xpc::ReportJSRuntimeExplicitTreeStats(
   1322        mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize);
   1323 
   1324    if (mPerformanceUserEntries) {
   1325      nsCString path = mCxStats.Path();
   1326      path.AppendLiteral("dom/performance/user-entries");
   1327      mHandleReport->Callback(""_ns, path, nsIMemoryReporter::KIND_HEAP,
   1328                              nsIMemoryReporter::UNITS_BYTES,
   1329                              static_cast<int64_t>(mPerformanceUserEntries),
   1330                              "Memory used for performance user entries."_ns,
   1331                              mHandlerData);
   1332    }
   1333 
   1334    if (mPerformanceResourceEntries) {
   1335      nsCString path = mCxStats.Path();
   1336      path.AppendLiteral("dom/performance/resource-entries");
   1337      mHandleReport->Callback(
   1338          ""_ns, path, nsIMemoryReporter::KIND_HEAP,
   1339          nsIMemoryReporter::UNITS_BYTES,
   1340          static_cast<int64_t>(mPerformanceResourceEntries),
   1341          "Memory used for performance resource entries."_ns, mHandlerData);
   1342    }
   1343  }
   1344 
   1345  manager->EndReport();
   1346 
   1347  return NS_OK;
   1348 }
   1349 
   1350 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
   1351    : mEventTarget(aEventTarget),
   1352      mResult(NS_ERROR_FAILURE),
   1353      mCompleted(false)
   1354 #ifdef DEBUG
   1355      ,
   1356      mHasRun(false)
   1357 #endif
   1358 {
   1359 }
   1360 
   1361 Document* WorkerPrivate::GetDocument() const {
   1362  AssertIsOnMainThread();
   1363  if (nsPIDOMWindowInner* window = GetAncestorWindow()) {
   1364    return window->GetExtantDoc();
   1365  }
   1366  // couldn't query a document, give up and return nullptr
   1367  return nullptr;
   1368 }
   1369 
   1370 nsPIDOMWindowInner* WorkerPrivate::GetAncestorWindow() const {
   1371  AssertIsOnMainThread();
   1372 
   1373  // We should query the window from the top level worker in case of a nested
   1374  // worker, as only the top level one can have a window.
   1375  WorkerPrivate* top = GetTopLevelWorker();
   1376  return top->GetWindow();
   1377 }
   1378 
   1379 class EvictFromBFCacheRunnable final : public WorkerProxyToMainThreadRunnable {
   1380 public:
   1381  void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override {
   1382    MOZ_ASSERT(aWorkerPrivate);
   1383    AssertIsOnMainThread();
   1384    if (nsCOMPtr<nsPIDOMWindowInner> win =
   1385            aWorkerPrivate->GetAncestorWindow()) {
   1386      win->RemoveFromBFCacheSync();
   1387    }
   1388  }
   1389 
   1390  void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override {
   1391    MOZ_ASSERT(aWorkerPrivate);
   1392    aWorkerPrivate->AssertIsOnWorkerThread();
   1393  }
   1394 };
   1395 
   1396 void WorkerPrivate::EvictFromBFCache() {
   1397  AssertIsOnWorkerThread();
   1398  RefPtr<EvictFromBFCacheRunnable> runnable = new EvictFromBFCacheRunnable();
   1399  runnable->Dispatch(this);
   1400 }
   1401 
   1402 nsresult WorkerPrivate::SetCsp(nsIContentSecurityPolicy* aCSP) {
   1403  AssertIsOnMainThread();
   1404  if (!aCSP) {
   1405    return NS_OK;
   1406  }
   1407  aCSP->EnsureEventTarget(mMainThreadEventTarget);
   1408 
   1409  mLoadInfo.mCSP = aCSP;
   1410  auto ctx = WorkerCSPContext::CreateFromCSP(aCSP);
   1411  if (NS_WARN_IF(ctx.isErr())) {
   1412    return ctx.unwrapErr();
   1413  }
   1414  mLoadInfo.mCSPContext = ctx.unwrap();
   1415  return NS_OK;
   1416 }
   1417 
   1418 nsresult WorkerPrivate::SetCSPFromHeaderValues(
   1419    const nsACString& aCSPHeaderValue,
   1420    const nsACString& aCSPReportOnlyHeaderValue) {
   1421  AssertIsOnMainThread();
   1422  MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
   1423 
   1424  NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
   1425  NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
   1426 
   1427  nsresult rv;
   1428  nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();
   1429 
   1430  // First, we try to query the URI from the Principal, but
   1431  // in case selfURI remains empty (e.g in case the Principal
   1432  // is a SystemPrincipal) then we fall back and use the
   1433  // base URI as selfURI for CSP.
   1434  nsCOMPtr<nsIURI> selfURI;
   1435  // Its not recommended to use the BasePrincipal to get the URI
   1436  // but in this case we need to make an exception
   1437  auto* basePrin = BasePrincipal::Cast(mLoadInfo.mPrincipal);
   1438  if (basePrin) {
   1439    basePrin->GetURI(getter_AddRefs(selfURI));
   1440  }
   1441  if (!selfURI) {
   1442    selfURI = mLoadInfo.mBaseURI;
   1443  }
   1444  MOZ_ASSERT(selfURI, "need a self URI for CSP");
   1445 
   1446  rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI, ""_ns,
   1447                                           0);
   1448  NS_ENSURE_SUCCESS(rv, rv);
   1449 
   1450  csp->EnsureEventTarget(mMainThreadEventTarget);
   1451 
   1452  // If there's a CSP header, apply it.
   1453  if (!cspHeaderValue.IsEmpty()) {
   1454    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
   1455    NS_ENSURE_SUCCESS(rv, rv);
   1456  }
   1457  // If there's a report-only CSP header, apply it.
   1458  if (!cspROHeaderValue.IsEmpty()) {
   1459    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
   1460    NS_ENSURE_SUCCESS(rv, rv);
   1461  }
   1462 
   1463  RefPtr<extensions::WebExtensionPolicy> addonPolicy;
   1464 
   1465  if (basePrin) {
   1466    addonPolicy = basePrin->AddonPolicy();
   1467  }
   1468 
   1469  // For extension workers there aren't any csp header values,
   1470  // instead it will inherit the Extension CSP.
   1471  if (addonPolicy) {
   1472    csp->AppendPolicy(addonPolicy->BaseCSP(), false, false);
   1473    csp->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false);
   1474  }
   1475 
   1476  mLoadInfo.mCSP = csp;
   1477 
   1478  auto ctx = WorkerCSPContext::CreateFromCSP(csp);
   1479  if (NS_WARN_IF(ctx.isErr())) {
   1480    return ctx.unwrapErr();
   1481  }
   1482  mLoadInfo.mCSPContext = ctx.unwrap();
   1483  return NS_OK;
   1484 }
   1485 
   1486 bool WorkerPrivate::IsFrozenForWorkerThread() const {
   1487  auto data = mWorkerThreadAccessible.Access();
   1488  return data->mFrozen;
   1489 }
   1490 
   1491 bool WorkerPrivate::IsFrozen() const {
   1492  AssertIsOnParentThread();
   1493  return mParentFrozen;
   1494 }
   1495 
   1496 void WorkerPrivate::StoreCSPOnClient() {
   1497  auto data = mWorkerThreadAccessible.Access();
   1498  MOZ_ASSERT(data->mScope);
   1499  if (mLoadInfo.mCSPContext) {
   1500    mozilla::ipc::PolicyContainerArgs policyContainerArgs;
   1501    policyContainerArgs.csp() = Some(mLoadInfo.mCSPContext->CSPInfo());
   1502    data->mScope->MutableClientSourceRef().SetPolicyContainerArgs(
   1503        policyContainerArgs);
   1504  }
   1505 }
   1506 
   1507 void WorkerPrivate::UpdateReferrerInfoFromHeader(
   1508    const nsACString& aReferrerPolicyHeaderValue) {
   1509  NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
   1510 
   1511  if (headerValue.IsEmpty()) {
   1512    return;
   1513  }
   1514 
   1515  ReferrerPolicy policy =
   1516      ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue);
   1517  if (policy == ReferrerPolicy::_empty) {
   1518    return;
   1519  }
   1520 
   1521  nsCOMPtr<nsIReferrerInfo> referrerInfo =
   1522      static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy);
   1523  SetReferrerInfo(referrerInfo);
   1524 }
   1525 
   1526 void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
   1527  AssertIsOnParentThread();
   1528 
   1529  // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
   1530  // Worker object, which is really held by the worker thread.  We traverse this
   1531  // reference if and only if all main thread event queues are empty, no
   1532  // shutdown tasks, no StrongWorkerRefs, no child workers, no timeouts, no
   1533  // blocking background actors, and we have not released the main thread
   1534  // reference.  We do not unlink it. This allows the CC to break cycles
   1535  // involving the Worker and begin shutting it down (which does happen in
   1536  // unlink) but ensures that the WorkerPrivate won't be deleted before we're
   1537  // done shutting down the thread.
   1538  if (IsEligibleForCC() && !mMainThreadObjectsForgotten) {
   1539    nsCycleCollectionTraversalCallback& cb = aCb;
   1540    WorkerPrivate* tmp = this;
   1541    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
   1542  }
   1543 }
   1544 
   1545 nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
   1546                                 nsIEventTarget* aSyncLoopTarget) {
   1547  // May be called on any thread!
   1548  RefPtr<WorkerRunnable> runnable(aRunnable);
   1549 
   1550  LOGV(("WorkerPrivate::Dispatch [%p] runnable %p", this, runnable.get()));
   1551  if (!aSyncLoopTarget) {
   1552    // Dispatch control runnable
   1553    if (runnable->IsControlRunnable()) {
   1554      return DispatchControlRunnable(runnable.forget());
   1555    }
   1556 
   1557    // Dispatch debugger runnable
   1558    if (runnable->IsDebuggerRunnable()) {
   1559      return DispatchDebuggerRunnable(runnable.forget());
   1560    }
   1561  }
   1562  MutexAutoLock lock(mMutex);
   1563  return DispatchLockHeld(runnable.forget(), aSyncLoopTarget, lock);
   1564 }
   1565 
   1566 nsresult WorkerPrivate::DispatchToParent(
   1567    already_AddRefed<WorkerRunnable> aRunnable) {
   1568  RefPtr<WorkerRunnable> runnable(aRunnable);
   1569 
   1570  LOGV(("WorkerPrivate::DispatchToParent [%p] runnable %p", this,
   1571        runnable.get()));
   1572 
   1573  WorkerPrivate* parent = GetParent();
   1574  // Dispatch to parent worker
   1575  if (parent) {
   1576    if (runnable->IsControlRunnable()) {
   1577      return parent->DispatchControlRunnable(runnable.forget());
   1578    }
   1579    return parent->Dispatch(runnable.forget());
   1580  }
   1581 
   1582  // Dispatch to main thread
   1583  if (runnable->IsDebuggeeRunnable()) {
   1584    RefPtr<WorkerParentDebuggeeRunnable> debuggeeRunnable =
   1585        runnable.forget().downcast<WorkerParentDebuggeeRunnable>();
   1586    return DispatchDebuggeeToMainThread(debuggeeRunnable.forget(),
   1587                                        NS_DISPATCH_FALLIBLE);
   1588  }
   1589  return DispatchToMainThread(runnable.forget(), NS_DISPATCH_FALLIBLE);
   1590 }
   1591 
   1592 nsresult WorkerPrivate::DispatchLockHeld(
   1593    already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget,
   1594    const MutexAutoLock& aProofOfLock) {
   1595  // May be called on any thread!
   1596  RefPtr<WorkerRunnable> runnable(aRunnable);
   1597  LOGV(("WorkerPrivate::DispatchLockHeld [%p] runnable: %p", this,
   1598        runnable.get()));
   1599 
   1600  MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
   1601 
   1602  // Dispatch normal worker runnable
   1603  if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Canceling)) {
   1604    NS_WARNING(
   1605        "A runnable was posted to a worker that is already shutting "
   1606        "down!");
   1607    return NS_ERROR_UNEXPECTED;
   1608  }
   1609 
   1610  // Postpone the debuggee runnable dispatching while remote debugger
   1611  // registration
   1612  if (runnable->IsDebuggeeRunnable() && !mDebuggerReady &&
   1613      !mRemoteDebuggerReady &&
   1614      (!mRemoteDebuggerRegistered && XRE_IsParentProcess())) {
   1615    MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
   1616    mDelayedDebuggeeRunnables.AppendElement(runnable);
   1617    return NS_OK;
   1618  }
   1619 
   1620  if (!mThread) {
   1621    if (ParentStatus() == Pending || mStatus == Pending) {
   1622      LOGV(
   1623          ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is queued in "
   1624           "mPreStartRunnables",
   1625           this, runnable.get()));
   1626      RefPtr<WorkerThreadRunnable> workerThreadRunnable =
   1627          static_cast<WorkerThreadRunnable*>(runnable.get());
   1628      PROFILER_MARKER("WorkerPrivate::DispatchLockHeld", OTHER,
   1629                      {MarkerStack::MaybeCapture(
   1630                          profiler_feature_active(ProfilerFeature::Flows))},
   1631                      FlowMarker, Flow::FromPointer(runnable.get()));
   1632      mPreStartRunnables.AppendElement(workerThreadRunnable);
   1633      return NS_OK;
   1634    }
   1635 
   1636    NS_WARNING(
   1637        "Using a worker event target after the thread has already"
   1638        "been released!");
   1639    return NS_ERROR_UNEXPECTED;
   1640  }
   1641 
   1642  nsresult rv;
   1643  if (aSyncLoopTarget) {
   1644    LOGV(
   1645        ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to a "
   1646         "SyncLoop(%p)",
   1647         this, runnable.get(), aSyncLoopTarget));
   1648    rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_FALLIBLE);
   1649  } else {
   1650    // If mStatus is Pending, the WorkerPrivate initialization still can fail.
   1651    // Append this WorkerThreadRunnable to WorkerPrivate::mPreStartRunnables,
   1652    // such that this WorkerThreadRunnable can get the correct value of
   1653    // mCleanPreStartDispatching in WorkerPrivate::RunLoopNeverRan().
   1654    if (mStatus == Pending) {
   1655      LOGV(
   1656          ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is append in "
   1657           "mPreStartRunnables",
   1658           this, runnable.get()));
   1659      RefPtr<WorkerThreadRunnable> workerThreadRunnable =
   1660          static_cast<WorkerThreadRunnable*>(runnable.get());
   1661      PROFILER_MARKER("WorkerPrivate::DispatchLockHeld", OTHER,
   1662                      {MarkerStack::MaybeCapture(
   1663                          profiler_feature_active(ProfilerFeature::Flows))},
   1664                      FlowMarker, Flow::FromPointer(runnable.get()));
   1665      mPreStartRunnables.AppendElement(workerThreadRunnable);
   1666    }
   1667 
   1668    // WorkerDebuggeeRunnables don't need any special treatment here. True,
   1669    // they should not be delivered to a frozen worker. But frozen workers
   1670    // aren't drawing from the thread's main event queue anyway, only from
   1671    // mControlQueue.
   1672    LOGV(
   1673        ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to the "
   1674         "main event queue",
   1675         this, runnable.get()));
   1676    rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
   1677  }
   1678 
   1679  if (NS_WARN_IF(NS_FAILED(rv))) {
   1680    LOGV(("WorkerPrivate::Dispatch Failed [%p]", this));
   1681    return rv;
   1682  }
   1683 
   1684  mCondVar.Notify();
   1685  return NS_OK;
   1686 }
   1687 
   1688 void WorkerPrivate::EnableDebugger() {
   1689  AssertIsOnParentThread();
   1690 
   1691  if (NS_FAILED(RegisterWorkerDebugger(this))) {
   1692    NS_WARNING("Failed to register worker debugger!");
   1693    return;
   1694  }
   1695 }
   1696 
   1697 void WorkerPrivate::DisableDebugger() {
   1698  AssertIsOnParentThread();
   1699 
   1700  // RegisterDebuggerMainThreadRunnable might be dispatched but not executed.
   1701  // Wait for its execution before unregistraion.
   1702  if (!NS_IsMainThread()) {
   1703    WaitForIsDebuggerRegistered(true);
   1704  }
   1705 
   1706  if (NS_FAILED(UnregisterWorkerDebugger(this))) {
   1707    NS_WARNING("Failed to unregister worker debugger!");
   1708  }
   1709 }
   1710 
   1711 void WorkerPrivate::BindRemoteWorkerDebuggerChild() {
   1712  AssertIsOnWorkerThread();
   1713  MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger);
   1714 
   1715  if (XRE_IsParentProcess()) {
   1716    return;
   1717  }
   1718 
   1719  RefPtr<RemoteWorkerDebuggerChild> debugger =
   1720      MakeRefPtr<RemoteWorkerDebuggerChild>(this);
   1721  mDebuggerChildEp.Bind(debugger);
   1722  {
   1723    MutexAutoLock lock(mMutex);
   1724    MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger);
   1725    mRemoteDebugger = std::move(debugger);
   1726    mDebuggerBindingCondVar.Notify();
   1727  }
   1728 }
   1729 
   1730 void WorkerPrivate::CreateRemoteDebuggerEndpoints() {
   1731  AssertIsOnParentThread();
   1732 
   1733  if (XRE_IsParentProcess()) {
   1734    return;
   1735  }
   1736 
   1737  MutexAutoLock lock(mMutex);
   1738  MOZ_ASSERT_DEBUG_OR_FUZZING(!mRemoteDebugger &&
   1739                              !mDebuggerParentEp.IsValid() &&
   1740                              !mDebuggerChildEp.IsValid());
   1741 
   1742  (void)NS_WARN_IF(NS_FAILED(PRemoteWorkerDebugger::CreateEndpoints(
   1743      &mDebuggerParentEp, &mDebuggerChildEp)));
   1744 }
   1745 
   1746 void WorkerPrivate::SetIsRemoteDebuggerRegistered(const bool& aRegistered) {
   1747  AssertIsOnWorkerThread();
   1748 
   1749  if (XRE_IsParentProcess()) {
   1750    return;
   1751  }
   1752 
   1753  if (aRegistered) {
   1754    MutexAutoLock lock(mMutex);
   1755    MOZ_ASSERT(mRemoteDebuggerRegistered != aRegistered);
   1756 
   1757    mRemoteDebuggerRegistered = aRegistered;
   1758    bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered;
   1759    if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) {
   1760      LOGV(
   1761          ("WorkerPrivate::SetIsRemoteDebuggerRegistered [%p] dispatching "
   1762           "the delayed debuggee runnables",
   1763           this));
   1764      // Dispatch all the delayed runnables without releasing the lock, to
   1765      // ensure that the order in which debuggee runnables execute is the same
   1766      // as the order in which they were originally dispatched.
   1767      auto pending = std::move(mDelayedDebuggeeRunnables);
   1768      for (uint32_t i = 0; i < pending.Length(); i++) {
   1769        RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
   1770        (void)NS_WARN_IF(
   1771            NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock)));
   1772      }
   1773      MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
   1774    }
   1775    mDebuggerBindingCondVar.Notify();
   1776    return;
   1777  }
   1778 
   1779  RefPtr<RemoteWorkerDebuggerChild> unregisteredDebugger;
   1780  {
   1781    MutexAutoLock lock(mMutex);
   1782    // Can not call RemoteWorkerDebuggerChild::Close() with lock. It causes
   1783    // deadlock between mMutex and MessageChannel::mMonitor.
   1784    unregisteredDebugger = std::move(mRemoteDebugger);
   1785    // Force to set as unregistered, mRemoteDebuggerRegistered could be false
   1786    // here since Worker quickly shutdown or initialization fails in
   1787    // WorkerThreadPrimaryRunnable::Run().
   1788    mRemoteDebuggerRegistered = aRegistered;
   1789  }
   1790  if (unregisteredDebugger) {
   1791    unregisteredDebugger->Close();
   1792    unregisteredDebugger = nullptr;
   1793  }
   1794  {
   1795    MutexAutoLock lock(mMutex);
   1796    mDebuggerBindingCondVar.Notify();
   1797  }
   1798 }
   1799 
   1800 void WorkerPrivate::SetIsRemoteDebuggerReady(const bool& aReady) {
   1801  AssertIsOnWorkerThread();
   1802  MutexAutoLock lock(mMutex);
   1803 
   1804  if (XRE_IsParentProcess()) {
   1805    return;
   1806  }
   1807 
   1808  if (mRemoteDebuggerReady == aReady) {
   1809    return;
   1810  }
   1811 
   1812  bool debuggerRegistered = mDebuggerRegistered && mRemoteDebuggerRegistered;
   1813 
   1814  if (!aReady && debuggerRegistered) {
   1815    // The debugger can only be marked as not ready during registration.
   1816    return;
   1817  }
   1818 
   1819  mRemoteDebuggerReady = aReady;
   1820 
   1821  if (mRemoteDebuggerReady && mDebuggerReady && debuggerRegistered) {
   1822    LOGV(
   1823        ("WorkerPrivate::SetIsRemoteDebuggerReady [%p] dispatching "
   1824         "the delayed debuggee runnables",
   1825         this));
   1826    // Dispatch all the delayed runnables without releasing the lock, to ensure
   1827    // that the order in which debuggee runnables execute is the same as the
   1828    // order in which they were originally dispatched.
   1829    auto pending = std::move(mDelayedDebuggeeRunnables);
   1830    for (uint32_t i = 0; i < pending.Length(); i++) {
   1831      RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
   1832      (void)NS_WARN_IF(
   1833          NS_FAILED(DispatchLockHeld(runnable.forget(), nullptr, lock)));
   1834    }
   1835    MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
   1836  }
   1837 }
   1838 
   1839 void WorkerPrivate::SetIsQueued(const bool& aQueued) {
   1840  AssertIsOnParentThread();
   1841  mIsQueued = aQueued;
   1842 }
   1843 
   1844 bool WorkerPrivate::IsQueued() const {
   1845  AssertIsOnParentThread();
   1846  return mIsQueued;
   1847 }
   1848 
   1849 void WorkerPrivate::EnableRemoteDebugger() {
   1850  AssertIsOnParentThread();
   1851 
   1852  // XXX Skip for ChromeWorker now, this should be removed after Devtool codes
   1853  // adapt to RemoteWorkerDebugger mechanism.
   1854  if (XRE_IsParentProcess()) {
   1855    return;
   1856  }
   1857 
   1858  // Wait for RemoteWorkerDebuggerChild binding done in the worker thread.
   1859  mozilla::ipc::Endpoint<PRemoteWorkerDebuggerParent> parentEp;
   1860  {
   1861    MutexAutoLock lock(mMutex);
   1862    if (!mRemoteDebugger) {
   1863      mDebuggerBindingCondVar.Wait();
   1864    }
   1865    // If Worker Thread never run the event loop, i.e. JSContext initilaization
   1866    // fails, directly return for the cases. Because mRemoteDebugger is only
   1867    // created after initialization successfully, but mDebuggerBindingCondVar
   1868    // can get notified if the initialization fails.
   1869    if (!mRemoteDebugger) {
   1870      return;
   1871    }
   1872    parentEp = std::move(mDebuggerParentEp);
   1873  }
   1874 
   1875  // Call IPC for RemoteWorkerDebuggerParent binding and registration.
   1876  RemoteWorkerDebuggerInfo info(
   1877      mIsChromeWorker, mWorkerKind, mScriptURL, WindowID(),
   1878      WrapNotNull(GetPrincipal()), IsServiceWorker() ? ServiceWorkerID() : 0,
   1879      Id(), mWorkerName,
   1880      GetParent() ? nsAutoString(GetParent()->Id()) : EmptyString());
   1881 
   1882  MOZ_ASSERT_DEBUG_OR_FUZZING(parentEp.IsValid());
   1883  RemoteWorkerService::RegisterRemoteDebugger(std::move(info),
   1884                                              std::move(parentEp));
   1885  // Wait for register done
   1886  {
   1887    MutexAutoLock lock(mMutex);
   1888    if (!mRemoteDebuggerRegistered) {
   1889      mDebuggerBindingCondVar.Wait();
   1890    }
   1891    // Warning the case if the Worker shutdown before remote debugger
   1892    // registration down.
   1893    (void)NS_WARN_IF(!mRemoteDebuggerRegistered);
   1894  }
   1895 }
   1896 
   1897 void WorkerPrivate::DisableRemoteDebugger() {
   1898  AssertIsOnParentThread();
   1899 
   1900  if (XRE_IsParentProcess()) {
   1901    return;
   1902  }
   1903 
   1904  RefPtr<DisableRemoteDebuggerRunnable> r =
   1905      new DisableRemoteDebuggerRunnable(this);
   1906 
   1907  if (r->Dispatch(this)) {
   1908    MutexAutoLock lock(mMutex);
   1909    if (mRemoteDebuggerRegistered) {
   1910      mDebuggerBindingCondVar.Wait();
   1911    }
   1912  }
   1913 }
   1914 
   1915 void WorkerPrivate::DisableRemoteDebuggerOnWorkerThread(
   1916    const bool& aForShutdown) {
   1917  AssertIsOnWorkerThread();
   1918 
   1919  if (XRE_IsParentProcess()) {
   1920    return;
   1921  }
   1922  RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
   1923  {
   1924    MutexAutoLock lock(mMutex);
   1925    remoteDebugger = mRemoteDebugger;
   1926  }
   1927  if (remoteDebugger) {
   1928    remoteDebugger->SendUnregister();
   1929  }
   1930 
   1931  // Now notify the parent thread if it is blocked by waiting
   1932  // RemoteWorkerDebugger registration or unregsiteration.
   1933  if (aForShutdown) {
   1934    SetIsRemoteDebuggerRegistered(false);
   1935  }
   1936 }
   1937 
   1938 nsresult WorkerPrivate::DispatchControlRunnable(
   1939    already_AddRefed<WorkerRunnable> aWorkerRunnable) {
   1940  // May be called on any thread!
   1941  RefPtr<WorkerRunnable> runnable(aWorkerRunnable);
   1942  MOZ_ASSERT_DEBUG_OR_FUZZING(runnable && runnable->IsControlRunnable());
   1943 
   1944  LOG(WorkerLog(), ("WorkerPrivate::DispatchControlRunnable [%p] runnable %p",
   1945                    this, runnable.get()));
   1946 
   1947  JSContext* cx = nullptr;
   1948  {
   1949    MutexAutoLock lock(mMutex);
   1950 
   1951    if (mStatus == Dead) {
   1952      return NS_ERROR_UNEXPECTED;
   1953    }
   1954 
   1955    // Unfortunately we can not distinguish if we are on WorkerThread or not.
   1956    // mThread is set in WorkerPrivate::SetWorkerPrivateInWorkerThread(), but
   1957    // ControlRunnable can be dispatching before setting mThread.
   1958    MOZ_ASSERT(mDispatchingControlRunnables < UINT32_MAX);
   1959    mDispatchingControlRunnables++;
   1960 
   1961    // Transfer ownership to the control queue.
   1962    mControlQueue.Push(runnable.forget().take());
   1963    cx = mJSContext;
   1964    MOZ_ASSERT_IF(cx, mThread);
   1965  }
   1966 
   1967  if (cx) {
   1968    JS_RequestInterruptCallback(cx);
   1969  }
   1970 
   1971  {
   1972    MutexAutoLock lock(mMutex);
   1973    if (!--mDispatchingControlRunnables) {
   1974      mCondVar.Notify();
   1975    }
   1976  }
   1977 
   1978  return NS_OK;
   1979 }
   1980 
   1981 void DebuggerInterruptTimerCallback(nsITimer* aTimer, void* aClosure)
   1982    MOZ_NO_THREAD_SAFETY_ANALYSIS {
   1983  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1984  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
   1985  workerPrivate->DebuggerInterruptRequest();
   1986 }
   1987 
   1988 nsresult WorkerPrivate::DispatchDebuggerRunnable(
   1989    already_AddRefed<WorkerRunnable> aDebuggerRunnable) {
   1990  // May be called on any thread!
   1991 
   1992  RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
   1993 
   1994  MOZ_ASSERT(runnable);
   1995 
   1996  MutexAutoLock lock(mMutex);
   1997  if (!mDebuggerInterruptTimer) {
   1998    // There is no timer, so we need to create one.  For locking discipline
   1999    // purposes we can't manipulate the timer while our mutex is held so
   2000    // drop the mutex while we build and configure the timer.  Only this
   2001    // function here on the main thread will create a timer, so we're not
   2002    // racing anyone to create or assign the timer.
   2003    nsCOMPtr<nsITimer> timer;
   2004    {
   2005      MutexAutoUnlock unlock(mMutex);
   2006      timer = NS_NewTimer();
   2007      MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(mWorkerControlEventTarget));
   2008 
   2009      // Whenever an event is scheduled on the WorkerControlEventTarget an
   2010      // interrupt is automatically requested which causes us to yield JS
   2011      // execution and the next JS execution in the queue to execute. This
   2012      // allows for simple code reuse of the existing interrupt callback code
   2013      // used for control events.
   2014      MOZ_ALWAYS_SUCCEEDS(timer->InitWithNamedFuncCallback(
   2015          DebuggerInterruptTimerCallback, nullptr,
   2016          DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS, nsITimer::TYPE_ONE_SHOT,
   2017          "dom:DebuggerInterruptTimer"_ns));
   2018    }
   2019 
   2020    // okay, we have our mutex back now, put the timer in place.
   2021    mDebuggerInterruptTimer.swap(timer);
   2022  }
   2023 
   2024  if (mStatus == Dead) {
   2025    NS_WARNING(
   2026        "A debugger runnable was posted to a worker that is already "
   2027        "shutting down!");
   2028    return NS_ERROR_UNEXPECTED;
   2029  }
   2030 
   2031  // Transfer ownership to the debugger queue.
   2032  mDebuggerQueue.Push(runnable.forget().take());
   2033 
   2034  mCondVar.Notify();
   2035 
   2036  return NS_OK;
   2037 }
   2038 
   2039 void WorkerPrivate::DebuggerInterruptRequest() {
   2040  AssertIsOnWorkerThread();
   2041 
   2042  auto data = mWorkerThreadAccessible.Access();
   2043  data->mDebuggerInterruptRequested = true;
   2044 }
   2045 
   2046 already_AddRefed<WorkerRunnable> WorkerPrivate::MaybeWrapAsWorkerRunnable(
   2047    already_AddRefed<nsIRunnable> aRunnable) {
   2048  // May be called on any thread!
   2049 
   2050  nsCOMPtr<nsIRunnable> runnable(aRunnable);
   2051  MOZ_ASSERT(runnable);
   2052 
   2053  LOGV(("WorkerPrivate::MaybeWrapAsWorkerRunnable [%p] runnable: %p", this,
   2054        runnable.get()));
   2055 
   2056  RefPtr<WorkerRunnable> workerRunnable =
   2057      WorkerRunnable::FromRunnable(runnable);
   2058  if (workerRunnable) {
   2059    return workerRunnable.forget();
   2060  }
   2061 
   2062  workerRunnable = new ExternalRunnableWrapper(this, runnable);
   2063  return workerRunnable.forget();
   2064 }
   2065 
   2066 bool WorkerPrivate::Start() {
   2067  // May be called on any thread!
   2068  LOG(WorkerLog(), ("WorkerPrivate::Start [%p]", this));
   2069  {
   2070    MutexAutoLock lock(mMutex);
   2071    NS_ASSERTION(mParentStatus != Running, "How can this be?!");
   2072 
   2073    if (mParentStatus == Pending) {
   2074      mParentStatus = Running;
   2075      return true;
   2076    }
   2077  }
   2078 
   2079  return false;
   2080 }
   2081 
   2082 // aCx is null when called from the finalizer
   2083 bool WorkerPrivate::Notify(WorkerStatus aStatus) {
   2084  AssertIsOnParentThread();
   2085  // This method is only called for Canceling or later.
   2086  MOZ_DIAGNOSTIC_ASSERT(aStatus >= Canceling);
   2087 
   2088  bool pending;
   2089  {
   2090    MutexAutoLock lock(mMutex);
   2091 
   2092    if (mParentStatus >= aStatus) {
   2093      return true;
   2094    }
   2095 
   2096    pending = mParentStatus == Pending;
   2097    mParentStatus = aStatus;
   2098  }
   2099 
   2100  if (mCancellationCallback) {
   2101    mCancellationCallback(!pending);
   2102    mCancellationCallback = nullptr;
   2103  }
   2104 
   2105  mParentRef->DropWorkerPrivate();
   2106 
   2107  if (pending) {
   2108 #ifdef DEBUG
   2109    {
   2110      // Fake a thread here just so that our assertions don't go off for no
   2111      // reason.
   2112      nsIThread* currentThread = NS_GetCurrentThread();
   2113      MOZ_ASSERT(currentThread);
   2114 
   2115      MOZ_ASSERT(!mPRThread);
   2116      mPRThread = PRThreadFromThread(currentThread);
   2117      MOZ_ASSERT(mPRThread);
   2118    }
   2119 #endif
   2120 
   2121    // Worker never got a chance to run, go ahead and delete it.
   2122    ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
   2123    return true;
   2124  }
   2125 
   2126  // No Canceling timeout is needed.
   2127  if (mCancelingTimer) {
   2128    mCancelingTimer->Cancel();
   2129    mCancelingTimer = nullptr;
   2130  }
   2131 
   2132  // The NotifyRunnable kicks off a series of events that need the
   2133  // CancelingOnParentRunnable to be executed always.
   2134  // Note that we already advanced mParentStatus above and we check that
   2135  // status in all other (asynchronous) call sites of SetIsPaused.
   2136  if (!mParent) {
   2137    MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(false));
   2138  }
   2139 
   2140  RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
   2141  return runnable->Dispatch(this);
   2142 }
   2143 
   2144 bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) {
   2145  AssertIsOnParentThread();
   2146 
   2147  mParentFrozen = true;
   2148 
   2149  bool isCanceling = false;
   2150  {
   2151    MutexAutoLock lock(mMutex);
   2152 
   2153    isCanceling = mParentStatus >= Canceling;
   2154  }
   2155 
   2156  // WorkerDebuggeeRunnables sent from a worker to content must not be
   2157  // delivered while the worker is frozen.
   2158  //
   2159  // Since a top-level worker and all its children share the same
   2160  // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
   2161  // top-level worker.
   2162  if (aWindow) {
   2163    // This is called from WorkerPrivate construction, and We may not have
   2164    // allocated mMainThreadDebuggeeEventTarget yet.
   2165    if (mMainThreadDebuggeeEventTarget) {
   2166      // Pausing a ThrottledEventQueue is infallible.
   2167      MOZ_ALWAYS_SUCCEEDS(
   2168          mMainThreadDebuggeeEventTarget->SetIsPaused(!isCanceling));
   2169    }
   2170  }
   2171 
   2172  if (isCanceling) {
   2173    return true;
   2174  }
   2175 
   2176  // DisableRemoteDebugger();
   2177 
   2178  DisableDebugger();
   2179 
   2180  RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
   2181  return runnable->Dispatch(this);
   2182 }
   2183 
   2184 bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) {
   2185  AssertIsOnParentThread();
   2186  MOZ_ASSERT(mParentFrozen);
   2187 
   2188  mParentFrozen = false;
   2189 
   2190  {
   2191    bool isCanceling = false;
   2192 
   2193    {
   2194      MutexAutoLock lock(mMutex);
   2195 
   2196      isCanceling = mParentStatus >= Canceling;
   2197    }
   2198 
   2199    // Delivery of WorkerDebuggeeRunnables to the window may resume.
   2200    //
   2201    // Since a top-level worker and all its children share the same
   2202    // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
   2203    // top-level worker.
   2204    if (aWindow) {
   2205      // Since the worker is no longer frozen, only a paused parent window
   2206      // should require the queue to remain paused.
   2207      //
   2208      // This can only fail if the ThrottledEventQueue cannot dispatch its
   2209      // executor to the main thread, in which case the main thread was never
   2210      // going to draw runnables from it anyway, so the failure doesn't matter.
   2211      (void)mMainThreadDebuggeeEventTarget->SetIsPaused(
   2212          IsParentWindowPaused() && !isCanceling);
   2213    }
   2214 
   2215    if (isCanceling) {
   2216      return true;
   2217    }
   2218  }
   2219 
   2220  // Create remote debugger endpoints here for child binding in ThawRunnable;
   2221  // CreateRemoteDebuggerEndpoints();
   2222  RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
   2223  bool rv = runnable->Dispatch(this);
   2224  // EnableRemoteDebugger();
   2225 
   2226  EnableDebugger();
   2227 
   2228  return rv;
   2229 }
   2230 
   2231 void WorkerPrivate::ParentWindowPaused() {
   2232  AssertIsOnMainThread();
   2233  MOZ_ASSERT(!mParentWindowPaused);
   2234  mParentWindowPaused = true;
   2235 
   2236  // This is called from WorkerPrivate construction, and we may not have
   2237  // allocated mMainThreadDebuggeeEventTarget yet.
   2238  if (mMainThreadDebuggeeEventTarget) {
   2239    bool isCanceling = false;
   2240 
   2241    {
   2242      MutexAutoLock lock(mMutex);
   2243 
   2244      isCanceling = mParentStatus >= Canceling;
   2245    }
   2246 
   2247    // If we are already canceling we might wait for CancelingOnParentRunnable
   2248    // to be executed, so do not pause.
   2249    MOZ_ALWAYS_SUCCEEDS(
   2250        mMainThreadDebuggeeEventTarget->SetIsPaused(!isCanceling));
   2251  }
   2252 }
   2253 
   2254 void WorkerPrivate::ParentWindowResumed() {
   2255  AssertIsOnMainThread();
   2256 
   2257  MOZ_ASSERT(mParentWindowPaused);
   2258  mParentWindowPaused = false;
   2259 
   2260  bool isCanceling = false;
   2261  {
   2262    MutexAutoLock lock(mMutex);
   2263 
   2264    isCanceling = mParentStatus >= Canceling;
   2265  }
   2266 
   2267  // Since the window is no longer paused, the queue should only remain paused
   2268  // if the worker is frozen.
   2269  //
   2270  // This can only fail if the ThrottledEventQueue cannot dispatch its executor
   2271  // to the main thread, in which case the main thread was never going to draw
   2272  // runnables from it anyway, so the failure doesn't matter.
   2273  (void)mMainThreadDebuggeeEventTarget->SetIsPaused(IsFrozen() && !isCanceling);
   2274 }
   2275 
   2276 void WorkerPrivate::PropagateStorageAccessPermissionGranted() {
   2277  AssertIsOnParentThread();
   2278 
   2279  {
   2280    MutexAutoLock lock(mMutex);
   2281 
   2282    if (mParentStatus >= Canceling) {
   2283      return;
   2284    }
   2285  }
   2286 
   2287  RefPtr<PropagateStorageAccessPermissionGrantedRunnable> runnable =
   2288      new PropagateStorageAccessPermissionGrantedRunnable(this);
   2289  (void)NS_WARN_IF(!runnable->Dispatch(this));
   2290 }
   2291 
   2292 void WorkerPrivate::NotifyStorageKeyUsed() {
   2293  AssertIsOnWorkerThread();
   2294 
   2295  // Only notify once per global.
   2296  if (hasNotifiedStorageKeyUsed) {
   2297    return;
   2298  }
   2299  hasNotifiedStorageKeyUsed = true;
   2300 
   2301  // Notify about storage access on the main thread.
   2302  RefPtr<StrongWorkerRef> strongRef =
   2303      StrongWorkerRef::Create(this, "WorkerPrivate::NotifyStorageKeyUsed");
   2304  if (!strongRef) {
   2305    return;
   2306  }
   2307  RefPtr<ThreadSafeWorkerRef> ref = new ThreadSafeWorkerRef(strongRef);
   2308  DispatchToMainThread(NS_NewRunnableFunction(
   2309      "WorkerPrivate::NotifyStorageKeyUsed", [ref = std::move(ref)] {
   2310        nsGlobalWindowInner* window =
   2311            nsGlobalWindowInner::Cast(ref->Private()->GetAncestorWindow());
   2312        if (window) {
   2313          window->MaybeNotifyStorageKeyUsed();
   2314        }
   2315      }));
   2316 }
   2317 
   2318 bool WorkerPrivate::Close() {
   2319  mMutex.AssertCurrentThreadOwns();
   2320  if (mParentStatus < Closing) {
   2321    mParentStatus = Closing;
   2322  }
   2323 
   2324  return true;
   2325 }
   2326 
   2327 bool WorkerPrivate::ProxyReleaseMainThreadObjects() {
   2328  AssertIsOnParentThread();
   2329  MOZ_ASSERT(!mMainThreadObjectsForgotten);
   2330 
   2331  nsCOMPtr<nsILoadGroup> loadGroupToCancel;
   2332  // If we're not overriden, then do nothing here.  Let the load group get
   2333  // handled in ForgetMainThreadObjects().
   2334  if (mLoadInfo.mInterfaceRequestor) {
   2335    mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
   2336  }
   2337 
   2338  bool result = mLoadInfo.ProxyReleaseMainThreadObjects(
   2339      this, std::move(loadGroupToCancel));
   2340 
   2341  mMainThreadObjectsForgotten = true;
   2342 
   2343  return result;
   2344 }
   2345 
   2346 void WorkerPrivate::UpdateContextOptions(
   2347    const JS::ContextOptions& aContextOptions) {
   2348  AssertIsOnParentThread();
   2349 
   2350  {
   2351    MutexAutoLock lock(mMutex);
   2352    mJSSettings.contextOptions = aContextOptions;
   2353  }
   2354 
   2355  RefPtr<UpdateContextOptionsRunnable> runnable =
   2356      new UpdateContextOptionsRunnable(this, aContextOptions);
   2357  if (!runnable->Dispatch(this)) {
   2358    NS_WARNING("Failed to update worker context options!");
   2359  }
   2360 }
   2361 
   2362 void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
   2363  AssertIsOnParentThread();
   2364 
   2365  RefPtr<UpdateLanguagesRunnable> runnable =
   2366      new UpdateLanguagesRunnable(this, aLanguages);
   2367  if (!runnable->Dispatch(this)) {
   2368    NS_WARNING("Failed to update worker languages!");
   2369  }
   2370 }
   2371 
   2372 void WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
   2373                                                  Maybe<uint32_t> aValue) {
   2374  AssertIsOnParentThread();
   2375 
   2376  bool changed = false;
   2377 
   2378  {
   2379    MutexAutoLock lock(mMutex);
   2380    changed = mJSSettings.ApplyGCSetting(aKey, aValue);
   2381  }
   2382 
   2383  if (changed) {
   2384    RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
   2385        new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
   2386    if (!runnable->Dispatch(this)) {
   2387      NS_WARNING("Failed to update memory parameter!");
   2388    }
   2389  }
   2390 }
   2391 
   2392 #ifdef JS_GC_ZEAL
   2393 void WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency) {
   2394  AssertIsOnParentThread();
   2395 
   2396  {
   2397    MutexAutoLock lock(mMutex);
   2398    mJSSettings.gcZeal = aGCZeal;
   2399    mJSSettings.gcZealFrequency = aFrequency;
   2400  }
   2401 
   2402  RefPtr<UpdateGCZealRunnable> runnable =
   2403      new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
   2404  if (!runnable->Dispatch(this)) {
   2405    NS_WARNING("Failed to update worker gczeal!");
   2406  }
   2407 }
   2408 #endif
   2409 
   2410 void WorkerPrivate::SetLowMemoryState(bool aState) {
   2411  AssertIsOnParentThread();
   2412 
   2413  RefPtr<SetLowMemoryStateRunnable> runnable =
   2414      new SetLowMemoryStateRunnable(this, aState);
   2415  if (!runnable->Dispatch(this)) {
   2416    NS_WARNING("Failed to set low memory state!");
   2417  }
   2418 }
   2419 
   2420 void WorkerPrivate::GarbageCollect(bool aShrinking) {
   2421  AssertIsOnParentThread();
   2422 
   2423  RefPtr<GarbageCollectRunnable> runnable = new GarbageCollectRunnable(
   2424      this, aShrinking, /* aCollectChildren = */ true);
   2425  if (!runnable->Dispatch(this)) {
   2426    NS_WARNING("Failed to GC worker!");
   2427  }
   2428 }
   2429 
   2430 void WorkerPrivate::CycleCollect() {
   2431  AssertIsOnParentThread();
   2432 
   2433  RefPtr<CycleCollectRunnable> runnable =
   2434      new CycleCollectRunnable(this, /* aCollectChildren = */ true);
   2435  if (!runnable->Dispatch(this)) {
   2436    NS_WARNING("Failed to CC worker!");
   2437  }
   2438 }
   2439 
   2440 void WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline) {
   2441  AssertIsOnParentThread();
   2442 
   2443  RefPtr<OfflineStatusChangeRunnable> runnable =
   2444      new OfflineStatusChangeRunnable(this, aIsOffline);
   2445  if (!runnable->Dispatch(this)) {
   2446    NS_WARNING("Failed to dispatch offline status change event!");
   2447  }
   2448 }
   2449 
   2450 void WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline) {
   2451  auto data = mWorkerThreadAccessible.Access();
   2452 
   2453  // The worker is already in this state. No need to dispatch an event.
   2454  if (data->mOnLine == !aIsOffline) {
   2455    return;
   2456  }
   2457 
   2458  if (ShouldResistFingerprinting(RFPTarget::NetworkConnection)) {
   2459    // We always report the worker as online if resistFingerprinting is
   2460    // enabled, regardless of the actual network status.
   2461    return;
   2462  }
   2463 
   2464  for (uint32_t index = 0; index < data->mChildWorkers.Length(); ++index) {
   2465    data->mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
   2466  }
   2467 
   2468  data->mOnLine = !aIsOffline;
   2469  WorkerGlobalScope* globalScope = GlobalScope();
   2470  RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
   2471  if (nav) {
   2472    nav->SetOnLine(data->mOnLine);
   2473  }
   2474 
   2475  nsString eventType;
   2476  if (aIsOffline) {
   2477    eventType.AssignLiteral("offline");
   2478  } else {
   2479    eventType.AssignLiteral("online");
   2480  }
   2481 
   2482  RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
   2483 
   2484  event->InitEvent(eventType, false, false);
   2485  event->SetTrusted(true);
   2486 
   2487  globalScope->DispatchEvent(*event);
   2488 }
   2489 
   2490 void WorkerPrivate::MemoryPressure() {
   2491  AssertIsOnParentThread();
   2492 
   2493  RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
   2494  (void)NS_WARN_IF(!runnable->Dispatch(this));
   2495 }
   2496 
   2497 RefPtr<WorkerPrivate::JSMemoryUsagePromise> WorkerPrivate::GetJSMemoryUsage() {
   2498  AssertIsOnMainThread();
   2499 
   2500  {
   2501    MutexAutoLock lock(mMutex);
   2502    // If we have started shutting down the worker, do not dispatch a runnable
   2503    // to measure its memory.
   2504    if (ParentStatus() > Running) {
   2505      return nullptr;
   2506    }
   2507  }
   2508 
   2509  return InvokeAsync(ControlEventTarget(), __func__, []() {
   2510    WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   2511    MOZ_ASSERT(wp);
   2512    wp->AssertIsOnWorkerThread();
   2513    MutexAutoLock lock(wp->mMutex);
   2514    return JSMemoryUsagePromise::CreateAndResolve(
   2515        js::GetGCHeapUsage(wp->mJSContext), __func__);
   2516  });
   2517 }
   2518 
   2519 void WorkerPrivate::WorkerScriptLoaded() {
   2520  AssertIsOnMainThread();
   2521 
   2522  if (IsSharedWorker() || IsServiceWorker()) {
   2523    // No longer need to hold references to the window or document we came from.
   2524    mLoadInfo.mWindow = nullptr;
   2525    mLoadInfo.mScriptContext = nullptr;
   2526  }
   2527 }
   2528 
   2529 void WorkerPrivate::SetBaseURI(nsIURI* aBaseURI) {
   2530  AssertIsOnMainThread();
   2531 
   2532  if (!mLoadInfo.mBaseURI) {
   2533    NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
   2534    mLoadInfo.mResolvedScriptURI = aBaseURI;
   2535  }
   2536 
   2537  mLoadInfo.mBaseURI = aBaseURI;
   2538 
   2539  if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
   2540    mLocationInfo.mHref.Truncate();
   2541  }
   2542 
   2543  mLocationInfo.mHostname.Truncate();
   2544  nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
   2545 
   2546  nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
   2547  if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
   2548    mLocationInfo.mPathname.Truncate();
   2549  }
   2550 
   2551  nsCString temp;
   2552 
   2553  if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
   2554    mLocationInfo.mSearch.Assign('?');
   2555    mLocationInfo.mSearch.Append(temp);
   2556  }
   2557 
   2558  if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
   2559    if (mLocationInfo.mHash.IsEmpty()) {
   2560      mLocationInfo.mHash.Assign('#');
   2561      mLocationInfo.mHash.Append(temp);
   2562    }
   2563  }
   2564 
   2565  if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
   2566    mLocationInfo.mProtocol.Append(':');
   2567  } else {
   2568    mLocationInfo.mProtocol.Truncate();
   2569  }
   2570 
   2571  int32_t port;
   2572  if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
   2573    mLocationInfo.mPort.AppendInt(port);
   2574 
   2575    nsAutoCString host(mLocationInfo.mHostname);
   2576    host.Append(':');
   2577    host.Append(mLocationInfo.mPort);
   2578 
   2579    mLocationInfo.mHost.Assign(host);
   2580  } else {
   2581    mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
   2582  }
   2583 
   2584  nsContentUtils::GetWebExposedOriginSerialization(aBaseURI,
   2585                                                   mLocationInfo.mOrigin);
   2586 }
   2587 
   2588 nsresult WorkerPrivate::SetPrincipalsAndCSPOnMainThread(
   2589    nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
   2590    nsILoadGroup* aLoadGroup, nsIContentSecurityPolicy* aCsp) {
   2591  return mLoadInfo.SetPrincipalsAndCSPOnMainThread(
   2592      aPrincipal, aPartitionedPrincipal, aLoadGroup, aCsp);
   2593 }
   2594 
   2595 nsresult WorkerPrivate::SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel) {
   2596  return mLoadInfo.SetPrincipalsAndCSPFromChannel(aChannel);
   2597 }
   2598 
   2599 bool WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel) {
   2600  return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
   2601 }
   2602 
   2603 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   2604 bool WorkerPrivate::PrincipalURIMatchesScriptURL() {
   2605  return mLoadInfo.PrincipalURIMatchesScriptURL();
   2606 }
   2607 #endif
   2608 
   2609 void WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup) {
   2610  AssertIsOnMainThread();
   2611 
   2612  // The load group should have been overriden at init time.
   2613  mLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aBaseLoadGroup);
   2614 }
   2615 
   2616 void WorkerPrivate::UpdateIsOnContentBlockingAllowList(
   2617    bool aOnContentBlockingAllowList) {
   2618  AssertIsOnWorkerThread();
   2619  MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
   2620 
   2621  RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create(
   2622      this, "WorkerPrivate::UpdateIsOnContentBlockingAllowList");
   2623  if (!strongRef) {
   2624    return;
   2625  }
   2626  RefPtr<ThreadSafeWorkerRef> ref = new ThreadSafeWorkerRef(strongRef);
   2627  DispatchToMainThread(NS_NewRunnableFunction(
   2628      "WorkerPrivate::UpdateIsOnContentBlockingAllowList",
   2629      [ref = std::move(ref), aOnContentBlockingAllowList] {
   2630        ref->Private()
   2631            ->mLoadInfo.mCookieJarSettingsArgs.isOnContentBlockingAllowList() =
   2632            aOnContentBlockingAllowList;
   2633 
   2634        nsCOMPtr<nsICookieJarSettings> workerCookieJarSettings;
   2635        net::CookieJarSettings::Deserialize(
   2636            ref->Private()->mLoadInfo.mCookieJarSettingsArgs,
   2637            getter_AddRefs(workerCookieJarSettings));
   2638        bool shouldResistFingerprinting =
   2639            nsContentUtils::ShouldResistFingerprinting_dangerous(
   2640                ref->Private()->mLoadInfo.mPrincipal,
   2641                "Service Workers exist outside a Document or Channel; as a "
   2642                "property of the domain (and origin attributes). We don't have "
   2643                "a "
   2644                "CookieJarSettings to perform the *nested check*, but we can "
   2645                "rely "
   2646                "on the FPI/dFPI partition key check. The WorkerPrivate's "
   2647                "ShouldResistFingerprinting function for the ServiceWorker "
   2648                "depends "
   2649                "on this boolean and will also consider an explicit RFPTarget.",
   2650                RFPTarget::IsAlwaysEnabledForPrecompute) &&
   2651            !nsContentUtils::ETPSaysShouldNotResistFingerprinting(
   2652                workerCookieJarSettings, false);
   2653 
   2654        ref->Private()
   2655            ->mLoadInfo.mCookieJarSettingsArgs.shouldResistFingerprinting() =
   2656            shouldResistFingerprinting;
   2657        ref->Private()->mLoadInfo.mShouldResistFingerprinting =
   2658            shouldResistFingerprinting;
   2659      }));
   2660 
   2661  /* From:
   2662    https://searchfox.org/mozilla-central/rev/964b8aa226c68bbf83c9ffc38984804734bb0de2/js/public/RealmOptions.h#316-318
   2663    > RealmCreationOptions specify fundamental realm characteristics that must
   2664    be specified when the realm is created, that can't be changed after the
   2665    realm is created.
   2666  */
   2667  /*
   2668  nsCString locale;
   2669  if (aEnabled) {
   2670    locale = nsRFPService::GetSpoofedJSLocale();
   2671  }
   2672 
   2673  MutexAutoLock lock(mMutex);
   2674  mJSSettings.chromeRealmOptions.creationOptions().setForceUTC(aEnabled);
   2675  mJSSettings.chromeRealmOptions.creationOptions().setAlwaysUseFdlibm(aEnabled);
   2676  if (aEnabled) {
   2677    mJSSettings.chromeRealmOptions.creationOptions().setLocaleCopyZ(
   2678        locale.get());
   2679  }
   2680 
   2681  mJSSettings.contentRealmOptions.creationOptions().setForceUTC(aEnabled);
   2682  mJSSettings.contentRealmOptions.creationOptions().setAlwaysUseFdlibm(
   2683      aEnabled);
   2684  if (aEnabled) {
   2685    mJSSettings.contentRealmOptions.creationOptions().setLocaleCopyZ(
   2686        locale.get());
   2687  }
   2688  */
   2689 }
   2690 
   2691 bool WorkerPrivate::IsOnParentThread() const {
   2692  if (GetParent()) {
   2693    return GetParent()->IsOnWorkerThread();
   2694  }
   2695  return NS_IsMainThread();
   2696 }
   2697 
   2698 #ifdef DEBUG
   2699 
   2700 void WorkerPrivate::AssertIsOnParentThread() const {
   2701  if (GetParent()) {
   2702    GetParent()->AssertIsOnWorkerThread();
   2703  } else {
   2704    AssertIsOnMainThread();
   2705  }
   2706 }
   2707 
   2708 void WorkerPrivate::AssertInnerWindowIsCorrect() const {
   2709  AssertIsOnParentThread();
   2710 
   2711  // Only care about top level workers from windows.
   2712  if (mParent || !mLoadInfo.mWindow) {
   2713    return;
   2714  }
   2715 
   2716  AssertIsOnMainThread();
   2717 
   2718  nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
   2719  NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
   2720               "Inner window no longer correct!");
   2721 }
   2722 
   2723 #endif
   2724 
   2725 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   2726 bool WorkerPrivate::PrincipalIsValid() const {
   2727  return mLoadInfo.PrincipalIsValid();
   2728 }
   2729 #endif
   2730 
   2731 WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible(
   2732    WorkerPrivate* const aParent)
   2733    : mNumWorkerRefsPreventingShutdownStart(0),
   2734      mDebuggerEventLoopLevel(0),
   2735      mNonblockingCCBackgroundActorCount(0),
   2736      mErrorHandlerRecursionCount(0),
   2737      mFrozen(false),
   2738      mDebuggerInterruptRequested(false),
   2739      mPeriodicGCTimerRunning(false),
   2740      mIdleGCTimerRunning(false),
   2741      mOnLine(aParent ? aParent->OnLine() : !NS_IsOffline()),
   2742      mJSThreadExecutionGranted(false),
   2743      mCCCollectedAnything(false) {}
   2744 
   2745 namespace {
   2746 
   2747 bool IsNewWorkerSecureContext(const WorkerPrivate* const aParent,
   2748                              const WorkerKind aWorkerKind,
   2749                              const WorkerLoadInfo& aLoadInfo) {
   2750  if (aParent) {
   2751    return aParent->IsSecureContext();
   2752  }
   2753 
   2754  // Our secure context state depends on the kind of worker we have.
   2755 
   2756  if (aLoadInfo.mPrincipal && aLoadInfo.mPrincipal->IsSystemPrincipal()) {
   2757    return true;
   2758  }
   2759 
   2760  if (aWorkerKind == WorkerKindService) {
   2761    return true;
   2762  }
   2763 
   2764  if (aLoadInfo.mSecureContext != WorkerLoadInfo::eNotSet) {
   2765    return aLoadInfo.mSecureContext == WorkerLoadInfo::eSecureContext;
   2766  }
   2767 
   2768  MOZ_ASSERT_UNREACHABLE(
   2769      "non-chrome worker that is not a service worker "
   2770      "that has no parent and no associated window");
   2771 
   2772  return false;
   2773 }
   2774 
   2775 }  // namespace
   2776 
   2777 WorkerPrivate::WorkerPrivate(
   2778    WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
   2779    WorkerKind aWorkerKind, RequestCredentials aRequestCredentials,
   2780    enum WorkerType aWorkerType, const nsAString& aWorkerName,
   2781    const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
   2782    nsString&& aId, const nsID& aAgentClusterId,
   2783    const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy,
   2784    CancellationCallback&& aCancellationCallback,
   2785    TerminationCallback&& aTerminationCallback,
   2786    mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&&
   2787        aChildEp)
   2788    : mMutex("WorkerPrivate Mutex"),
   2789      mCondVar(mMutex, "WorkerPrivate CondVar"),
   2790      mParent(aParent),
   2791      mScriptURL(aScriptURL),
   2792      mWorkerName(aWorkerName),
   2793      mCredentialsMode(aRequestCredentials),
   2794      mWorkerType(aWorkerType),  // If the worker runs as a script or a module
   2795      mWorkerKind(aWorkerKind),
   2796      mCancellationCallback(std::move(aCancellationCallback)),
   2797      mTerminationCallback(std::move(aTerminationCallback)),
   2798      mLoadInfo(std::move(aLoadInfo)),
   2799      mDebugger(nullptr),
   2800      mDispatchingControlRunnables(0),
   2801      mJSContext(nullptr),
   2802      mPRThread(nullptr),
   2803      mWorkerControlEventTarget(new WorkerEventTarget(
   2804          this, WorkerEventTarget::Behavior::ControlOnly)),
   2805      mWorkerHybridEventTarget(
   2806          new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
   2807      mChildEp(std::move(aChildEp)),
   2808      mRemoteDebuggerRegistered(false),
   2809      mRemoteDebuggerReady(true),
   2810      mIsQueued(false),
   2811      mDebuggerBindingCondVar(mMutex,
   2812                              "WorkerPrivate RemoteDebuggerBindingCondVar"),
   2813      mWorkerDebuggerEventTarget(new WorkerEventTarget(
   2814          this, WorkerEventTarget::Behavior::DebuggerOnly)),
   2815      mParentStatus(Pending),
   2816      mStatus(Pending),
   2817      mCreationTimeStamp(TimeStamp::Now()),
   2818      mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
   2819      mReportedUseCounters(false),
   2820      mAgentClusterId(aAgentClusterId),
   2821      mWorkerThreadAccessible(aParent),
   2822      mPostSyncLoopOperations(0),
   2823      mParentWindowPaused(false),
   2824      mWorkerScriptExecutedSuccessfully(false),
   2825      mFetchHandlerWasAdded(false),
   2826      mMainThreadObjectsForgotten(false),
   2827      mIsChromeWorker(aIsChromeWorker),
   2828      mParentFrozen(false),
   2829      mIsSecureContext(
   2830          IsNewWorkerSecureContext(mParent, mWorkerKind, mLoadInfo)),
   2831      mDebuggerRegistered(false),
   2832      mIsInBackground(false),
   2833      mDebuggerReady(true),
   2834      mExtensionAPIAllowed(false),
   2835      mIsInAutomation(false),
   2836      mId(std::move(aId)),
   2837      mAgentClusterOpenerPolicy(aAgentClusterOpenerPolicy),
   2838      mIsPrivilegedAddonGlobal(false),
   2839      mTopLevelWorkerFinishedRunnableCount(0),
   2840      mWorkerFinishedRunnableCount(0),
   2841      mFontVisibility(ComputeFontVisibility()) {
   2842  LOG(WorkerLog(), ("WorkerPrivate::WorkerPrivate [%p]", this));
   2843  MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
   2844 
   2845  if (aParent) {
   2846    aParent->AssertIsOnWorkerThread();
   2847 
   2848    // Note that this copies our parent's secure context state into mJSSettings.
   2849    aParent->CopyJSSettings(mJSSettings);
   2850 
   2851    MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
   2852 
   2853    mIsInAutomation = aParent->IsInAutomation();
   2854 
   2855    MOZ_ASSERT(IsDedicatedWorker());
   2856 
   2857    if (aParent->mParentFrozen) {
   2858      Freeze(nullptr);
   2859    }
   2860 
   2861    if (aParent->IsRunningInBackground()) {
   2862      mIsInBackground = true;
   2863    }
   2864    if (aParent->IsPlayingAudio()) {
   2865      mIsPlayingAudio = true;
   2866    }
   2867 
   2868    if (aParent->HasActivePeerConnections()) {
   2869      mHasActivePeerConnections = true;
   2870    }
   2871 
   2872    mIsPrivilegedAddonGlobal = aParent->mIsPrivilegedAddonGlobal;
   2873  } else {
   2874    AssertIsOnMainThread();
   2875 
   2876    RuntimeService::GetDefaultJSSettings(mJSSettings);
   2877 
   2878    {
   2879      JS::RealmOptions& chromeRealmOptions = mJSSettings.chromeRealmOptions;
   2880      JS::RealmOptions& contentRealmOptions = mJSSettings.contentRealmOptions;
   2881 
   2882      xpc::InitGlobalObjectOptions(
   2883          chromeRealmOptions, UsesSystemPrincipal(), mIsSecureContext,
   2884          ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
   2885          ShouldResistFingerprinting(RFPTarget::JSMathFdlibm),
   2886          ShouldResistFingerprinting(RFPTarget::JSLocale), ""_ns, u""_ns);
   2887      xpc::InitGlobalObjectOptions(
   2888          contentRealmOptions, UsesSystemPrincipal(), mIsSecureContext,
   2889          ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
   2890          ShouldResistFingerprinting(RFPTarget::JSMathFdlibm),
   2891          ShouldResistFingerprinting(RFPTarget::JSLocale), ""_ns, u""_ns);
   2892 
   2893      // Check if it's a privileged addon executing in order to allow access
   2894      // to SharedArrayBuffer
   2895      if (mLoadInfo.mPrincipal) {
   2896        if (auto* policy =
   2897                BasePrincipal::Cast(mLoadInfo.mPrincipal)->AddonPolicy()) {
   2898          if (policy->IsPrivileged() &&
   2899              ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
   2900            // Privileged extensions are allowed to use SharedArrayBuffer in
   2901            // their extension process, but never in content scripts in
   2902            // content processes.
   2903            mIsPrivilegedAddonGlobal = true;
   2904          }
   2905 
   2906          if (StaticPrefs::
   2907                  extensions_backgroundServiceWorker_enabled_AtStartup() &&
   2908              mWorkerKind == WorkerKindService &&
   2909              policy->IsManifestBackgroundWorker(mScriptURL)) {
   2910            // Only allows ExtensionAPI for extension service workers
   2911            // that are declared in the extension manifest json as
   2912            // the background service worker.
   2913            mExtensionAPIAllowed = true;
   2914          }
   2915        }
   2916      }
   2917 
   2918      // The SharedArrayBuffer global constructor property should not be present
   2919      // in a fresh global object when shared memory objects aren't allowed
   2920      // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
   2921      // act to isolate this worker to a separate process).
   2922      const bool defineSharedArrayBufferConstructor = IsSharedMemoryAllowed();
   2923      chromeRealmOptions.creationOptions()
   2924          .setDefineSharedArrayBufferConstructor(
   2925              defineSharedArrayBufferConstructor);
   2926      contentRealmOptions.creationOptions()
   2927          .setDefineSharedArrayBufferConstructor(
   2928              defineSharedArrayBufferConstructor);
   2929    }
   2930 
   2931    mIsInAutomation = xpc::IsInAutomation();
   2932 
   2933    // Our parent can get suspended after it initiates the async creation
   2934    // of a new worker thread.  In this case suspend the new worker as well.
   2935    if (mLoadInfo.mWindow &&
   2936        nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsSuspended()) {
   2937      ParentWindowPaused();
   2938    }
   2939 
   2940    if (mLoadInfo.mWindow &&
   2941        nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsFrozen()) {
   2942      Freeze(mLoadInfo.mWindow);
   2943    }
   2944 
   2945    if (mLoadInfo.mWindow && mLoadInfo.mWindow->GetOuterWindow() &&
   2946        mLoadInfo.mWindow->GetOuterWindow()->IsBackground()) {
   2947      mIsInBackground = true;
   2948    }
   2949 
   2950    if (mLoadInfo.mWindow &&
   2951        nsGlobalWindowInner::Cast(mLoadInfo.mWindow)->IsPlayingAudio()) {
   2952      SetIsPlayingAudio(true);
   2953    }
   2954 
   2955    if (mLoadInfo.mWindow && nsGlobalWindowInner::Cast(mLoadInfo.mWindow)
   2956                                 ->HasActivePeerConnections()) {
   2957      SetActivePeerConnections(true);
   2958    }
   2959  }
   2960 
   2961  nsCOMPtr<nsISerialEventTarget> target;
   2962 
   2963  // A child worker just inherits the parent workers ThrottledEventQueue
   2964  // and main thread target for now.  This is mainly due to the restriction
   2965  // that ThrottledEventQueue can only be created on the main thread at the
   2966  // moment.
   2967  if (aParent) {
   2968    mMainThreadEventTargetForMessaging =
   2969        aParent->mMainThreadEventTargetForMessaging;
   2970    mMainThreadEventTarget = aParent->mMainThreadEventTarget;
   2971    mMainThreadDebuggeeEventTarget = aParent->mMainThreadDebuggeeEventTarget;
   2972    return;
   2973  }
   2974 
   2975  MOZ_ASSERT(NS_IsMainThread());
   2976  target = GetWindow()
   2977               ? GetWindow()->GetBrowsingContextGroup()->GetWorkerEventQueue()
   2978               : nullptr;
   2979 
   2980  if (!target) {
   2981    target = GetMainThreadSerialEventTarget();
   2982    MOZ_DIAGNOSTIC_ASSERT(target);
   2983  }
   2984 
   2985  // Throttle events to the main thread using a ThrottledEventQueue specific to
   2986  // this tree of worker threads.
   2987  mMainThreadEventTargetForMessaging =
   2988      ThrottledEventQueue::Create(target, "Worker queue for messaging");
   2989  mMainThreadEventTarget = ThrottledEventQueue::Create(
   2990      GetMainThreadSerialEventTarget(), "Worker queue",
   2991      nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
   2992  mMainThreadDebuggeeEventTarget =
   2993      ThrottledEventQueue::Create(target, "Worker debuggee queue");
   2994  if (IsParentWindowPaused() || IsFrozen()) {
   2995    MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
   2996  }
   2997 }
   2998 
   2999 WorkerPrivate::~WorkerPrivate() {
   3000  MOZ_DIAGNOSTIC_ASSERT(mTopLevelWorkerFinishedRunnableCount == 0);
   3001  MOZ_DIAGNOSTIC_ASSERT(mWorkerFinishedRunnableCount == 0);
   3002  MOZ_DIAGNOSTIC_ASSERT(mPendingJSAsyncTasks.empty());
   3003 
   3004  mWorkerDebuggerEventTarget->ForgetWorkerPrivate(this);
   3005 
   3006  mWorkerControlEventTarget->ForgetWorkerPrivate(this);
   3007 
   3008  // We force the hybrid event target to forget the thread when we
   3009  // enter the Killing state, but we do it again here to be safe.
   3010  // Its possible that we may be created and destroyed without progressing
   3011  // to Killing via some obscure code path.
   3012  mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
   3013 }
   3014 
   3015 WorkerPrivate::AgentClusterIdAndCoop
   3016 WorkerPrivate::ComputeAgentClusterIdAndCoop(WorkerPrivate* aParent,
   3017                                            WorkerKind aWorkerKind,
   3018                                            WorkerLoadInfo* aLoadInfo,
   3019                                            bool aIsChromeWorker) {
   3020  nsILoadInfo::CrossOriginOpenerPolicy agentClusterCoop =
   3021      nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
   3022 
   3023  if (aParent) {
   3024    MOZ_ASSERT(aWorkerKind == WorkerKind::WorkerKindDedicated);
   3025 
   3026    return {aParent->AgentClusterId(), aParent->mAgentClusterOpenerPolicy};
   3027  }
   3028 
   3029  AssertIsOnMainThread();
   3030 
   3031  if (aWorkerKind == WorkerKind::WorkerKindService ||
   3032      aWorkerKind == WorkerKind::WorkerKindShared) {
   3033    return {aLoadInfo->mAgentClusterId, agentClusterCoop};
   3034  }
   3035 
   3036  if (aLoadInfo->mWindow) {
   3037    Document* doc = aLoadInfo->mWindow->GetExtantDoc();
   3038    MOZ_DIAGNOSTIC_ASSERT(doc);
   3039    RefPtr<DocGroup> docGroup = doc->GetDocGroup();
   3040 
   3041    nsID agentClusterId =
   3042        docGroup ? docGroup->AgentClusterId() : nsID::GenerateUUID();
   3043 
   3044    BrowsingContext* bc = aLoadInfo->mWindow->GetBrowsingContext();
   3045    MOZ_DIAGNOSTIC_ASSERT(bc);
   3046    return {agentClusterId, bc->Top()->GetOpenerPolicy()};
   3047  }
   3048 
   3049  // Chrome workers share an AgentCluster with the XPC module global. This
   3050  // allows things like shared memory and WASM modules to be transferred between
   3051  // chrome workers and system JS.
   3052  // Also set COOP+COEP flags to allow access to shared memory.
   3053  if (aIsChromeWorker) {
   3054    if (nsIGlobalObject* systemGlobal =
   3055            xpc::NativeGlobal(xpc::PrivilegedJunkScope())) {
   3056      nsID agentClusterId = systemGlobal->GetAgentClusterId().valueOrFrom(
   3057          [] { return nsID::GenerateUUID(); });
   3058      return {
   3059          agentClusterId,
   3060          nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP};
   3061    }
   3062  }
   3063 
   3064  // If the window object was failed to be set into the WorkerLoadInfo, we
   3065  // make the worker into another agent cluster group instead of failures.
   3066  return {nsID::GenerateUUID(), agentClusterCoop};
   3067 }
   3068 
   3069 // static
   3070 already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
   3071    JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
   3072    WorkerKind aWorkerKind, RequestCredentials aRequestCredentials,
   3073    enum WorkerType aWorkerType, const nsAString& aWorkerName,
   3074    const nsACString& aServiceWorkerScope, WorkerLoadInfo* aLoadInfo,
   3075    ErrorResult& aRv, nsString aId,
   3076    CancellationCallback&& aCancellationCallback,
   3077    TerminationCallback&& aTerminationCallback,
   3078    mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&&
   3079        aChildEp) {
   3080  WorkerPrivate* parent =
   3081      NS_IsMainThread() ? nullptr : GetCurrentThreadWorkerPrivate();
   3082 
   3083  // If this is a sub-worker, we need to keep the parent worker alive until this
   3084  // one is registered.
   3085  RefPtr<StrongWorkerRef> workerRef;
   3086  if (parent) {
   3087    parent->AssertIsOnWorkerThread();
   3088 
   3089    workerRef = StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor");
   3090    if (NS_WARN_IF(!workerRef)) {
   3091      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   3092      return nullptr;
   3093    }
   3094  } else {
   3095    AssertIsOnMainThread();
   3096  }
   3097 
   3098  Maybe<WorkerLoadInfo> stackLoadInfo;
   3099  if (!aLoadInfo) {
   3100    stackLoadInfo.emplace();
   3101 
   3102    nsresult rv = GetLoadInfo(
   3103        aCx, nullptr, parent, aScriptURL, aWorkerType, aRequestCredentials,
   3104        aIsChromeWorker, InheritLoadGroup, aWorkerKind, stackLoadInfo.ptr());
   3105    aRv.MightThrowJSException();
   3106    if (NS_FAILED(rv)) {
   3107      workerinternals::ReportLoadError(aRv, rv, aScriptURL);
   3108      return nullptr;
   3109    }
   3110 
   3111    aLoadInfo = stackLoadInfo.ptr();
   3112  }
   3113 
   3114  // NB: This has to be done before creating the WorkerPrivate, because it will
   3115  // attempt to use static variables that are initialized in the RuntimeService
   3116  // constructor.
   3117  RuntimeService* runtimeService;
   3118 
   3119  if (!parent) {
   3120    runtimeService = RuntimeService::GetOrCreateService();
   3121    if (!runtimeService) {
   3122      aRv.Throw(NS_ERROR_FAILURE);
   3123      return nullptr;
   3124    }
   3125  } else {
   3126    runtimeService = RuntimeService::GetService();
   3127  }
   3128 
   3129  MOZ_ASSERT(runtimeService);
   3130 
   3131  // Don't create a worker with the shutting down RuntimeService.
   3132  if (runtimeService->IsShuttingDown()) {
   3133    aRv.Throw(NS_ERROR_UNEXPECTED);
   3134    return nullptr;
   3135  }
   3136 
   3137  AgentClusterIdAndCoop idAndCoop = ComputeAgentClusterIdAndCoop(
   3138      parent, aWorkerKind, aLoadInfo, aIsChromeWorker);
   3139 
   3140  RefPtr<WorkerPrivate> worker = new WorkerPrivate(
   3141      parent, aScriptURL, aIsChromeWorker, aWorkerKind, aRequestCredentials,
   3142      aWorkerType, aWorkerName, aServiceWorkerScope, *aLoadInfo, std::move(aId),
   3143      idAndCoop.mId, idAndCoop.mCoop, std::move(aCancellationCallback),
   3144      std::move(aTerminationCallback), std::move(aChildEp));
   3145 
   3146  // Gecko contexts always have an explicitly-set default locale (set by
   3147  // XPJSRuntime::Initialize for the main thread, set by
   3148  // WorkerThreadPrimaryRunnable::Run for workers just before running worker
   3149  // code), so this is never SpiderMonkey's builtin default locale.
   3150  JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
   3151  if (NS_WARN_IF(!defaultLocale)) {
   3152    aRv.Throw(NS_ERROR_UNEXPECTED);
   3153    return nullptr;
   3154  }
   3155 
   3156  worker->mDefaultLocale = std::move(defaultLocale);
   3157 
   3158  // Create remote debugger endpoint here for child binding in
   3159  // WorkerThreadPrimaryRunnable
   3160  // worker->CreateRemoteDebuggerEndpoints();
   3161 
   3162  if (!runtimeService->RegisterWorker(*worker)) {
   3163    aRv.Throw(NS_ERROR_UNEXPECTED);
   3164    return nullptr;
   3165  }
   3166 
   3167  // From this point on (worker thread has been started) we
   3168  // must keep ourself alive. We can now only be cleared by
   3169  // ClearSelfAndParentEventTargetRef().
   3170  worker->mSelfRef = worker;
   3171  worker->mParentRef = MakeRefPtr<WorkerParentRef>(worker);
   3172 
   3173  // Enable remote worker debugger when the worker is really scheduled.
   3174  /*
   3175  if (!worker->mIsQueued) {
   3176    worker->EnableRemoteDebugger();
   3177  }
   3178  */
   3179 
   3180  worker->EnableDebugger();
   3181 
   3182  MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
   3183 
   3184  UniquePtr<SerializedStackHolder> stack;
   3185  if (worker->IsWatchedByDevTools()) {
   3186    stack = GetCurrentStackForNetMonitor(aCx);
   3187  }
   3188 
   3189  // This should be non-null for dedicated workers and null for Shared and
   3190  // Service workers. All Encoding values are static and will live as long
   3191  // as the process and the convention is to therefore use raw pointers.
   3192  const mozilla::Encoding* aDocumentEncoding =
   3193      NS_IsMainThread() && !worker->GetParent() && worker->GetDocument()
   3194          ? worker->GetDocument()->GetDocumentCharacterSet().get()
   3195          : nullptr;
   3196 
   3197  RefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(
   3198      worker, std::move(stack), aScriptURL, aDocumentEncoding);
   3199  if (!compiler->Dispatch(worker)) {
   3200    aRv.Throw(NS_ERROR_UNEXPECTED);
   3201    return nullptr;
   3202  }
   3203 
   3204  return worker.forget();
   3205 }
   3206 
   3207 // Mark worker private as running in the background tab
   3208 // for further throttling
   3209 void WorkerPrivate::SetIsRunningInBackground() {
   3210  AssertIsOnParentThread();
   3211 
   3212  RefPtr<ChangeBackgroundStateRunnable> runnable =
   3213      new ChangeBackgroundStateRunnable(this, true);
   3214  runnable->Dispatch(this);
   3215 
   3216  LOG(WorkerLog(), ("SetIsRunningInBackground [%p]", this));
   3217 }
   3218 
   3219 void WorkerPrivate::SetIsRunningInForeground() {
   3220  AssertIsOnParentThread();
   3221 
   3222  RefPtr<ChangeBackgroundStateRunnable> runnable =
   3223      new ChangeBackgroundStateRunnable(this, false);
   3224  runnable->Dispatch(this);
   3225 
   3226  LOG(WorkerLog(), ("SetIsRunningInForeground [%p]", this));
   3227 }
   3228 
   3229 void WorkerPrivate::SetIsPlayingAudio(bool aIsPlayingAudio) {
   3230  AssertIsOnParentThread();
   3231 
   3232  RefPtr<ChangePlaybackStateRunnable> runnable =
   3233      new ChangePlaybackStateRunnable(this, aIsPlayingAudio);
   3234  runnable->Dispatch(this);
   3235 
   3236  AUTO_PROFILER_MARKER_UNTYPED("WorkerPrivate::SetIsPlayingAudio", DOM, {});
   3237  LOG(WorkerLog(), ("SetIsPlayingAudio [%p]", this));
   3238 }
   3239 
   3240 void WorkerPrivate::SetActivePeerConnections(bool aHasPeerConnections) {
   3241  AssertIsOnParentThread();
   3242 
   3243  RefPtr<ChangeActivePeerConnectionsRunnable> runnable =
   3244      new ChangeActivePeerConnectionsRunnable(this, aHasPeerConnections);
   3245  runnable->Dispatch(this);
   3246 
   3247  AUTO_PROFILER_MARKER_UNTYPED("WorkerPrivate::SetActivePeerConnections", DOM,
   3248                               {});
   3249  LOG(WorkerLog(), ("SetActivePeerConnections [%p]", this));
   3250 }
   3251 
   3252 nsresult WorkerPrivate::SetIsDebuggerReady(bool aReady) {
   3253  AssertIsOnMainThread();
   3254  MutexAutoLock lock(mMutex);
   3255 
   3256  if (mDebuggerReady == aReady) {
   3257    return NS_OK;
   3258  }
   3259 
   3260  if (!aReady && mDebuggerRegistered) {
   3261    // The debugger can only be marked as not ready during registration.
   3262    return NS_ERROR_FAILURE;
   3263  }
   3264 
   3265  mDebuggerReady = aReady;
   3266 
   3267  bool debuggerRegistered = mDebuggerRegistered && (mRemoteDebuggerRegistered ||
   3268                                                    XRE_IsParentProcess());
   3269 
   3270  if (aReady && debuggerRegistered) {
   3271    // Dispatch all the delayed runnables without releasing the lock, to ensure
   3272    // that the order in which debuggee runnables execute is the same as the
   3273    // order in which they were originally dispatched.
   3274    auto pending = std::move(mDelayedDebuggeeRunnables);
   3275    for (uint32_t i = 0; i < pending.Length(); i++) {
   3276      RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
   3277      nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock);
   3278      NS_ENSURE_SUCCESS(rv, rv);
   3279    }
   3280    MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
   3281  }
   3282 
   3283  return NS_OK;
   3284 }
   3285 
   3286 // static
   3287 nsresult WorkerPrivate::GetLoadInfo(
   3288    JSContext* aCx, nsPIDOMWindowInner* aWindow, WorkerPrivate* aParent,
   3289    const nsAString& aScriptURL, const enum WorkerType& aWorkerType,
   3290    const RequestCredentials& aCredentials, bool aIsChromeWorker,
   3291    LoadGroupBehavior aLoadGroupBehavior, WorkerKind aWorkerKind,
   3292    WorkerLoadInfo* aLoadInfo) {
   3293  using namespace mozilla::dom::workerinternals;
   3294 
   3295  MOZ_ASSERT(aCx);
   3296  MOZ_ASSERT_IF(NS_IsMainThread(),
   3297                aCx == nsContentUtils::GetCurrentJSContext());
   3298 
   3299  if (aWindow) {
   3300    AssertIsOnMainThread();
   3301  }
   3302 
   3303  WorkerLoadInfo loadInfo;
   3304  nsresult rv;
   3305 
   3306  if (aParent) {
   3307    aParent->AssertIsOnWorkerThread();
   3308 
   3309    // If the parent is going away give up now.
   3310    WorkerStatus parentStatus;
   3311    {
   3312      MutexAutoLock lock(aParent->mMutex);
   3313      parentStatus = aParent->mStatus;
   3314    }
   3315 
   3316    if (parentStatus > Running) {
   3317      return NS_ERROR_FAILURE;
   3318    }
   3319 
   3320    // Passing a pointer to our stack loadInfo is safe here because this
   3321    // method uses a sync runnable to get the channel from the main thread.
   3322    rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, aWorkerType,
   3323                                          aCredentials, loadInfo);
   3324    if (NS_FAILED(rv)) {
   3325      MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
   3326      return rv;
   3327    }
   3328 
   3329    // Now that we've spun the loop there's no guarantee that our parent is
   3330    // still alive.  We may have received control messages initiating shutdown.
   3331    {
   3332      MutexAutoLock lock(aParent->mMutex);
   3333      parentStatus = aParent->mStatus;
   3334    }
   3335 
   3336    if (parentStatus > Running) {
   3337      MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
   3338      return NS_ERROR_FAILURE;
   3339    }
   3340 
   3341    loadInfo.mTrials = aParent->Trials();
   3342    loadInfo.mDomain = aParent->Domain();
   3343    loadInfo.mFromWindow = aParent->IsFromWindow();
   3344    loadInfo.mWindowID = aParent->WindowID();
   3345    loadInfo.mAssociatedBrowsingContextID =
   3346        aParent->AssociatedBrowsingContextID();
   3347    loadInfo.mStorageAccess = aParent->StorageAccess();
   3348    loadInfo.mUseRegularPrincipal = aParent->UseRegularPrincipal();
   3349    loadInfo.mUsingStorageAccess = aParent->UsingStorageAccess();
   3350    loadInfo.mCookieJarSettings = aParent->CookieJarSettings();
   3351    if (loadInfo.mCookieJarSettings) {
   3352      loadInfo.mCookieJarSettingsArgs = aParent->CookieJarSettingsArgs();
   3353    }
   3354    loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
   3355    loadInfo.mServiceWorkersTestingInWindow =
   3356        aParent->ServiceWorkersTestingInWindow();
   3357    loadInfo.mIsThirdPartyContext = aParent->IsThirdPartyContext();
   3358    loadInfo.mShouldResistFingerprinting = aParent->ShouldResistFingerprinting(
   3359        RFPTarget::IsAlwaysEnabledForPrecompute);
   3360    loadInfo.mOverriddenFingerprintingSettings =
   3361        aParent->GetOverriddenFingerprintingSettings();
   3362    loadInfo.mParentController = aParent->GlobalScope()->GetController();
   3363    loadInfo.mWatchedByDevTools = aParent->IsWatchedByDevTools();
   3364    loadInfo.mIsOn3PCBExceptionList = aParent->IsOn3PCBExceptionList();
   3365  } else {
   3366    AssertIsOnMainThread();
   3367 
   3368    // Make sure that the IndexedDatabaseManager is set up
   3369    IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate();
   3370    if (idm) {
   3371      (void)NS_WARN_IF(NS_FAILED(idm->EnsureLocale()));
   3372    } else {
   3373      NS_WARNING("Failed to get IndexedDatabaseManager!");
   3374    }
   3375 
   3376    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   3377    MOZ_ASSERT(ssm);
   3378 
   3379    bool isChrome = nsContentUtils::IsSystemCaller(aCx);
   3380 
   3381    // First check to make sure the caller has permission to make a privileged
   3382    // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
   3383    if (aIsChromeWorker && !isChrome) {
   3384      return NS_ERROR_DOM_SECURITY_ERR;
   3385    }
   3386 
   3387    // Chrome callers (whether creating a ChromeWorker or Worker) always get the
   3388    // system principal here as they're allowed to load anything. The script
   3389    // loader will refuse to run any script that does not also have the system
   3390    // principal.
   3391    if (isChrome) {
   3392      rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
   3393      NS_ENSURE_SUCCESS(rv, rv);
   3394    }
   3395 
   3396    // See if we're being called from a window.
   3397    nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
   3398    if (!globalWindow) {
   3399      globalWindow = xpc::CurrentWindowOrNull(aCx);
   3400    }
   3401 
   3402    nsCOMPtr<Document> document;
   3403    Maybe<ClientInfo> clientInfo;
   3404 
   3405    if (globalWindow) {
   3406      // Only use the current inner window, and only use it if the caller can
   3407      // access it.
   3408      if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
   3409        loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
   3410      }
   3411 
   3412      loadInfo.mTrials =
   3413          OriginTrials::FromWindow(nsGlobalWindowInner::Cast(loadInfo.mWindow));
   3414 
   3415      BrowsingContext* browsingContext = globalWindow->GetBrowsingContext();
   3416 
   3417      // TODO: fix this for SharedWorkers with multiple documents (bug
   3418      // 1177935)
   3419      loadInfo.mServiceWorkersTestingInWindow =
   3420          browsingContext &&
   3421          browsingContext->Top()->ServiceWorkersTestingEnabled();
   3422 
   3423      if (!loadInfo.mWindow ||
   3424          (globalWindow != loadInfo.mWindow &&
   3425           !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
   3426        return NS_ERROR_DOM_SECURITY_ERR;
   3427      }
   3428 
   3429      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
   3430      MOZ_ASSERT(sgo);
   3431 
   3432      loadInfo.mScriptContext = sgo->GetContext();
   3433      NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
   3434 
   3435      // If we're called from a window then we can dig out the principal and URI
   3436      // from the document.
   3437      document = loadInfo.mWindow->GetExtantDoc();
   3438      NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
   3439 
   3440      loadInfo.mBaseURI = document->GetDocBaseURI();
   3441      loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
   3442      NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
   3443 
   3444      clientInfo = globalWindow->GetClientInfo();
   3445 
   3446      // Use the document's NodePrincipal as loading principal if we're not
   3447      // being called from chrome.
   3448      if (!loadInfo.mLoadingPrincipal) {
   3449        loadInfo.mLoadingPrincipal = document->NodePrincipal();
   3450        NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
   3451 
   3452        // We use the document's base domain to limit the number of workers
   3453        // each domain can create. For sandboxed documents, we use the domain
   3454        // of their first non-sandboxed document, walking up until we find
   3455        // one. If we can't find one, we fall back to using the GUID of the
   3456        // null principal as the base domain.
   3457        if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
   3458          nsCOMPtr<Document> tmpDoc = document;
   3459          do {
   3460            tmpDoc = tmpDoc->GetInProcessParentDocument();
   3461          } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
   3462 
   3463          if (tmpDoc) {
   3464            // There was an unsandboxed ancestor, yay!
   3465            nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
   3466            rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
   3467            NS_ENSURE_SUCCESS(rv, rv);
   3468          } else {
   3469            // No unsandboxed ancestor, use our GUID.
   3470            rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
   3471            NS_ENSURE_SUCCESS(rv, rv);
   3472          }
   3473        } else {
   3474          // Document creating the worker is not sandboxed.
   3475          rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
   3476          NS_ENSURE_SUCCESS(rv, rv);
   3477        }
   3478      }
   3479 
   3480      NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
   3481                                                  loadInfo.mLoadingPrincipal),
   3482                     NS_ERROR_FAILURE);
   3483 
   3484      nsCOMPtr<nsIPermissionManager> permMgr =
   3485          do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
   3486      NS_ENSURE_SUCCESS(rv, rv);
   3487 
   3488      uint32_t perm;
   3489      rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal,
   3490                                                "systemXHR"_ns, &perm);
   3491      NS_ENSURE_SUCCESS(rv, rv);
   3492 
   3493      loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
   3494 
   3495      loadInfo.mWatchedByDevTools =
   3496          browsingContext && browsingContext->WatchedByDevTools();
   3497 
   3498      loadInfo.mReferrerInfo =
   3499          ReferrerInfo::CreateForFetch(loadInfo.mLoadingPrincipal, document);
   3500      loadInfo.mFromWindow = true;
   3501      loadInfo.mWindowID = globalWindow->WindowID();
   3502      loadInfo.mAssociatedBrowsingContextID =
   3503          globalWindow->GetBrowsingContext()->Id();
   3504      loadInfo.mStorageAccess = StorageAllowedForWindow(globalWindow);
   3505      loadInfo.mUseRegularPrincipal = document->UseRegularPrincipal();
   3506      loadInfo.mUsingStorageAccess = document->UsingStorageAccess();
   3507      loadInfo.mShouldResistFingerprinting =
   3508          document->ShouldResistFingerprinting(
   3509              RFPTarget::IsAlwaysEnabledForPrecompute);
   3510      loadInfo.mOverriddenFingerprintingSettings =
   3511          document->GetOverriddenFingerprintingSettings();
   3512      loadInfo.mIsOn3PCBExceptionList = document->IsOn3PCBExceptionList();
   3513 
   3514      // This is an hack to deny the storage-access-permission for workers of
   3515      // sub-iframes.
   3516      if (loadInfo.mUsingStorageAccess &&
   3517          StorageAllowedForDocument(document) != StorageAccess::eAllow) {
   3518        loadInfo.mUsingStorageAccess = false;
   3519      }
   3520      loadInfo.mIsThirdPartyContext =
   3521          AntiTrackingUtils::IsThirdPartyWindow(globalWindow, nullptr);
   3522      loadInfo.mCookieJarSettings = document->CookieJarSettings();
   3523      if (loadInfo.mCookieJarSettings) {
   3524        auto* cookieJarSettings =
   3525            net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings);
   3526        cookieJarSettings->Serialize(loadInfo.mCookieJarSettingsArgs);
   3527      }
   3528      StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
   3529          document, loadInfo.mOriginAttributes);
   3530      loadInfo.mParentController = globalWindow->GetController();
   3531      loadInfo.mSecureContext = loadInfo.mWindow->IsSecureContext()
   3532                                    ? WorkerLoadInfo::eSecureContext
   3533                                    : WorkerLoadInfo::eInsecureContext;
   3534    } else {
   3535      // Not a window
   3536      MOZ_ASSERT(isChrome);
   3537 
   3538      // We're being created outside of a window. Need to figure out the script
   3539      // that is creating us in order for us to use relative URIs later on.
   3540      JS::AutoFilename fileName;
   3541      if (JS::DescribeScriptedCaller(&fileName, aCx)) {
   3542        // In most cases, fileName is URI. In a few other cases
   3543        // (e.g. xpcshell), fileName is a file path. Ideally, we would
   3544        // prefer testing whether fileName parses as an URI and fallback
   3545        // to file path in case of error, but Windows file paths have
   3546        // the interesting property that they can be parsed as bogus
   3547        // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
   3548        // hostname "Windows", path "Tmp"), which defeats this algorithm.
   3549        // Therefore, we adopt the opposite convention.
   3550        nsCOMPtr<nsIFile> scriptFile;
   3551        rv = NS_NewNativeLocalFile(nsDependentCString(fileName.get()),
   3552                                   getter_AddRefs(scriptFile));
   3553        if (NS_SUCCEEDED(rv)) {
   3554          rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), scriptFile);
   3555        }
   3556        if (NS_FAILED(rv)) {
   3557          // As expected, fileName is not a path, so proceed with
   3558          // a uri.
   3559          rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), fileName.get());
   3560        }
   3561        if (NS_FAILED(rv)) {
   3562          return rv;
   3563        }
   3564      }
   3565      loadInfo.mXHRParamsAllowed = true;
   3566      loadInfo.mFromWindow = false;
   3567      loadInfo.mWindowID = UINT64_MAX;
   3568      loadInfo.mStorageAccess = StorageAccess::eAllow;
   3569      loadInfo.mUseRegularPrincipal = true;
   3570      loadInfo.mUsingStorageAccess = false;
   3571      loadInfo.mCookieJarSettings =
   3572          mozilla::net::CookieJarSettings::Create(loadInfo.mLoadingPrincipal);
   3573      loadInfo.mShouldResistFingerprinting =
   3574          nsContentUtils::ShouldResistFingerprinting_dangerous(
   3575              loadInfo.mLoadingPrincipal,
   3576              "Unusual situation - we have no document or CookieJarSettings",
   3577              RFPTarget::IsAlwaysEnabledForPrecompute);
   3578      MOZ_ASSERT(loadInfo.mCookieJarSettings);
   3579      auto* cookieJarSettings =
   3580          net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings);
   3581      cookieJarSettings->Serialize(loadInfo.mCookieJarSettingsArgs);
   3582 
   3583      loadInfo.mOriginAttributes = OriginAttributes();
   3584      loadInfo.mIsThirdPartyContext = false;
   3585      loadInfo.mIsOn3PCBExceptionList = false;
   3586    }
   3587 
   3588    MOZ_ASSERT(loadInfo.mLoadingPrincipal);
   3589    MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
   3590 
   3591    if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
   3592      OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
   3593    }
   3594    MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
   3595                                            loadInfo.mLoadingPrincipal));
   3596 
   3597    // Top level workers' main script use the document charset for the script
   3598    // uri encoding.
   3599    nsCOMPtr<nsIURI> url;
   3600    rv = nsContentUtils::NewURIWithDocumentCharset(
   3601        getter_AddRefs(url), aScriptURL, document, loadInfo.mBaseURI);
   3602    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   3603 
   3604    rv = ChannelFromScriptURLMainThread(
   3605        loadInfo.mLoadingPrincipal, document, loadInfo.mLoadGroup, url,
   3606        aWorkerType, aCredentials, clientInfo, ContentPolicyType(aWorkerKind),
   3607        loadInfo.mCookieJarSettings, loadInfo.mReferrerInfo,
   3608        getter_AddRefs(loadInfo.mChannel));
   3609    NS_ENSURE_SUCCESS(rv, rv);
   3610 
   3611    rv = NS_GetFinalChannelURI(loadInfo.mChannel,
   3612                               getter_AddRefs(loadInfo.mResolvedScriptURI));
   3613    NS_ENSURE_SUCCESS(rv, rv);
   3614 
   3615    // We need the correct hasStoragePermission flag for the channel here since
   3616    // we will do a content blocking check later when we set the storage
   3617    // principal for the worker. The channel here won't be opened when we do the
   3618    // check later, so the hasStoragePermission flag is incorrect. To address
   3619    // this, We copy the hasStoragePermission flag from the document if there is
   3620    // a window. The worker is created as the same origin of the window. So, the
   3621    // worker is supposed to have the same storage permission as the window as
   3622    // well as the hasStoragePermission flag.
   3623    nsCOMPtr<nsILoadInfo> channelLoadInfo = loadInfo.mChannel->LoadInfo();
   3624    rv = channelLoadInfo->SetStoragePermission(
   3625        loadInfo.mUsingStorageAccess ? nsILoadInfo::HasStoragePermission
   3626                                     : nsILoadInfo::NoStoragePermission);
   3627    NS_ENSURE_SUCCESS(rv, rv);
   3628 
   3629    rv = loadInfo.SetPrincipalsAndCSPFromChannel(loadInfo.mChannel);
   3630    NS_ENSURE_SUCCESS(rv, rv);
   3631  }
   3632 
   3633  MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
   3634  MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
   3635 
   3636  *aLoadInfo = std::move(loadInfo);
   3637  return NS_OK;
   3638 }
   3639 
   3640 // static
   3641 void WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
   3642                                              nsIPrincipal* aPrincipal) {
   3643  MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
   3644  MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal);
   3645 
   3646  aLoadInfo.mInterfaceRequestor =
   3647      new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
   3648  aLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aLoadInfo.mLoadGroup);
   3649 
   3650  // NOTE: this defaults the load context to:
   3651  //  - private browsing = false
   3652  //  - content = true
   3653  //  - use remote tabs = false
   3654  nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   3655 
   3656  nsresult rv =
   3657      loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
   3658  MOZ_ALWAYS_SUCCEEDS(rv);
   3659 
   3660  aLoadInfo.mLoadGroup = std::move(loadGroup);
   3661 
   3662  MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
   3663 }
   3664 
   3665 void WorkerPrivate::RunLoopNeverRan() {
   3666  LOG(WorkerLog(), ("WorkerPrivate::RunLoopNeverRan [%p]", this));
   3667  // RunLoopNeverRan is only called in WorkerThreadPrimaryRunnable::Run() to
   3668  // handle cases
   3669  //   1. Fail to get BackgroundChild for the worker thread or
   3670  //   2. Fail to initialize the worker's JS context
   3671  // However, mPreStartRunnables had already dispatched in
   3672  // WorkerThread::SetWorkerPrivateInWorkerThread() where beforing above jobs
   3673  // start. So we need to clean up these dispatched runnables for the worker
   3674  // thread.
   3675 
   3676  auto data = mWorkerThreadAccessible.Access();
   3677  RefPtr<WorkerThread> thread;
   3678  {
   3679    MutexAutoLock lock(mMutex);
   3680 
   3681    if (!mPreStartRunnables.IsEmpty()) {
   3682      for (const RefPtr<WorkerThreadRunnable>& runnable : mPreStartRunnables) {
   3683        runnable->mCleanPreStartDispatching = true;
   3684        PROFILER_MARKER("WorkerPrivate::RunLoopNeverRan", OTHER, {},
   3685                        TerminatingFlowMarker,
   3686                        Flow::FromPointer(runnable.get()));
   3687      }
   3688      mPreStartRunnables.Clear();
   3689    }
   3690 
   3691    // Switch State to Dead
   3692    mStatus = Dead;
   3693    thread = mThread;
   3694  }
   3695 
   3696  // Clear the dispatched mPreStartRunnables.
   3697  if (thread && NS_HasPendingEvents(thread)) {
   3698    NS_ProcessPendingEvents(nullptr);
   3699  }
   3700 
   3701  // After mStatus is set to Dead there can be no more
   3702  // WorkerControlRunnables so no need to lock here.
   3703  if (!mControlQueue.IsEmpty()) {
   3704    WorkerRunnable* runnable = nullptr;
   3705    while (mControlQueue.Pop(runnable)) {
   3706      runnable->Release();
   3707    }
   3708  }
   3709 
   3710  // There should be no StrongWorkerRefs, child Workers, and Timeouts, but
   3711  // WeakWorkerRefs could. WorkerThreadPrimaryRunnable could have created a
   3712  // PerformanceStorageWorker which holds a WeakWorkerRef.
   3713  // Notify WeakWorkerRefs with Dead status.
   3714  NotifyWorkerRefs(Dead);
   3715 }
   3716 
   3717 void WorkerPrivate::UnrootGlobalScopes() {
   3718  LOG(WorkerLog(), ("WorkerPrivate::UnrootGlobalScopes [%p]", this));
   3719  auto data = mWorkerThreadAccessible.Access();
   3720 
   3721  RefPtr<WorkerDebuggerGlobalScope> debugScope = data->mDebuggerScope.forget();
   3722  if (debugScope) {
   3723    MOZ_ASSERT(debugScope->mWorkerPrivate == this);
   3724  }
   3725  RefPtr<WorkerGlobalScope> scope = data->mScope.forget();
   3726  if (scope) {
   3727    MOZ_ASSERT(scope->mWorkerPrivate == this);
   3728  }
   3729 }
   3730 
   3731 void WorkerPrivate::DoRunLoop(JSContext* aCx) {
   3732  LOG(WorkerLog(), ("WorkerPrivate::DoRunLoop [%p]", this));
   3733  auto data = mWorkerThreadAccessible.Access();
   3734  MOZ_RELEASE_ASSERT(!GetExecutionManager());
   3735 
   3736  RefPtr<WorkerThread> thread;
   3737  {
   3738    MutexAutoLock lock(mMutex);
   3739    mJSContext = aCx;
   3740    // mThread is set before we enter, and is never changed during DoRunLoop.
   3741    // copy to local so we don't trigger mutex analysis
   3742    MOZ_ASSERT(mThread);
   3743    thread = mThread;
   3744 
   3745    MOZ_ASSERT(mStatus == Pending);
   3746    mStatus = Running;
   3747 
   3748    // Now, start to run the event loop, mPreStartRunnables can be cleared,
   3749    // since when get here, Worker initialization has done successfully.
   3750    mPreStartRunnables.Clear();
   3751  }
   3752 
   3753  // Create IPC between the content process worker thread and the parent
   3754  // process background thread for non-life cycle related operations of
   3755  // SharedWorker/ServiceWorker
   3756  if (mChildEp.IsValid()) {
   3757    mRemoteWorkerNonLifeCycleOpController =
   3758        RemoteWorkerNonLifeCycleOpControllerChild::Create();
   3759    MOZ_ASSERT_DEBUG_OR_FUZZING(mRemoteWorkerNonLifeCycleOpController);
   3760    mChildEp.Bind(mRemoteWorkerNonLifeCycleOpController);
   3761  }
   3762 
   3763  // Now that we've done that, we can go ahead and set up our AutoJSAPI.  We
   3764  // can't before this point, because it can't find the right JSContext before
   3765  // then, since it gets it from our mJSContext.
   3766  AutoJSAPI jsapi;
   3767  jsapi.Init();
   3768  MOZ_ASSERT(jsapi.cx() == aCx);
   3769 
   3770  EnableMemoryReporter();
   3771 
   3772  InitializeGCTimers();
   3773 
   3774  bool checkFinalGCCC =
   3775      StaticPrefs::dom_workers_GCCC_on_potentially_last_event();
   3776 
   3777  bool debuggerRunnablesPending = false;
   3778  bool normalRunnablesPending = false;
   3779  auto noRunnablesPendingAndKeepAlive =
   3780      [&debuggerRunnablesPending, &normalRunnablesPending, &thread, this]()
   3781          MOZ_REQUIRES(mMutex) {
   3782            // We want to keep both pending flags always updated while looping.
   3783            debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   3784            normalRunnablesPending = NS_HasPendingEvents(thread);
   3785 
   3786            bool anyRunnablesPending = !mControlQueue.IsEmpty() ||
   3787                                       debuggerRunnablesPending ||
   3788                                       normalRunnablesPending;
   3789            bool keepWorkerAlive = mStatus == Running || HasActiveWorkerRefs();
   3790 
   3791            return (!anyRunnablesPending && keepWorkerAlive);
   3792          };
   3793 
   3794  for (;;) {
   3795    WorkerStatus currentStatus;
   3796 
   3797    if (checkFinalGCCC) {
   3798      // If we get here after the last event ran but someone holds a WorkerRef
   3799      // and there is no other logic to release that WorkerRef than lazily
   3800      // through GC/CC, we might block forever on the next WaitForWorkerEvents.
   3801      // Every object holding a WorkerRef should really have a straight,
   3802      // deterministic line from the WorkerRef's callback being invoked to the
   3803      // WorkerRef being released which is supported by strong-references that
   3804      // can't form a cycle.
   3805      bool mayNeedFinalGCCC = false;
   3806      {
   3807        MutexAutoLock lock(mMutex);
   3808 
   3809        currentStatus = mStatus;
   3810        mayNeedFinalGCCC =
   3811            (mStatus >= Canceling && HasActiveWorkerRefs() &&
   3812             !debuggerRunnablesPending && !normalRunnablesPending &&
   3813             data->mPerformedShutdownAfterLastContentTaskExecuted);
   3814      }
   3815      if (mayNeedFinalGCCC) {
   3816 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   3817        // WorkerRef::ReleaseWorker will check this flag via
   3818        // AssertIsNotPotentiallyLastGCCCRunning
   3819        data->mIsPotentiallyLastGCCCRunning = true;
   3820 #endif
   3821        // GarbageCollectInternal will trigger both GC and CC
   3822        GarbageCollectInternal(aCx, true /* aShrinking */,
   3823                               true /* aCollectChildren */);
   3824 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   3825        data->mIsPotentiallyLastGCCCRunning = false;
   3826 #endif
   3827      }
   3828    }
   3829 
   3830    {
   3831      MutexAutoLock lock(mMutex);
   3832      if (checkFinalGCCC && currentStatus != mStatus) {
   3833        // Something moved our status while we were supposed to check for a
   3834        // potentially needed GC/CC. Just check again.
   3835        continue;
   3836      }
   3837 
   3838      // Wait for a runnable to arrive that we can execute, or for it to be okay
   3839      // to shutdown this worker once all holders have been removed.
   3840      // Holders may be removed from inside normal runnables,  but we don't
   3841      // check for that after processing normal runnables, so we need to let
   3842      // control flow to the shutdown logic without blocking.
   3843      while (noRunnablesPendingAndKeepAlive()) {
   3844        // We pop out to this loop when there are no pending events.
   3845        // If we don't reset these, we may not re-enter ProcessNextEvent()
   3846        // until we have events to process, and it may seem like we have
   3847        // an event running for a very long time.
   3848        thread->SetRunningEventDelay(TimeDuration(), TimeStamp());
   3849 
   3850        mWorkerLoopIsIdle = true;
   3851 
   3852        WaitForWorkerEvents();
   3853 
   3854        mWorkerLoopIsIdle = false;
   3855      }
   3856 
   3857      auto result = ProcessAllControlRunnablesLocked();
   3858      if (result != ProcessAllControlRunnablesResult::Nothing) {
   3859        // Update all saved runnable flags for side effect for the
   3860        // loop check about transitioning to Killing below.
   3861        (void)noRunnablesPendingAndKeepAlive();
   3862      }
   3863 
   3864      currentStatus = mStatus;
   3865    }
   3866 
   3867    // Status transitions to Closing/Canceling and there are no SyncLoops,
   3868    // set global start dying, disconnect EventTargetObjects and
   3869    // WebTaskScheduler.
   3870    // The Worker might switch to the "Killing" immediately then directly exits
   3871    // DoRunLoop(). Before exiting the DoRunLoop(), explicitly disconnecting the
   3872    // WorkerGlobalScope's EventTargetObject here would help to fail runnable
   3873    // dispatching when the Worker is in the status changing.
   3874    if (currentStatus >= Closing &&
   3875        !data->mPerformedShutdownAfterLastContentTaskExecuted) {
   3876      data->mPerformedShutdownAfterLastContentTaskExecuted.Flip();
   3877      if (data->mScope) {
   3878        data->mScope->NoteTerminating();
   3879        data->mScope->DisconnectGlobalTeardownObservers();
   3880        if (WebTaskScheduler* scheduler =
   3881                data->mScope->GetExistingScheduler()) {
   3882          scheduler->Disconnect();
   3883        }
   3884      }
   3885    }
   3886 
   3887    // Transition from Canceling to Killing and exit this loop when:
   3888    //  * All (non-weak) WorkerRefs have been released.
   3889    //  * There are no runnables pending. This is intended to let same-thread
   3890    //    dispatches as part of cleanup be able to run to completion, but any
   3891    //    logic that still wants async things to happen should be holding a
   3892    //    StrongWorkerRef.
   3893    if (currentStatus != Running && !HasActiveWorkerRefs() &&
   3894        !normalRunnablesPending && !debuggerRunnablesPending) {
   3895      // Now we are ready to kill the worker thread.
   3896      if (currentStatus == Canceling) {
   3897        NotifyInternal(Killing);
   3898 
   3899 #ifdef DEBUG
   3900        {
   3901          MutexAutoLock lock(mMutex);
   3902          currentStatus = mStatus;
   3903        }
   3904        MOZ_ASSERT(currentStatus == Killing);
   3905 #else
   3906        currentStatus = Killing;
   3907 #endif
   3908      }
   3909 
   3910      // If we're supposed to die then we should exit the loop.
   3911      if (currentStatus == Killing) {
   3912        // We are about to destroy worker, report all use counters.
   3913        ReportUseCounters();
   3914 
   3915        // Flush uncaught rejections immediately, without
   3916        // waiting for a next tick.
   3917        PromiseDebugging::FlushUncaughtRejections();
   3918 
   3919        // DisableRemoteDebuggerOnWorkerThread(true /*aForShutdown*/);
   3920 
   3921        ShutdownGCTimers();
   3922 
   3923        DisableMemoryReporter();
   3924 
   3925        // Move the timer out with the mutex held but only drop the ref
   3926        // when the mutex is not held.
   3927        nsCOMPtr<nsITimer> timer;
   3928        {
   3929          MutexAutoLock lock(mMutex);
   3930          mStatus = Dead;
   3931          // Wait for the dispatching ControlRunnables complete.
   3932          while (mDispatchingControlRunnables) {
   3933            mCondVar.Wait();
   3934          }
   3935          mJSContext = nullptr;
   3936          mDebuggerInterruptTimer.swap(timer);
   3937        }
   3938        timer = nullptr;
   3939 
   3940        // After mStatus is set to Dead there can be no more
   3941        // WorkerControlRunnables so no need to lock here.
   3942        if (!mControlQueue.IsEmpty()) {
   3943          LOG(WorkerLog(),
   3944              ("WorkerPrivate::DoRunLoop [%p] dropping control runnables in "
   3945               "Dead status",
   3946               this));
   3947          WorkerRunnable* runnable = nullptr;
   3948          while (mControlQueue.Pop(runnable)) {
   3949            runnable->Cancel();
   3950            runnable->Release();
   3951          }
   3952        }
   3953 
   3954        // We do not need the timeouts any more, they have been canceled
   3955        // by NotifyInternal(Killing) above if they were active.
   3956        UnlinkTimeouts();
   3957 
   3958        return;
   3959      }
   3960    }
   3961 
   3962    if (debuggerRunnablesPending || normalRunnablesPending) {
   3963      // Start the periodic GC timer if it is not already running.
   3964      SetGCTimerMode(PeriodicTimer);
   3965    }
   3966 
   3967    if (debuggerRunnablesPending) {
   3968      ProcessSingleDebuggerRunnable();
   3969 
   3970      {
   3971        MutexAutoLock lock(mMutex);
   3972        debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   3973      }
   3974 
   3975      if (debuggerRunnablesPending) {
   3976        WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
   3977        // If the worker was canceled before ever creating its content global
   3978        // then mCancelBeforeWorkerScopeConstructed could have been flipped and
   3979        // all of the WorkerDebuggerRunnables canceled, so the debugger global
   3980        // would never have been created.
   3981        if (globalScope) {
   3982          // Now *might* be a good time to GC. Let the JS engine make the
   3983          // decision.
   3984          JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
   3985          JS_MaybeGC(aCx);
   3986        }
   3987      }
   3988    } else if (normalRunnablesPending) {
   3989      // Process a single runnable from the main queue.
   3990      NS_ProcessNextEvent(thread, false);
   3991 
   3992      normalRunnablesPending = NS_HasPendingEvents(thread);
   3993      if (normalRunnablesPending && GlobalScope()) {
   3994        // Now *might* be a good time to GC. Let the JS engine make the
   3995        // decision.
   3996        JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject());
   3997        JS_MaybeGC(aCx);
   3998      }
   3999    }
   4000 
   4001    // Checking the background actors if needed, any runnable execution could
   4002    // release background actors which blocks GC/CC on
   4003    // WorkerPrivate::mParentEventTargetRef.
   4004    if (currentStatus < Canceling) {
   4005      UpdateCCFlag(CCFlag::CheckBackgroundActors);
   4006    }
   4007 
   4008    if (!debuggerRunnablesPending && !normalRunnablesPending) {
   4009      // Both the debugger event queue and the normal event queue has been
   4010      // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
   4011      SetGCTimerMode(IdleTimer);
   4012    }
   4013 
   4014    // If the worker thread is spamming the main thread faster than it can
   4015    // process the work, then pause the worker thread until the main thread
   4016    // catches up.
   4017    size_t queuedEvents = mMainThreadEventTargetForMessaging->Length() +
   4018                          mMainThreadDebuggeeEventTarget->Length();
   4019    if (queuedEvents > 5000) {
   4020      // Note, postMessage uses mMainThreadDebuggeeEventTarget!
   4021      mMainThreadDebuggeeEventTarget->AwaitIdle();
   4022    }
   4023  }
   4024 
   4025  MOZ_CRASH("Shouldn't get here!");
   4026 }
   4027 
   4028 namespace {
   4029 /**
   4030 * If there is a current CycleCollectedJSContext, return its recursion depth,
   4031 * otherwise return 1.
   4032 *
   4033 * In the edge case where a worker is starting up so late that PBackground is
   4034 * already shutting down, the cycle collected context will never be created,
   4035 * but we will need to drain the event loop in ClearMainEventQueue.  This will
   4036 * result in a normal NS_ProcessPendingEvents invocation which will call
   4037 * WorkerPrivate::OnProcessNextEvent and WorkerPrivate::AfterProcessNextEvent
   4038 * which want to handle the need to process control runnables and perform a
   4039 * sanity check assertion, respectively.
   4040 *
   4041 * We claim a depth of 1 when there's no CCJS because this most corresponds to
   4042 * reality, but this doesn't meant that other code might want to drain various
   4043 * runnable queues as part of this cleanup.
   4044 */
   4045 uint32_t GetEffectiveEventLoopRecursionDepth() {
   4046  auto* ccjs = CycleCollectedJSContext::Get();
   4047  if (ccjs) {
   4048    return ccjs->RecursionDepth();
   4049  }
   4050 
   4051  return 1;
   4052 }
   4053 
   4054 }  // namespace
   4055 
   4056 void WorkerPrivate::OnProcessNextEvent() {
   4057  AssertIsOnWorkerThread();
   4058 
   4059  uint32_t recursionDepth = GetEffectiveEventLoopRecursionDepth();
   4060  MOZ_ASSERT(recursionDepth);
   4061 
   4062  // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
   4063  // However, it's possible that non-worker C++ could spin its own nested event
   4064  // loop, and in that case we must ensure that we continue to process control
   4065  // runnables here.
   4066  if (recursionDepth > 1 && mSyncLoopStack.Length() < recursionDepth - 1) {
   4067    (void)ProcessAllControlRunnables();
   4068    // There's no running JS, and no state to revalidate, so we can ignore the
   4069    // return value.
   4070  }
   4071 }
   4072 
   4073 void WorkerPrivate::AfterProcessNextEvent() {
   4074  AssertIsOnWorkerThread();
   4075  MOZ_ASSERT(GetEffectiveEventLoopRecursionDepth());
   4076 }
   4077 
   4078 nsISerialEventTarget* WorkerPrivate::MainThreadEventTargetForMessaging() {
   4079  return mMainThreadEventTargetForMessaging;
   4080 }
   4081 
   4082 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(
   4083    nsIRunnable* aRunnable, nsIEventTarget::DispatchFlags aFlags) {
   4084  nsCOMPtr<nsIRunnable> r = aRunnable;
   4085  return DispatchToMainThreadForMessaging(r.forget(), aFlags);
   4086 }
   4087 
   4088 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(
   4089    already_AddRefed<nsIRunnable> aRunnable,
   4090    nsIEventTarget::DispatchFlags aFlags) {
   4091  return mMainThreadEventTargetForMessaging->Dispatch(std::move(aRunnable),
   4092                                                      aFlags);
   4093 }
   4094 
   4095 nsISerialEventTarget* WorkerPrivate::MainThreadEventTarget() {
   4096  return mMainThreadEventTarget;
   4097 }
   4098 
   4099 nsresult WorkerPrivate::DispatchToMainThread(
   4100    nsIRunnable* aRunnable, nsIEventTarget::DispatchFlags aFlags) {
   4101  nsCOMPtr<nsIRunnable> r = aRunnable;
   4102  return DispatchToMainThread(r.forget(), aFlags);
   4103 }
   4104 
   4105 nsresult WorkerPrivate::DispatchToMainThread(
   4106    already_AddRefed<nsIRunnable> aRunnable,
   4107    nsIEventTarget::DispatchFlags aFlags) {
   4108  return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags);
   4109 }
   4110 
   4111 nsresult WorkerPrivate::DispatchDebuggeeToMainThread(
   4112    already_AddRefed<WorkerRunnable> aRunnable,
   4113    nsIEventTarget::DispatchFlags aFlags) {
   4114  RefPtr<WorkerRunnable> debuggeeRunnable = std::move(aRunnable);
   4115  MOZ_ASSERT_DEBUG_OR_FUZZING(debuggeeRunnable->IsDebuggeeRunnable());
   4116  return mMainThreadDebuggeeEventTarget->Dispatch(debuggeeRunnable.forget(),
   4117                                                  aFlags);
   4118 }
   4119 
   4120 nsISerialEventTarget* WorkerPrivate::ControlEventTarget() {
   4121  return mWorkerControlEventTarget;
   4122 }
   4123 
   4124 nsISerialEventTarget* WorkerPrivate::HybridEventTarget() {
   4125  return mWorkerHybridEventTarget;
   4126 }
   4127 
   4128 ClientType WorkerPrivate::GetClientType() const {
   4129  switch (Kind()) {
   4130    case WorkerKindDedicated:
   4131      return ClientType::Worker;
   4132    case WorkerKindShared:
   4133      return ClientType::Sharedworker;
   4134    case WorkerKindService:
   4135      return ClientType::Serviceworker;
   4136    default:
   4137      MOZ_CRASH("unknown worker type!");
   4138  }
   4139 }
   4140 
   4141 UniquePtr<ClientSource> WorkerPrivate::CreateClientSource() {
   4142  auto data = mWorkerThreadAccessible.Access();
   4143  MOZ_ASSERT(!data->mScope, "Client should be created before the global");
   4144 
   4145  UniquePtr<ClientSource> clientSource;
   4146  if (IsServiceWorker()) {
   4147    clientSource = ClientManager::CreateSourceFromInfo(
   4148        GetSourceInfo(), mWorkerHybridEventTarget);
   4149  } else {
   4150    clientSource = ClientManager::CreateSource(
   4151        GetClientType(), mWorkerHybridEventTarget,
   4152        StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
   4153            this)
   4154            ? GetPartitionedPrincipalInfo()
   4155            : GetPrincipalInfo());
   4156  }
   4157  MOZ_DIAGNOSTIC_ASSERT(clientSource);
   4158 
   4159  clientSource->SetAgentClusterId(mAgentClusterId);
   4160 
   4161  if (data->mFrozen) {
   4162    clientSource->Freeze();
   4163  }
   4164 
   4165  // Shortly after the client is reserved we will try loading the main script
   4166  // for the worker.  This may get intercepted by the ServiceWorkerManager
   4167  // which will then try to create a ClientHandle.  Its actually possible for
   4168  // the main thread to create this ClientHandle before our IPC message creating
   4169  // the ClientSource completes.  To avoid this race we synchronously ping our
   4170  // parent Client actor here.  This ensure the worker ClientSource is created
   4171  // in the parent before the main thread might try reaching it with a
   4172  // ClientHandle.
   4173  //
   4174  // An alternative solution would have been to handle the out-of-order
   4175  // operations on the parent side.  We could have created a small window where
   4176  // we allow ClientHandle objects to exist without a ClientSource.  We would
   4177  // then time out these handles if they stayed orphaned for too long.  This
   4178  // approach would be much more complex, but also avoid this extra bit of
   4179  // latency when starting workers.
   4180  //
   4181  // Note, we only have to do this for workers that can be controlled by a
   4182  // service worker.  So avoid the sync overhead here if we are starting a
   4183  // service worker or a chrome worker.
   4184  if (Kind() != WorkerKindService && !IsChromeWorker()) {
   4185    clientSource->WorkerSyncPing(this);
   4186  }
   4187 
   4188  return clientSource;
   4189 }
   4190 
   4191 bool WorkerPrivate::EnsureCSPEventListener() {
   4192  if (!mCSPEventListener) {
   4193    mCSPEventListener = WorkerCSPEventListener::Create(this);
   4194    if (NS_WARN_IF(!mCSPEventListener)) {
   4195      return false;
   4196    }
   4197  }
   4198  return true;
   4199 }
   4200 
   4201 nsICSPEventListener* WorkerPrivate::CSPEventListener() const {
   4202  MOZ_ASSERT(mCSPEventListener);
   4203  return mCSPEventListener;
   4204 }
   4205 
   4206 void WorkerPrivate::EnsurePerformanceStorage() {
   4207  AssertIsOnWorkerThread();
   4208 
   4209  if (!mPerformanceStorage) {
   4210    mPerformanceStorage = PerformanceStorageWorker::Create(this);
   4211  }
   4212 }
   4213 
   4214 bool WorkerPrivate::GetExecutionGranted() const {
   4215  auto data = mWorkerThreadAccessible.Access();
   4216  return data->mJSThreadExecutionGranted;
   4217 }
   4218 
   4219 void WorkerPrivate::SetExecutionGranted(bool aGranted) {
   4220  auto data = mWorkerThreadAccessible.Access();
   4221  data->mJSThreadExecutionGranted = aGranted;
   4222 }
   4223 
   4224 void WorkerPrivate::ScheduleTimeSliceExpiration(uint32_t aDelay) {
   4225  auto data = mWorkerThreadAccessible.Access();
   4226 
   4227  if (!data->mTSTimer) {
   4228    data->mTSTimer = NS_NewTimer();
   4229    MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->SetTarget(mWorkerControlEventTarget));
   4230  }
   4231 
   4232  // Whenever an event is scheduled on the WorkerControlEventTarget an
   4233  // interrupt is automatically requested which causes us to yield JS execution
   4234  // and the next JS execution in the queue to execute.
   4235  // This allows for simple code reuse of the existing interrupt callback code
   4236  // used for control events.
   4237  MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->InitWithNamedFuncCallback(
   4238      [](nsITimer* Timer, void* aClosure) { return; }, nullptr, aDelay,
   4239      nsITimer::TYPE_ONE_SHOT, "TimeSliceExpirationTimer"_ns));
   4240 }
   4241 
   4242 void WorkerPrivate::CancelTimeSliceExpiration() {
   4243  auto data = mWorkerThreadAccessible.Access();
   4244  MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->Cancel());
   4245 }
   4246 
   4247 JSExecutionManager* WorkerPrivate::GetExecutionManager() const {
   4248  auto data = mWorkerThreadAccessible.Access();
   4249  return data->mExecutionManager.get();
   4250 }
   4251 
   4252 void WorkerPrivate::SetExecutionManager(JSExecutionManager* aManager) {
   4253  auto data = mWorkerThreadAccessible.Access();
   4254  data->mExecutionManager = aManager;
   4255 }
   4256 
   4257 void WorkerPrivate::ExecutionReady() {
   4258  auto data = mWorkerThreadAccessible.Access();
   4259  {
   4260    MutexAutoLock lock(mMutex);
   4261    if (mStatus >= Canceling) {
   4262      return;
   4263    }
   4264  }
   4265 
   4266  data->mScope->MutableClientSourceRef().WorkerExecutionReady(this);
   4267 
   4268  if (ExtensionAPIAllowed()) {
   4269    extensions::CreateAndDispatchInitWorkerContextRunnable();
   4270  }
   4271 }
   4272 
   4273 void WorkerPrivate::InitializeGCTimers() {
   4274  auto data = mWorkerThreadAccessible.Access();
   4275 
   4276  // We need timers for GC. The basic plan is to run a non-shrinking GC
   4277  // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
   4278  // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
   4279  // run a shrinking GC.
   4280  data->mPeriodicGCTimer = NS_NewTimer();
   4281  data->mIdleGCTimer = NS_NewTimer();
   4282 
   4283  data->mPeriodicGCTimerRunning = false;
   4284  data->mIdleGCTimerRunning = false;
   4285 }
   4286 
   4287 void WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) {
   4288  auto data = mWorkerThreadAccessible.Access();
   4289 
   4290  if (!data->mPeriodicGCTimer || !data->mIdleGCTimer) {
   4291    // GC timers have been cleared already.
   4292    return;
   4293  }
   4294 
   4295  if (aMode == NoTimer) {
   4296    MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel());
   4297    data->mPeriodicGCTimerRunning = false;
   4298    MOZ_ALWAYS_SUCCEEDS(data->mIdleGCTimer->Cancel());
   4299    data->mIdleGCTimerRunning = false;
   4300    return;
   4301  }
   4302 
   4303  WorkerStatus status;
   4304  {
   4305    MutexAutoLock lock(mMutex);
   4306    status = mStatus;
   4307  }
   4308 
   4309  if (status >= Killing) {
   4310    ShutdownGCTimers();
   4311    return;
   4312  }
   4313 
   4314  // If the idle timer is running, don't cancel it when the periodic timer
   4315  // is scheduled since we do want shrinking GC to be called occasionally.
   4316  if (aMode == PeriodicTimer && data->mPeriodicGCTimerRunning) {
   4317    return;
   4318  }
   4319 
   4320  if (aMode == IdleTimer) {
   4321    if (!data->mPeriodicGCTimerRunning) {
   4322      // Since running idle GC cancels both GC timers, after that we want
   4323      // first at least periodic GC timer getting activated, since that tells
   4324      // us that there have been some non-control tasks to process. Otherwise
   4325      // idle GC timer would keep running all the time.
   4326      return;
   4327    }
   4328 
   4329    // Cancel the periodic timer now, since the event loop is (in the common
   4330    // case) empty now.
   4331    MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel());
   4332    data->mPeriodicGCTimerRunning = false;
   4333 
   4334    if (data->mIdleGCTimerRunning) {
   4335      return;
   4336    }
   4337  }
   4338 
   4339  MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
   4340 
   4341  uint32_t delay = 0;
   4342  int16_t type = nsITimer::TYPE_ONE_SHOT;
   4343  nsTimerCallbackFunc callback = nullptr;
   4344  nsCString name;
   4345  nsITimer* timer = nullptr;
   4346 
   4347  if (aMode == PeriodicTimer) {
   4348    delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
   4349    type = nsITimer::TYPE_REPEATING_SLACK;
   4350    callback = PeriodicGCTimerCallback;
   4351    name.AssignLiteral("dom::PeriodicGCTimerCallback");
   4352    timer = data->mPeriodicGCTimer;
   4353    data->mPeriodicGCTimerRunning = true;
   4354    LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
   4355  } else {
   4356    delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
   4357    type = nsITimer::TYPE_ONE_SHOT;
   4358    callback = IdleGCTimerCallback;
   4359    name.AssignLiteral("dom::IdleGCTimerCallback");
   4360    timer = data->mIdleGCTimer;
   4361    data->mIdleGCTimerRunning = true;
   4362    LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
   4363  }
   4364 
   4365  MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(mWorkerControlEventTarget));
   4366  MOZ_ALWAYS_SUCCEEDS(
   4367      timer->InitWithNamedFuncCallback(callback, this, delay, type, name));
   4368 }
   4369 
   4370 void WorkerPrivate::ShutdownGCTimers() {
   4371  auto data = mWorkerThreadAccessible.Access();
   4372 
   4373  MOZ_ASSERT(!data->mPeriodicGCTimer == !data->mIdleGCTimer);
   4374 
   4375  if (!data->mPeriodicGCTimer && !data->mIdleGCTimer) {
   4376    return;
   4377  }
   4378 
   4379  // Always make sure the timers are canceled.
   4380  MOZ_ALWAYS_SUCCEEDS(data->mPeriodicGCTimer->Cancel());
   4381  MOZ_ALWAYS_SUCCEEDS(data->mIdleGCTimer->Cancel());
   4382 
   4383  LOG(WorkerLog(), ("Worker %p killed the GC timers\n", this));
   4384 
   4385  data->mPeriodicGCTimer = nullptr;
   4386  data->mIdleGCTimer = nullptr;
   4387  data->mPeriodicGCTimerRunning = false;
   4388  data->mIdleGCTimerRunning = false;
   4389 }
   4390 
   4391 bool WorkerPrivate::InterruptCallback(JSContext* aCx) {
   4392  auto data = mWorkerThreadAccessible.Access();
   4393 
   4394  AutoYieldJSThreadExecution yield;
   4395 
   4396  // If we are here it's because a WorkerControlRunnable has been dispatched.
   4397  // The runnable could be processed here or it could have already been
   4398  // processed by a sync event loop.
   4399  // The most important thing this method must do, is to decide if the JS
   4400  // execution should continue or not. If the runnable returns an error or if
   4401  // the worker status is >= Canceling, we should stop the JS execution.
   4402 
   4403  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
   4404 
   4405  bool mayContinue = true;
   4406  bool scheduledIdleGC = false;
   4407 
   4408  for (;;) {
   4409    // Run all control events now.
   4410    auto result = ProcessAllControlRunnables();
   4411    if (result == ProcessAllControlRunnablesResult::Abort) {
   4412      mayContinue = false;
   4413    }
   4414 
   4415    bool mayFreeze = data->mFrozen;
   4416 
   4417    {
   4418      MutexAutoLock lock(mMutex);
   4419 
   4420      if (mayFreeze) {
   4421        mayFreeze = mStatus <= Running;
   4422      }
   4423 
   4424      if (mStatus >= Canceling) {
   4425        mayContinue = false;
   4426      }
   4427    }
   4428 
   4429    if (!mayContinue || !mayFreeze) {
   4430      break;
   4431    }
   4432 
   4433    // Cancel the periodic GC timer here before freezing. The idle GC timer
   4434    // will clean everything up once it runs.
   4435    if (!scheduledIdleGC) {
   4436      SetGCTimerMode(IdleTimer);
   4437      scheduledIdleGC = true;
   4438    }
   4439 
   4440    while ((mayContinue = MayContinueRunning())) {
   4441      MutexAutoLock lock(mMutex);
   4442      if (!mControlQueue.IsEmpty()) {
   4443        break;
   4444      }
   4445 
   4446      WaitForWorkerEvents();
   4447    }
   4448  }
   4449 
   4450  if (!mayContinue) {
   4451    // We want only uncatchable exceptions here.
   4452    NS_ASSERTION(!JS_IsExceptionPending(aCx),
   4453                 "Should not have an exception set here!");
   4454    return false;
   4455  }
   4456 
   4457  // Make sure the periodic timer gets turned back on here.
   4458  SetGCTimerMode(PeriodicTimer);
   4459 
   4460  if (data->mDebuggerInterruptRequested) {
   4461    bool debuggerRunnablesPending = false;
   4462    {
   4463      MutexAutoLock lock(mMutex);
   4464      debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   4465    }
   4466    if (debuggerRunnablesPending) {
   4467      // Prevents interrupting the debugger's own logic unless it has called
   4468      // back into content
   4469      WorkerGlobalScope* globalScope = GlobalScope();
   4470      if (globalScope) {
   4471        JSObject* global = JS::CurrentGlobalOrNull(aCx);
   4472        if (global && global == globalScope->GetGlobalJSObject()) {
   4473          while (debuggerRunnablesPending) {
   4474            ProcessSingleDebuggerRunnable();
   4475            {
   4476              MutexAutoLock lock(mMutex);
   4477              debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   4478            }
   4479          }
   4480        }
   4481      }
   4482    }
   4483    data->mDebuggerInterruptRequested = false;
   4484  }
   4485 
   4486  return true;
   4487 }
   4488 
   4489 void WorkerPrivate::CloseInternal() {
   4490  AssertIsOnWorkerThread();
   4491  NotifyInternal(Closing);
   4492 }
   4493 
   4494 bool WorkerPrivate::IsOnCurrentThread() {
   4495  // May be called on any thread!
   4496 
   4497  MOZ_ASSERT(mPRThread);
   4498  return PR_GetCurrentThread() == mPRThread;
   4499 }
   4500 
   4501 void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
   4502  AssertIsOnWorkerThread();
   4503  {
   4504    // mWorkerThreadAccessible's accessor must be destructed before
   4505    // the scheduled Runnable gets to run.
   4506    auto data = mWorkerThreadAccessible.Access();
   4507    MOZ_ASSERT(data->mChildWorkers.IsEmpty());
   4508 
   4509    MOZ_RELEASE_ASSERT(!data->mDeletionScheduled);
   4510    data->mDeletionScheduled.Flip();
   4511  }
   4512  MOZ_ASSERT(mSyncLoopStack.IsEmpty());
   4513  MOZ_ASSERT(mPostSyncLoopOperations == 0);
   4514 
   4515  // If Worker is never ran, clear the mPreStartRunnables. To let the resource
   4516  // hold by the pre-submmited runnables.
   4517  if (WorkerNeverRan == aRanOrNot) {
   4518    ClearPreStartRunnables();
   4519  }
   4520 
   4521 #ifdef DEBUG
   4522  if (WorkerRan == aRanOrNot) {
   4523    nsIThread* currentThread = NS_GetCurrentThread();
   4524    MOZ_ASSERT(currentThread);
   4525    // On the worker thread WorkerRunnable will refuse to run if not nested
   4526    // on top of a WorkerThreadPrimaryRunnable.
   4527    (void)NS_WARN_IF(NS_HasPendingEvents(currentThread));
   4528  }
   4529 #endif
   4530 
   4531  // Force to set mRemoteDebuggerRegistered as false and notify if the Worker is
   4532  // waiting for the registration done.
   4533  SetIsRemoteDebuggerRegistered(false);
   4534 
   4535  if (WorkerPrivate* parent = GetParent()) {
   4536    RefPtr<WorkerFinishedRunnable> runnable =
   4537        new WorkerFinishedRunnable(parent, this);
   4538    if (!runnable->Dispatch(parent)) {
   4539      NS_WARNING("Failed to dispatch runnable!");
   4540    }
   4541  } else {
   4542    if (ExtensionAPIAllowed()) {
   4543      MOZ_ASSERT(IsServiceWorker());
   4544      RefPtr<Runnable> extWorkerRunnable =
   4545          extensions::CreateWorkerDestroyedRunnable(ServiceWorkerID(),
   4546                                                    GetBaseURI());
   4547      // Dispatch as a low priority runnable.
   4548      if (NS_FAILED(
   4549              DispatchToMainThreadForMessaging(extWorkerRunnable.forget()))) {
   4550        NS_WARNING(
   4551            "Failed to dispatch runnable to notify extensions worker "
   4552            "destroyed");
   4553      }
   4554    }
   4555 
   4556    // Note, this uses the lower priority DispatchToMainThreadForMessaging for
   4557    // dispatching TopLevelWorkerFinishedRunnable to the main thread so that
   4558    // other relevant runnables are guaranteed to run before it.
   4559    RefPtr<TopLevelWorkerFinishedRunnable> runnable =
   4560        new TopLevelWorkerFinishedRunnable(this);
   4561    if (NS_FAILED(DispatchToMainThreadForMessaging(runnable.forget()))) {
   4562      NS_WARNING("Failed to dispatch runnable!");
   4563    }
   4564 
   4565    // NOTE: Calling any WorkerPrivate methods (or accessing member data) after
   4566    // this point is unsafe (the TopLevelWorkerFinishedRunnable just dispatched
   4567    // may be able to call ClearSelfAndParentEventTargetRef on this
   4568    // WorkerPrivate instance and by the time we get here the WorkerPrivate
   4569    // instance destructor may have been already called).
   4570  }
   4571 }
   4572 
   4573 bool WorkerPrivate::CollectRuntimeStats(
   4574    JS::RuntimeStats* aRtStats, bool aAnonymize) MOZ_NO_THREAD_SAFETY_ANALYSIS {
   4575  // We don't have a lock to access mJSContext, but it's safe to access on this
   4576  // thread.
   4577  AssertIsOnWorkerThread();
   4578  NS_ASSERTION(aRtStats, "Null RuntimeStats!");
   4579  // We don't really own it, but it's safe to access on this thread
   4580  NS_ASSERTION(mJSContext, "This must never be null!");
   4581 
   4582  return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
   4583 }
   4584 
   4585 void WorkerPrivate::EnableMemoryReporter() {
   4586  auto data = mWorkerThreadAccessible.Access();
   4587  MOZ_ASSERT(!data->mMemoryReporter);
   4588 
   4589  // No need to lock here since the main thread can't race until we've
   4590  // successfully registered the reporter.
   4591  data->mMemoryReporter = new MemoryReporter(this);
   4592 
   4593  if (NS_FAILED(RegisterWeakAsyncMemoryReporter(data->mMemoryReporter))) {
   4594    NS_WARNING("Failed to register memory reporter!");
   4595    // No need to lock here since a failed registration means our memory
   4596    // reporter can't start running. Just clean up.
   4597    data->mMemoryReporter = nullptr;
   4598  }
   4599 }
   4600 
   4601 void WorkerPrivate::DisableMemoryReporter() {
   4602  auto data = mWorkerThreadAccessible.Access();
   4603 
   4604  RefPtr<MemoryReporter> memoryReporter;
   4605  {
   4606    // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
   4607    // MemoryReporter::Disable() below.
   4608    MutexAutoLock lock(mMutex);
   4609 
   4610    // There is nothing to do here if the memory reporter was never successfully
   4611    // registered.
   4612    if (!data->mMemoryReporter) {
   4613      return;
   4614    }
   4615 
   4616    // We don't need this set any longer. Swap it out so that we can unregister
   4617    // below.
   4618    data->mMemoryReporter.swap(memoryReporter);
   4619 
   4620    // Next disable the memory reporter so that the main thread stops trying to
   4621    // signal us.
   4622    memoryReporter->Disable();
   4623  }
   4624 
   4625  // Finally unregister the memory reporter.
   4626  if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
   4627    NS_WARNING("Failed to unregister memory reporter!");
   4628  }
   4629 }
   4630 
   4631 void WorkerPrivate::WaitForWorkerEvents() {
   4632  AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE);
   4633 
   4634  AssertIsOnWorkerThread();
   4635  mMutex.AssertCurrentThreadOwns();
   4636 
   4637  // Wait for a worker event.
   4638  mCondVar.Wait();
   4639 }
   4640 
   4641 WorkerPrivate::ProcessAllControlRunnablesResult
   4642 WorkerPrivate::ProcessAllControlRunnablesLocked() {
   4643  AssertIsOnWorkerThread();
   4644  mMutex.AssertCurrentThreadOwns();
   4645 
   4646  AutoYieldJSThreadExecution yield;
   4647 
   4648  auto result = ProcessAllControlRunnablesResult::Nothing;
   4649 
   4650  for (;;) {
   4651    WorkerRunnable* event;
   4652    if (!mControlQueue.Pop(event)) {
   4653      break;
   4654    }
   4655 
   4656    MutexAutoUnlock unlock(mMutex);
   4657 
   4658    {
   4659      MOZ_ASSERT(event);
   4660      AUTO_PROFILE_FOLLOWING_RUNNABLE(event);
   4661      if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
   4662        result = ProcessAllControlRunnablesResult::Abort;
   4663      }
   4664    }
   4665 
   4666    if (result == ProcessAllControlRunnablesResult::Nothing) {
   4667      // We ran at least one thing.
   4668      result = ProcessAllControlRunnablesResult::MayContinue;
   4669    }
   4670    event->Release();
   4671  }
   4672 
   4673  return result;
   4674 }
   4675 
   4676 void WorkerPrivate::ShutdownModuleLoader() {
   4677  AssertIsOnWorkerThread();
   4678 
   4679  WorkerGlobalScope* globalScope = GlobalScope();
   4680  if (globalScope) {
   4681    if (globalScope->GetModuleLoader(nullptr)) {
   4682      globalScope->GetModuleLoader(nullptr)->Shutdown();
   4683    }
   4684  }
   4685  WorkerDebuggerGlobalScope* debugGlobalScope = DebuggerGlobalScope();
   4686  if (debugGlobalScope) {
   4687    if (debugGlobalScope->GetModuleLoader(nullptr)) {
   4688      debugGlobalScope->GetModuleLoader(nullptr)->Shutdown();
   4689    }
   4690  }
   4691 }
   4692 
   4693 void WorkerPrivate::ClearPreStartRunnables() {
   4694  nsTArray<RefPtr<WorkerThreadRunnable>> prestart;
   4695  {
   4696    MutexAutoLock lock(mMutex);
   4697    mPreStartRunnables.SwapElements(prestart);
   4698  }
   4699  for (uint32_t count = prestart.Length(), index = 0; index < count; index++) {
   4700    LOG(WorkerLog(), ("WorkerPrivate::ClearPreStartRunnable [%p]", this));
   4701    RefPtr<WorkerRunnable> runnable = std::move(prestart[index]);
   4702    runnable->Cancel();
   4703  }
   4704 }
   4705 
   4706 void WorkerPrivate::ProcessSingleDebuggerRunnable() {
   4707  WorkerRunnable* runnable = nullptr;
   4708 
   4709  // Move the timer out with the mutex held but only drop the ref
   4710  // when the mutex is not held.
   4711  nsCOMPtr<nsITimer> timer;
   4712  {
   4713    MutexAutoLock lock(mMutex);
   4714 
   4715    mDebuggerQueue.Pop(runnable);
   4716 
   4717    mDebuggerInterruptTimer.swap(timer);
   4718  }
   4719  timer = nullptr;
   4720 
   4721  {
   4722    MOZ_ASSERT(runnable);
   4723    AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
   4724    static_cast<nsIRunnable*>(runnable)->Run();
   4725  }
   4726  runnable->Release();
   4727 
   4728  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
   4729  ccjs->PerformDebuggerMicroTaskCheckpoint();
   4730 }
   4731 
   4732 void WorkerPrivate::ClearDebuggerEventQueue() {
   4733  bool debuggerRunnablesPending = false;
   4734  {
   4735    MutexAutoLock lock(mMutex);
   4736    debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   4737  }
   4738  while (debuggerRunnablesPending) {
   4739    WorkerRunnable* runnable = nullptr;
   4740    {
   4741      MutexAutoLock lock(mMutex);
   4742      mDebuggerQueue.Pop(runnable);
   4743      debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   4744    }
   4745    // It should be ok to simply release the runnable, without running it.
   4746    runnable->Release();
   4747 
   4748    // Move the timer out with the mutex held but only drop the ref
   4749    // when the mutex is not held.
   4750    nsCOMPtr<nsITimer> timer;
   4751    {
   4752      MutexAutoLock lock(mMutex);
   4753      mDebuggerInterruptTimer.swap(timer);
   4754    }
   4755    timer = nullptr;
   4756  }
   4757 }
   4758 
   4759 bool WorkerPrivate::FreezeInternal() {
   4760  auto data = mWorkerThreadAccessible.Access();
   4761  NS_ASSERTION(!data->mFrozen, "Already frozen!");
   4762 
   4763  AutoYieldJSThreadExecution yield;
   4764 
   4765  // The worker can freeze even if it failed to run (and doesn't have a global).
   4766  if (data->mScope) {
   4767    data->mScope->MutableClientSourceRef().Freeze();
   4768  }
   4769 
   4770  data->mFrozen = true;
   4771 
   4772  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4773    data->mChildWorkers[index]->Freeze(nullptr);
   4774  }
   4775  auto* timeoutManager =
   4776      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4777  if (timeoutManager) {
   4778    timeoutManager->Suspend();
   4779    timeoutManager->Freeze();
   4780  }
   4781 
   4782  return true;
   4783 }
   4784 
   4785 bool WorkerPrivate::HasActiveWorkerRefs() {
   4786  auto data = mWorkerThreadAccessible.Access();
   4787  auto* timeoutManager =
   4788      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4789  return !data->mChildWorkers.IsEmpty() ||
   4790         (timeoutManager && timeoutManager->HasTimeouts()) ||
   4791         !data->mWorkerRefs.IsEmpty();
   4792 }
   4793 
   4794 bool WorkerPrivate::ThawInternal() {
   4795  auto data = mWorkerThreadAccessible.Access();
   4796  NS_ASSERTION(data->mFrozen, "Not yet frozen!");
   4797 
   4798  // BindRemoteWorkerDebuggerChild();
   4799 
   4800  data->mFrozen = false;
   4801 
   4802  auto* timeoutManager =
   4803      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4804  if (timeoutManager) {
   4805    timeoutManager->Thaw();
   4806    timeoutManager->Resume();
   4807  }
   4808 
   4809  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4810    data->mChildWorkers[index]->Thaw(nullptr);
   4811  }
   4812 
   4813  // The worker can thaw even if it failed to run (and doesn't have a global).
   4814  if (data->mScope) {
   4815    data->mScope->MutableClientSourceRef().Thaw();
   4816  }
   4817 
   4818  return true;
   4819 }
   4820 
   4821 bool WorkerPrivate::ChangeBackgroundStateInternal(bool aIsBackground) {
   4822  AssertIsOnWorkerThread();
   4823  mIsInBackground = aIsBackground;
   4824  auto data = mWorkerThreadAccessible.Access();
   4825  if (StaticPrefs::dom_workers_throttling_enabled_AtStartup()) {
   4826    auto* timeoutManager =
   4827        data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4828    if (timeoutManager) {
   4829      timeoutManager->UpdateBackgroundState();
   4830    }
   4831  }
   4832 
   4833  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4834    if (aIsBackground) {
   4835      data->mChildWorkers[index]->SetIsRunningInBackground();
   4836    } else {
   4837      data->mChildWorkers[index]->SetIsRunningInForeground();
   4838    }
   4839  }
   4840  return true;
   4841 }
   4842 
   4843 bool WorkerPrivate::ChangePlaybackStateInternal(bool aIsPlayingAudio) {
   4844  AssertIsOnWorkerThread();
   4845  mIsPlayingAudio = aIsPlayingAudio;
   4846  auto data = mWorkerThreadAccessible.Access();
   4847 
   4848  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4849    data->mChildWorkers[index]->SetIsPlayingAudio(aIsPlayingAudio);
   4850  }
   4851  return true;
   4852 }
   4853 
   4854 bool WorkerPrivate::ChangePeerConnectionsInternal(bool aHasPeerConnections) {
   4855  AssertIsOnWorkerThread();
   4856  mHasActivePeerConnections = aHasPeerConnections;
   4857  auto data = mWorkerThreadAccessible.Access();
   4858 
   4859  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4860    data->mChildWorkers[index]->SetActivePeerConnections(aHasPeerConnections);
   4861  }
   4862  return true;
   4863 }
   4864 
   4865 void WorkerPrivate::PropagateStorageAccessPermissionGrantedInternal() {
   4866  auto data = mWorkerThreadAccessible.Access();
   4867 
   4868  mLoadInfo.mUseRegularPrincipal = true;
   4869  mLoadInfo.mUsingStorageAccess = true;
   4870 
   4871  WorkerGlobalScope* globalScope = GlobalScope();
   4872  if (globalScope) {
   4873    globalScope->StorageAccessPermissionGranted();
   4874  }
   4875 
   4876  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   4877    data->mChildWorkers[index]->PropagateStorageAccessPermissionGranted();
   4878  }
   4879 }
   4880 
   4881 void WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb) {
   4882  auto data = mWorkerThreadAccessible.Access();
   4883  auto* timeoutManager =
   4884      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4885  if (timeoutManager) {
   4886    timeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
   4887      cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
   4888    });
   4889  }
   4890 }
   4891 
   4892 void WorkerPrivate::UnlinkTimeouts() {
   4893  auto data = mWorkerThreadAccessible.Access();
   4894  auto* timeoutManager =
   4895      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   4896  if (timeoutManager) {
   4897    timeoutManager->ClearAllTimeouts();
   4898    if (!timeoutManager->HasTimeouts()) {
   4899      UpdateCCFlag(CCFlag::EligibleForTimeout);
   4900    }
   4901  }
   4902 }
   4903 
   4904 bool WorkerPrivate::AddChildWorker(WorkerPrivate& aChildWorker) {
   4905  auto data = mWorkerThreadAccessible.Access();
   4906 
   4907 #ifdef DEBUG
   4908  {
   4909    WorkerStatus currentStatus;
   4910    {
   4911      MutexAutoLock lock(mMutex);
   4912      currentStatus = mStatus;
   4913    }
   4914 
   4915    MOZ_ASSERT(currentStatus == Running);
   4916  }
   4917 #endif
   4918 
   4919  NS_ASSERTION(!data->mChildWorkers.Contains(&aChildWorker),
   4920               "Already know about this one!");
   4921  data->mChildWorkers.AppendElement(&aChildWorker);
   4922 
   4923  if (data->mChildWorkers.Length() == 1) {
   4924    UpdateCCFlag(CCFlag::IneligibleForChildWorker);
   4925  }
   4926 
   4927  return true;
   4928 }
   4929 
   4930 void WorkerPrivate::RemoveChildWorker(WorkerPrivate& aChildWorker) {
   4931  auto data = mWorkerThreadAccessible.Access();
   4932 
   4933  NS_ASSERTION(data->mChildWorkers.Contains(&aChildWorker),
   4934               "Didn't know about this one!");
   4935  data->mChildWorkers.RemoveElement(&aChildWorker);
   4936 
   4937  if (data->mChildWorkers.IsEmpty()) {
   4938    UpdateCCFlag(CCFlag::EligibleForChildWorker);
   4939  }
   4940 }
   4941 
   4942 bool WorkerPrivate::AddWorkerRef(WorkerRef* aWorkerRef,
   4943                                 WorkerStatus aFailStatus) {
   4944  MOZ_ASSERT(aWorkerRef);
   4945  auto data = mWorkerThreadAccessible.Access();
   4946 
   4947  {
   4948    MutexAutoLock lock(mMutex);
   4949 
   4950    LOG(WorkerLog(),
   4951        ("WorkerPrivate::AddWorkerRef [%p] mStatus: %u, aFailStatus: (%u)",
   4952         this, static_cast<uint8_t>(mStatus),
   4953         static_cast<uint8_t>(aFailStatus)));
   4954 
   4955    if (mStatus >= aFailStatus) {
   4956      return false;
   4957    }
   4958 
   4959    // We shouldn't create strong references to workers before their main loop
   4960    // begins running. Strong references must be disposed of on the worker
   4961    // thread, so strong references from other threads use a control runnable
   4962    // for that purpose. If the worker fails to reach the main loop stage then
   4963    // no control runnables get run and it would be impossible to get rid of the
   4964    // reference properly.
   4965    MOZ_DIAGNOSTIC_ASSERT_IF(aWorkerRef->IsPreventingShutdown(),
   4966                             mStatus >= WorkerStatus::Running);
   4967  }
   4968 
   4969  MOZ_ASSERT(!data->mWorkerRefs.Contains(aWorkerRef),
   4970             "Already know about this one!");
   4971 
   4972  if (aWorkerRef->IsPreventingShutdown()) {
   4973    data->mNumWorkerRefsPreventingShutdownStart += 1;
   4974    if (data->mNumWorkerRefsPreventingShutdownStart == 1) {
   4975      UpdateCCFlag(CCFlag::IneligibleForWorkerRef);
   4976    }
   4977  }
   4978 
   4979  data->mWorkerRefs.AppendElement(aWorkerRef);
   4980  return true;
   4981 }
   4982 
   4983 void WorkerPrivate::RemoveWorkerRef(WorkerRef* aWorkerRef) {
   4984  MOZ_ASSERT(aWorkerRef);
   4985  LOG(WorkerLog(),
   4986      ("WorkerPrivate::RemoveWorkerRef [%p] aWorkerRef: %p", this, aWorkerRef));
   4987  auto data = mWorkerThreadAccessible.Access();
   4988 
   4989  MOZ_ASSERT(data->mWorkerRefs.Contains(aWorkerRef),
   4990             "Didn't know about this one!");
   4991  data->mWorkerRefs.RemoveElement(aWorkerRef);
   4992 
   4993  if (aWorkerRef->IsPreventingShutdown()) {
   4994    data->mNumWorkerRefsPreventingShutdownStart -= 1;
   4995    if (!data->mNumWorkerRefsPreventingShutdownStart) {
   4996      UpdateCCFlag(CCFlag::EligibleForWorkerRef);
   4997    }
   4998  }
   4999 }
   5000 
   5001 void WorkerPrivate::NotifyWorkerRefs(WorkerStatus aStatus) {
   5002  auto data = mWorkerThreadAccessible.Access();
   5003 
   5004  NS_ASSERTION(aStatus > Closing, "Bad status!");
   5005 
   5006  LOG(WorkerLog(), ("WorkerPrivate::NotifyWorkerRefs [%p] aStatus: %u", this,
   5007                    static_cast<uint8_t>(aStatus)));
   5008 
   5009  for (auto* workerRef : data->mWorkerRefs.ForwardRange()) {
   5010    LOG(WorkerLog(), ("WorkerPrivate::NotifyWorkerRefs [%p] WorkerRefs(%s %p)",
   5011                      this, workerRef->mName, workerRef));
   5012    workerRef->Notify();
   5013  }
   5014 
   5015  AutoTArray<CheckedUnsafePtr<WorkerPrivate>, 10> children;
   5016  children.AppendElements(data->mChildWorkers);
   5017 
   5018  for (uint32_t index = 0; index < children.Length(); index++) {
   5019    if (!children[index]->Notify(aStatus)) {
   5020      NS_WARNING("Failed to notify child worker!");
   5021    }
   5022  }
   5023 }
   5024 
   5025 nsresult WorkerPrivate::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
   5026  NS_ENSURE_ARG(aTask);
   5027 
   5028  MutexAutoLock lock(mMutex);
   5029  // If we've already started running shutdown tasks, don't allow registering
   5030  // new ones.
   5031  if (mShutdownTasksRun) {
   5032    return NS_ERROR_UNEXPECTED;
   5033  }
   5034  return mShutdownTasks.AddTask(aTask);
   5035 }
   5036 
   5037 nsresult WorkerPrivate::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
   5038  NS_ENSURE_ARG(aTask);
   5039 
   5040  MutexAutoLock lock(mMutex);
   5041  return mShutdownTasks.RemoveTask(aTask);
   5042 }
   5043 
   5044 void WorkerPrivate::JSAsyncTaskStarted(JS::Dispatchable* aDispatchable) {
   5045  RefPtr<StrongWorkerRef> ref = StrongWorkerRef::Create(this, "JSAsyncTask");
   5046  MOZ_ASSERT_DEBUG_OR_FUZZING(ref);
   5047  if (NS_WARN_IF(!ref)) {
   5048    return;
   5049  }
   5050  MOZ_ALWAYS_TRUE(mPendingJSAsyncTasks.putNew(aDispatchable, std::move(ref)));
   5051 }
   5052 
   5053 void WorkerPrivate::JSAsyncTaskFinished(JS::Dispatchable* aDispatchable) {
   5054  mPendingJSAsyncTasks.remove(aDispatchable);
   5055 }
   5056 
   5057 void WorkerPrivate::RunShutdownTasks() {
   5058  TargetShutdownTaskSet::TasksArray shutdownTasks;
   5059 
   5060  {
   5061    MutexAutoLock lock(mMutex);
   5062    mShutdownTasksRun = true;
   5063    shutdownTasks = mShutdownTasks.Extract();
   5064  }
   5065 
   5066  for (const auto& task : shutdownTasks) {
   5067    task->TargetShutdown();
   5068  }
   5069  mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
   5070 }
   5071 
   5072 RefPtr<WorkerParentRef> WorkerPrivate::GetWorkerParentRef() const {
   5073  RefPtr<WorkerParentRef> ref(mParentRef);
   5074  return ref;
   5075 }
   5076 
   5077 void WorkerPrivate::AdjustNonblockingCCBackgroundActorCount(int32_t aCount) {
   5078  AssertIsOnWorkerThread();
   5079  auto data = mWorkerThreadAccessible.Access();
   5080  LOGV(("WorkerPrivate::AdjustNonblockingCCBackgroundActors [%p] (%d/%u)", this,
   5081        aCount, data->mNonblockingCCBackgroundActorCount));
   5082 
   5083 #ifdef DEBUG
   5084  if (aCount < 0) {
   5085    MOZ_ASSERT(data->mNonblockingCCBackgroundActorCount >=
   5086               (uint32_t)abs(aCount));
   5087  }
   5088 #endif
   5089 
   5090  data->mNonblockingCCBackgroundActorCount += aCount;
   5091 }
   5092 
   5093 void WorkerPrivate::UpdateCCFlag(const CCFlag aFlag) {
   5094  AssertIsOnWorkerThread();
   5095 
   5096  auto data = mWorkerThreadAccessible.Access();
   5097 
   5098 #ifdef DEBUG
   5099  switch (aFlag) {
   5100    case CCFlag::EligibleForWorkerRef: {
   5101      MOZ_ASSERT(!data->mNumWorkerRefsPreventingShutdownStart);
   5102      break;
   5103    }
   5104    case CCFlag::IneligibleForWorkerRef: {
   5105      MOZ_ASSERT(data->mNumWorkerRefsPreventingShutdownStart);
   5106      break;
   5107    }
   5108    case CCFlag::EligibleForChildWorker: {
   5109      MOZ_ASSERT(data->mChildWorkers.IsEmpty());
   5110      break;
   5111    }
   5112    case CCFlag::IneligibleForChildWorker: {
   5113      MOZ_ASSERT(!data->mChildWorkers.IsEmpty());
   5114      break;
   5115    }
   5116    case CCFlag::EligibleForTimeout: {
   5117      auto* timeoutManager =
   5118          data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   5119      MOZ_ASSERT(timeoutManager && !timeoutManager->HasTimeouts());
   5120      break;
   5121    }
   5122    case CCFlag::IneligibleForTimeout: {
   5123      auto* timeoutManager =
   5124          data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   5125      MOZ_ASSERT(!timeoutManager || timeoutManager->HasTimeouts());
   5126      break;
   5127    }
   5128    case CCFlag::CheckBackgroundActors: {
   5129      break;
   5130    }
   5131  }
   5132 #endif
   5133 
   5134  {
   5135    MutexAutoLock lock(mMutex);
   5136    if (mStatus > Canceling) {
   5137      mCCFlagSaysEligible = true;
   5138      return;
   5139    }
   5140  }
   5141  auto HasBackgroundActors = [nonblockingActorCount =
   5142                                  data->mNonblockingCCBackgroundActorCount]() {
   5143    RefPtr<PBackgroundChild> backgroundChild =
   5144        BackgroundChild::GetForCurrentThread();
   5145    MOZ_ASSERT(backgroundChild);
   5146    auto totalCount = backgroundChild->AllManagedActorsCount();
   5147    LOGV(("WorkerPrivate::UpdateCCFlag HasBackgroundActors: %s(%u/%u)",
   5148          totalCount > nonblockingActorCount ? "true" : "false", totalCount,
   5149          nonblockingActorCount));
   5150 
   5151    return totalCount > nonblockingActorCount;
   5152  };
   5153 
   5154  auto* timeoutManager =
   5155      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   5156 
   5157  bool noTimeouts{true};
   5158  if (timeoutManager) {
   5159    noTimeouts = !timeoutManager->HasTimeouts();
   5160  }
   5161 
   5162  bool eligibleForCC = data->mChildWorkers.IsEmpty() && noTimeouts &&
   5163                       !data->mNumWorkerRefsPreventingShutdownStart;
   5164 
   5165  // Only checking BackgroundActors when no strong WorkerRef, ChildWorker, and
   5166  // Timeout since the checking is expensive.
   5167  if (eligibleForCC) {
   5168    eligibleForCC = !HasBackgroundActors();
   5169  }
   5170 
   5171  {
   5172    MutexAutoLock lock(mMutex);
   5173    mCCFlagSaysEligible = eligibleForCC;
   5174  }
   5175 }
   5176 
   5177 bool WorkerPrivate::IsEligibleForCC() {
   5178  LOGV(("WorkerPrivate::IsEligibleForCC [%p]", this));
   5179  MutexAutoLock lock(mMutex);
   5180  if (mStatus > Canceling) {
   5181    return true;
   5182  }
   5183 
   5184  bool hasShutdownTasks = !mShutdownTasks.IsEmpty();
   5185  bool hasPendingEvents = false;
   5186  if (mThread) {
   5187    hasPendingEvents =
   5188        NS_SUCCEEDED(mThread->HasPendingEvents(&hasPendingEvents)) &&
   5189        hasPendingEvents;
   5190  }
   5191 
   5192  LOGV(("mMainThreadEventTarget: %s",
   5193        mMainThreadEventTarget->IsEmpty() ? "empty" : "non-empty"));
   5194  LOGV(("mMainThreadEventTargetForMessaging: %s",
   5195        mMainThreadEventTargetForMessaging->IsEmpty() ? "empty" : "non-empty"));
   5196  LOGV(("mMainThreadDebuggerEventTarget: %s",
   5197        mMainThreadDebuggeeEventTarget->IsEmpty() ? "empty" : "non-empty"));
   5198  LOGV(("mCCFlagSaysEligible: %s", mCCFlagSaysEligible ? "true" : "false"));
   5199  LOGV(("hasShutdownTasks: %s", hasShutdownTasks ? "true" : "false"));
   5200  LOGV(("hasPendingEvents: %s", hasPendingEvents ? "true" : "false"));
   5201 
   5202  return mMainThreadEventTarget->IsEmpty() &&
   5203         mMainThreadEventTargetForMessaging->IsEmpty() &&
   5204         mMainThreadDebuggeeEventTarget->IsEmpty() && mCCFlagSaysEligible &&
   5205         !hasShutdownTasks && !hasPendingEvents && mWorkerLoopIsIdle;
   5206 }
   5207 
   5208 void WorkerPrivate::CancelAllTimeouts() {
   5209  auto data = mWorkerThreadAccessible.Access();
   5210 
   5211  auto* timeoutManager =
   5212      data->mScope ? data->mScope->GetTimeoutManager() : nullptr;
   5213  if (timeoutManager) {
   5214    timeoutManager->ClearAllTimeouts();
   5215    if (!timeoutManager->HasTimeouts()) {
   5216      UpdateCCFlag(CCFlag::EligibleForTimeout);
   5217    }
   5218  }
   5219 
   5220  LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
   5221 }
   5222 
   5223 already_AddRefed<nsISerialEventTarget> WorkerPrivate::CreateNewSyncLoop(
   5224    WorkerStatus aFailStatus) {
   5225  AssertIsOnWorkerThread();
   5226  MOZ_ASSERT(
   5227      aFailStatus >= Canceling,
   5228      "Sync loops can be created when the worker is in Running/Closing state!");
   5229 
   5230  LOG(WorkerLog(), ("WorkerPrivate::CreateNewSyncLoop [%p] failstatus: %u",
   5231                    this, static_cast<uint8_t>(aFailStatus)));
   5232 
   5233  ThreadEventQueue* queue = nullptr;
   5234  {
   5235    MutexAutoLock lock(mMutex);
   5236 
   5237    if (mStatus >= aFailStatus) {
   5238      return nullptr;
   5239    }
   5240    queue = static_cast<ThreadEventQueue*>(mThread->EventQueue());
   5241  }
   5242 
   5243  nsCOMPtr<nsISerialEventTarget> nestedEventTarget = queue->PushEventQueue();
   5244  MOZ_ASSERT(nestedEventTarget);
   5245 
   5246  RefPtr<EventTarget> workerEventTarget =
   5247      new EventTarget(this, nestedEventTarget);
   5248 
   5249  {
   5250    // Modifications must be protected by mMutex in DEBUG builds, see comment
   5251    // about mSyncLoopStack in WorkerPrivate.h.
   5252 #ifdef DEBUG
   5253    MutexAutoLock lock(mMutex);
   5254 #endif
   5255 
   5256    mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
   5257  }
   5258 
   5259  return workerEventTarget.forget();
   5260 }
   5261 
   5262 nsresult WorkerPrivate::RunCurrentSyncLoop() {
   5263  AssertIsOnWorkerThread();
   5264  LOG(WorkerLog(), ("WorkerPrivate::RunCurrentSyncLoop [%p]", this));
   5265  RefPtr<WorkerThread> thread;
   5266  JSContext* cx = GetJSContext();
   5267  MOZ_ASSERT(cx);
   5268  // mThread is set before we enter, and is never changed during
   5269  // RunCurrentSyncLoop.
   5270  {
   5271    MutexAutoLock lock(mMutex);
   5272    // Copy to local so we don't trigger mutex analysis lower down
   5273    // mThread is set before we enter, and is never changed during
   5274    // RunCurrentSyncLoop copy to local so we don't trigger mutex analysis
   5275    thread = mThread;
   5276  }
   5277 
   5278  AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
   5279 
   5280  // This should not change between now and the time we finish running this sync
   5281  // loop.
   5282  uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
   5283 
   5284  SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex].get();
   5285 
   5286  AutoYieldJSThreadExecution yield;
   5287 
   5288  MOZ_ASSERT(loopInfo);
   5289  MOZ_ASSERT(!loopInfo->mHasRun);
   5290  MOZ_ASSERT(!loopInfo->mCompleted);
   5291 
   5292 #ifdef DEBUG
   5293  loopInfo->mHasRun = true;
   5294 #endif
   5295 
   5296  {
   5297    while (!loopInfo->mCompleted) {
   5298      bool normalRunnablesPending = false;
   5299 
   5300      // Don't block with the periodic GC timer running.
   5301      if (!NS_HasPendingEvents(thread)) {
   5302        SetGCTimerMode(IdleTimer);
   5303      }
   5304 
   5305      // Wait for something to do.
   5306      {
   5307        MutexAutoLock lock(mMutex);
   5308 
   5309        for (;;) {
   5310          while (mControlQueue.IsEmpty() && !normalRunnablesPending &&
   5311                 !(normalRunnablesPending = NS_HasPendingEvents(thread))) {
   5312            WaitForWorkerEvents();
   5313          }
   5314 
   5315          auto result = ProcessAllControlRunnablesLocked();
   5316          if (result != ProcessAllControlRunnablesResult::Nothing) {
   5317            // The state of the world may have changed. Recheck it if we need to
   5318            // continue.
   5319            normalRunnablesPending =
   5320                result == ProcessAllControlRunnablesResult::MayContinue &&
   5321                NS_HasPendingEvents(thread);
   5322 
   5323            // NB: If we processed a NotifyRunnable, we might have run
   5324            // non-control runnables, one of which may have shut down the
   5325            // sync loop.
   5326            if (loopInfo->mCompleted) {
   5327              break;
   5328            }
   5329          }
   5330 
   5331          // If we *didn't* run any control runnables, this should be unchanged.
   5332          MOZ_ASSERT(!loopInfo->mCompleted);
   5333 
   5334          if (normalRunnablesPending) {
   5335            break;
   5336          }
   5337        }
   5338      }
   5339 
   5340      if (normalRunnablesPending) {
   5341        // Make sure the periodic timer is running before we continue.
   5342        SetGCTimerMode(PeriodicTimer);
   5343 
   5344        MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false));
   5345 
   5346        // Now *might* be a good time to GC. Let the JS engine make the
   5347        // decision.
   5348        if (GetCurrentEventLoopGlobal()) {
   5349          // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
   5350          // Realm, so it's safe to try to GC.
   5351          MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
   5352          JS_MaybeGC(cx);
   5353        }
   5354      }
   5355    }
   5356  }
   5357 
   5358  // Make sure that the stack didn't change underneath us.
   5359  MOZ_ASSERT(mSyncLoopStack[currentLoopIndex].get() == loopInfo);
   5360 
   5361  return DestroySyncLoop(currentLoopIndex);
   5362 }
   5363 
   5364 nsresult WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex) {
   5365  MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
   5366  MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
   5367 
   5368  LOG(WorkerLog(),
   5369      ("WorkerPrivate::DestroySyncLoop [%p] aLoopIndex: %u", this, aLoopIndex));
   5370 
   5371  AutoYieldJSThreadExecution yield;
   5372 
   5373  // We're about to delete the loop, stash its event target and result.
   5374  const auto& loopInfo = mSyncLoopStack[aLoopIndex];
   5375 
   5376  nsresult result = loopInfo->mResult;
   5377 
   5378  {
   5379    RefPtr<nsIEventTarget> nestedEventTarget(
   5380        loopInfo->mEventTarget->GetNestedEventTarget());
   5381    MOZ_ASSERT(nestedEventTarget);
   5382 
   5383    loopInfo->mEventTarget->Shutdown();
   5384 
   5385    {
   5386      MutexAutoLock lock(mMutex);
   5387      static_cast<ThreadEventQueue*>(mThread->EventQueue())
   5388          ->PopEventQueue(nestedEventTarget);
   5389    }
   5390  }
   5391 
   5392  // Are we making a 1 -> 0 transition here?
   5393  if (mSyncLoopStack.Length() == 1) {
   5394    if ((mPostSyncLoopOperations & eDispatchCancelingRunnable)) {
   5395      LOG(WorkerLog(),
   5396          ("WorkerPrivate::DestroySyncLoop [%p] Dispatching CancelingRunnables",
   5397           this));
   5398      DispatchCancelingRunnable();
   5399    }
   5400 
   5401    mPostSyncLoopOperations = 0;
   5402  }
   5403 
   5404  {
   5405    // Modifications must be protected by mMutex in DEBUG builds, see comment
   5406    // about mSyncLoopStack in WorkerPrivate.h.
   5407 #ifdef DEBUG
   5408    MutexAutoLock lock(mMutex);
   5409 #endif
   5410 
   5411    // This will delete |loopInfo|!
   5412    mSyncLoopStack.RemoveElementAt(aLoopIndex);
   5413  }
   5414 
   5415  return result;
   5416 }
   5417 
   5418 void WorkerPrivate::DispatchCancelingRunnable() {
   5419  // Here we use a normal runnable to know when the current JS chunk of code
   5420  // is finished. We cannot use a WorkerRunnable because they are not
   5421  // accepted any more by the worker, and we do not want to use a
   5422  // WorkerControlRunnable because they are immediately executed.
   5423 
   5424  LOG(WorkerLog(), ("WorkerPrivate::DispatchCancelingRunnable [%p]", this));
   5425  RefPtr<CancelingRunnable> r = new CancelingRunnable();
   5426  {
   5427    MutexAutoLock lock(mMutex);
   5428    mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
   5429  }
   5430 
   5431  // At the same time, we want to be sure that we interrupt infinite loops.
   5432  // The following runnable starts a timer that cancel the worker, from the
   5433  // parent thread, after CANCELING_TIMEOUT millseconds.
   5434  LOG(WorkerLog(), ("WorkerPrivate::DispatchCancelingRunnable [%p] Setup a "
   5435                    "timeout canceling",
   5436                    this));
   5437  RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
   5438      new CancelingWithTimeoutOnParentRunnable(this);
   5439  rr->Dispatch(this);
   5440 }
   5441 
   5442 void WorkerPrivate::ReportUseCounters() {
   5443  AssertIsOnWorkerThread();
   5444 
   5445  if (mReportedUseCounters) {
   5446    return;
   5447  }
   5448  mReportedUseCounters = true;
   5449 
   5450  if (IsChromeWorker()) {
   5451    return;
   5452  }
   5453 
   5454  const size_t kind = Kind();
   5455  switch (kind) {
   5456    case WorkerKindDedicated:
   5457      glean::use_counter::dedicated_workers_destroyed.Add();
   5458      break;
   5459    case WorkerKindShared:
   5460      glean::use_counter::shared_workers_destroyed.Add();
   5461      break;
   5462    case WorkerKindService:
   5463      glean::use_counter::service_workers_destroyed.Add();
   5464      break;
   5465    default:
   5466      MOZ_ASSERT(false, "Unknown worker kind");
   5467      return;
   5468  }
   5469 
   5470  Maybe<nsCString> workerPathForLogging;
   5471  const bool dumpCounters = StaticPrefs::dom_use_counters_dump_worker();
   5472  if (dumpCounters) {
   5473    nsAutoCString path(Domain());
   5474    path.AppendLiteral("(");
   5475    NS_ConvertUTF16toUTF8 script(ScriptURL());
   5476    path.Append(script);
   5477    path.AppendPrintf(", 0x%p)", this);
   5478    workerPathForLogging.emplace(std::move(path));
   5479  }
   5480 
   5481  const size_t count = static_cast<size_t>(UseCounterWorker::Count);
   5482 
   5483  const auto workerKind = Kind();
   5484  for (size_t c = 0; c < count; ++c) {
   5485    if (!GetUseCounter(static_cast<UseCounterWorker>(c))) {
   5486      continue;
   5487    }
   5488    const char* metricName =
   5489        IncrementWorkerUseCounter(static_cast<UseCounterWorker>(c), workerKind);
   5490    if (dumpCounters) {
   5491      printf_stderr("USE_COUNTER_WORKER: %s - %s\n", metricName,
   5492                    workerPathForLogging->get());
   5493    }
   5494  }
   5495 }
   5496 
   5497 void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget,
   5498                                 nsresult aResult) {
   5499  AssertValidSyncLoop(aSyncLoopTarget);
   5500 
   5501  if (!MaybeStopSyncLoop(aSyncLoopTarget, aResult)) {
   5502    // TODO: I wonder if we should really ever crash here given the assert.
   5503    MOZ_CRASH("Unknown sync loop!");
   5504  }
   5505 }
   5506 
   5507 bool WorkerPrivate::MaybeStopSyncLoop(nsIEventTarget* aSyncLoopTarget,
   5508                                      nsresult aResult) {
   5509  AssertIsOnWorkerThread();
   5510 
   5511  for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
   5512    const auto& loopInfo = mSyncLoopStack[index - 1];
   5513    MOZ_ASSERT(loopInfo);
   5514    MOZ_ASSERT(loopInfo->mEventTarget);
   5515 
   5516    if (loopInfo->mEventTarget == aSyncLoopTarget) {
   5517      // Can't assert |loop->mHasRun| here because dispatch failures can cause
   5518      // us to bail out early.
   5519      MOZ_ASSERT(!loopInfo->mCompleted);
   5520 
   5521      loopInfo->mResult = aResult;
   5522      loopInfo->mCompleted = true;
   5523 
   5524      loopInfo->mEventTarget->Disable();
   5525 
   5526      return true;
   5527    }
   5528 
   5529    MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
   5530  }
   5531 
   5532  return false;
   5533 }
   5534 
   5535 #ifdef DEBUG
   5536 void WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) {
   5537  MOZ_ASSERT(aSyncLoopTarget);
   5538 
   5539  EventTarget* workerTarget;
   5540  nsresult rv = aSyncLoopTarget->QueryInterface(
   5541      kDEBUGWorkerEventTargetIID, reinterpret_cast<void**>(&workerTarget));
   5542  MOZ_ASSERT(NS_SUCCEEDED(rv));
   5543  MOZ_ASSERT(workerTarget);
   5544 
   5545  bool valid = false;
   5546 
   5547  {
   5548    MutexAutoLock lock(mMutex);
   5549 
   5550    for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
   5551      const auto& loopInfo = mSyncLoopStack[index];
   5552      MOZ_ASSERT(loopInfo);
   5553      MOZ_ASSERT(loopInfo->mEventTarget);
   5554 
   5555      if (loopInfo->mEventTarget == aSyncLoopTarget) {
   5556        valid = true;
   5557        break;
   5558      }
   5559 
   5560      MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
   5561    }
   5562  }
   5563 
   5564  MOZ_ASSERT(valid);
   5565 }
   5566 #endif
   5567 
   5568 void WorkerPrivate::PostMessageToParent(
   5569    JSContext* aCx, JS::Handle<JS::Value> aMessage,
   5570    const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) {
   5571  LOG(WorkerLog(), ("WorkerPrivate::PostMessageToParent [%p]", this));
   5572  AssertIsOnWorkerThread();
   5573  MOZ_DIAGNOSTIC_ASSERT(IsDedicatedWorker());
   5574 
   5575  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
   5576 
   5577  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
   5578                                                          &transferable);
   5579  if (NS_WARN_IF(aRv.Failed())) {
   5580    return;
   5581  }
   5582 
   5583  RefPtr<MessageEventToParentRunnable> runnable =
   5584      new MessageEventToParentRunnable(this);
   5585 
   5586  JS::CloneDataPolicy clonePolicy;
   5587 
   5588  // Parent and dedicated workers are always part of the same cluster.
   5589  clonePolicy.allowIntraClusterClonableSharedObjects();
   5590 
   5591  if (IsSharedMemoryAllowed()) {
   5592    clonePolicy.allowSharedMemoryObjects();
   5593  }
   5594 
   5595  runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
   5596 
   5597  if (NS_WARN_IF(aRv.Failed())) {
   5598    return;
   5599  }
   5600 
   5601  if (!runnable->Dispatch(this)) {
   5602    aRv = NS_ERROR_FAILURE;
   5603  }
   5604 }
   5605 
   5606 void WorkerPrivate::EnterDebuggerEventLoop() {
   5607  auto data = mWorkerThreadAccessible.Access();
   5608 
   5609  JSContext* cx = GetJSContext();
   5610  MOZ_ASSERT(cx);
   5611 
   5612  AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
   5613  AutoYieldJSThreadExecution yield;
   5614 
   5615  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
   5616 
   5617  uint32_t currentEventLoopLevel = ++data->mDebuggerEventLoopLevel;
   5618 
   5619  while (currentEventLoopLevel <= data->mDebuggerEventLoopLevel) {
   5620    bool debuggerRunnablesPending = false;
   5621 
   5622    {
   5623      MutexAutoLock lock(mMutex);
   5624 
   5625      debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
   5626    }
   5627 
   5628    // Don't block with the periodic GC timer running.
   5629    if (!debuggerRunnablesPending) {
   5630      SetGCTimerMode(IdleTimer);
   5631    }
   5632 
   5633    // Wait for something to do
   5634    {
   5635      MutexAutoLock lock(mMutex);
   5636 
   5637      if (StaticPrefs::javascript_options_use_js_microtask_queue()) {
   5638        // When JS microtask queue is enabled, check for debugger microtasks
   5639        // directly from the JS engine
   5640        while (mControlQueue.IsEmpty() &&
   5641               !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
   5642               !JS::HasDebuggerMicroTasks(cx)) {
   5643          WaitForWorkerEvents();
   5644        }
   5645      } else {
   5646        // Legacy path: check the debugger microtask queue in
   5647        // CycleCollectedJSContext
   5648        std::deque<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
   5649            ccjscx->GetDebuggerMicroTaskQueue();
   5650        while (mControlQueue.IsEmpty() &&
   5651               !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
   5652               debuggerMtQueue.empty()) {
   5653          WaitForWorkerEvents();
   5654        }
   5655      }
   5656 
   5657      ProcessAllControlRunnablesLocked();
   5658 
   5659      // XXXkhuey should we abort JS on the stack here if we got Abort above?
   5660    }
   5661    ccjscx->PerformDebuggerMicroTaskCheckpoint();
   5662    if (debuggerRunnablesPending) {
   5663      // Start the periodic GC timer if it is not already running.
   5664      SetGCTimerMode(PeriodicTimer);
   5665 
   5666      ProcessSingleDebuggerRunnable();
   5667 
   5668      // Now *might* be a good time to GC. Let the JS engine make the decision.
   5669      if (GetCurrentEventLoopGlobal()) {
   5670        // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
   5671        // Realm, so it's safe to try to GC.
   5672        MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
   5673        JS_MaybeGC(cx);
   5674      }
   5675    }
   5676  }
   5677 }
   5678 
   5679 void WorkerPrivate::LeaveDebuggerEventLoop() {
   5680  auto data = mWorkerThreadAccessible.Access();
   5681 
   5682  // TODO: Why lock the mutex if we're accessing data accessible to one thread
   5683  // only?
   5684  MutexAutoLock lock(mMutex);
   5685 
   5686  if (data->mDebuggerEventLoopLevel > 0) {
   5687    --data->mDebuggerEventLoopLevel;
   5688  }
   5689 }
   5690 
   5691 void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) {
   5692  AssertIsOnWorkerThread();
   5693 
   5694  mDebugger->PostMessageToDebugger(aMessage);
   5695  RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
   5696  {
   5697    MutexAutoLock lock(mMutex);
   5698    if (!mRemoteDebugger) {
   5699      return;
   5700    }
   5701    remoteDebugger = mRemoteDebugger;
   5702  }
   5703  MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger);
   5704  (void)remoteDebugger->SendPostMessageToDebugger(nsAutoString(aMessage));
   5705 }
   5706 
   5707 void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler,
   5708                                         ErrorResult& aRv) {
   5709  AssertIsOnWorkerThread();
   5710 
   5711  RefPtr<DebuggerImmediateRunnable> runnable =
   5712      new DebuggerImmediateRunnable(this, aHandler);
   5713  if (!runnable->Dispatch(this)) {
   5714    aRv.Throw(NS_ERROR_FAILURE);
   5715  }
   5716 }
   5717 
   5718 void WorkerPrivate::ReportErrorToDebugger(const nsACString& aFilename,
   5719                                          uint32_t aLineno,
   5720                                          const nsAString& aMessage) {
   5721  AssertIsOnWorkerThread();
   5722  mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
   5723  RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
   5724  {
   5725    MutexAutoLock lock(mMutex);
   5726    if (!mRemoteDebugger) {
   5727      return;
   5728    }
   5729    remoteDebugger = mRemoteDebugger;
   5730  }
   5731  MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger);
   5732  (void)remoteDebugger->SendReportErrorToDebugger(RemoteWorkerDebuggerErrorInfo(
   5733      nsAutoCString(aFilename), aLineno, nsAutoString(aMessage)));
   5734 }
   5735 
   5736 void WorkerPrivate::UpdateWindowIDToDebugger(const uint64_t& aWindowID,
   5737                                             const bool& aIsAdd) {
   5738  AssertIsOnWorkerThread();
   5739  // only need to update the remote debugger since local debugger grab the
   5740  // windowIDs information from RemoteWorkerChild directly.
   5741 
   5742  RefPtr<RemoteWorkerDebuggerChild> remoteDebugger;
   5743  {
   5744    MutexAutoLock lock(mMutex);
   5745    if (!mRemoteDebugger) {
   5746      return;
   5747    }
   5748    remoteDebugger = mRemoteDebugger;
   5749  }
   5750  MOZ_ASSERT_DEBUG_OR_FUZZING(remoteDebugger);
   5751  if (aIsAdd) {
   5752    (void)remoteDebugger->SendAddWindowID(aWindowID);
   5753  } else {
   5754    (void)remoteDebugger->SendRemoveWindowID(aWindowID);
   5755  }
   5756 }
   5757 
   5758 bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
   5759  auto data = mWorkerThreadAccessible.Access();
   5760 
   5761  // Yield execution while notifying out-of-module WorkerRefs and cancelling
   5762  // runnables.
   5763  AutoYieldJSThreadExecution yield;
   5764 
   5765  NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
   5766 
   5767  RefPtr<EventTarget> eventTarget;
   5768 
   5769  // Save the old status and set the new status.
   5770  {
   5771    MutexAutoLock lock(mMutex);
   5772 
   5773    LOG(WorkerLog(),
   5774        ("WorkerPrivate::NotifyInternal [%p] mStatus: %u, aStatus: %u", this,
   5775         static_cast<uint8_t>(mStatus), static_cast<uint8_t>(aStatus)));
   5776 
   5777    if (mStatus >= aStatus) {
   5778      return true;
   5779    }
   5780 
   5781    MOZ_ASSERT_IF(aStatus == Killing,
   5782                  mStatus == Canceling && mParentStatus == Canceling);
   5783 
   5784    mStatus = aStatus;
   5785 
   5786    // Mark parent status as closing immediately to avoid new events being
   5787    // dispatched after we clear the queue below.
   5788    if (aStatus == Closing) {
   5789      Close();
   5790    }
   5791 
   5792    // Synchronize the mParentStatus with mStatus, such that event dispatching
   5793    // will fail in proper after WorkerPrivate gets into Killing status.
   5794    if (aStatus >= Killing) {
   5795      mParentStatus = aStatus;
   5796    }
   5797  }
   5798 
   5799  // Status transistion to "Canceling"/"Killing", mark the scope as dying when
   5800  // "Canceling," or shutdown the StorageManager when "Killing."
   5801  if (aStatus >= Canceling) {
   5802    if (data->mScope) {
   5803      if (aStatus == Canceling) {
   5804        data->mScope->NoteTerminating();
   5805      } else {
   5806        data->mScope->NoteShuttingDown();
   5807      }
   5808    }
   5809  }
   5810 
   5811  if (aStatus >= Closing) {
   5812    CancelAllTimeouts();
   5813 
   5814    JSContext* cx = GetJSContext();
   5815    if (cx) {
   5816      // This will invoke the JS async task finished callback for cancellable
   5817      // JS tasks, which will invoke JSAsyncTaskFinished and remove from
   5818      // mPendingJSAsyncTasks.
   5819      //
   5820      // There may still be outstanding JS tasks for things that couldn't be
   5821      // cancelled. These must either finish normally, or be blocked on
   5822      // through a call to JS::ShutdownAsyncTasks. Cycle collector shutdown
   5823      // will call this during worker shutdown.
   5824      JS::CancelAsyncTasks(cx);
   5825    }
   5826  }
   5827 
   5828  if (aStatus == Closing && GlobalScope()) {
   5829    GlobalScope()->SetIsNotEligibleForMessaging();
   5830  }
   5831 
   5832  // Let all our holders know the new status.
   5833  if (aStatus == Canceling) {
   5834    NotifyWorkerRefs(aStatus);
   5835  }
   5836 
   5837  if (aStatus == Canceling && mRemoteWorkerNonLifeCycleOpController) {
   5838    mRemoteWorkerNonLifeCycleOpController->TransistionStateToCanceled();
   5839  }
   5840 
   5841  if (aStatus == Killing && mRemoteWorkerNonLifeCycleOpController) {
   5842    mRemoteWorkerNonLifeCycleOpController->TransistionStateToKilled();
   5843    mRemoteWorkerNonLifeCycleOpController = nullptr;
   5844  }
   5845 
   5846  // If the worker script never ran, or failed to compile, we don't need to do
   5847  // anything else.
   5848  WorkerGlobalScope* global = GlobalScope();
   5849  if (!global) {
   5850    if (aStatus == Canceling) {
   5851      MOZ_ASSERT(!data->mCancelBeforeWorkerScopeConstructed);
   5852      data->mCancelBeforeWorkerScopeConstructed.Flip();
   5853    }
   5854    return true;
   5855  }
   5856 
   5857  // Don't abort the script now, but we dispatch a runnable to do it when the
   5858  // current JS frame is executed.
   5859  if (aStatus == Closing) {
   5860    if (!mSyncLoopStack.IsEmpty()) {
   5861      LOG(WorkerLog(), ("WorkerPrivate::NotifyInternal [%p] request to "
   5862                        "dispatch canceling runnables...",
   5863                        this));
   5864      mPostSyncLoopOperations |= eDispatchCancelingRunnable;
   5865    } else {
   5866      DispatchCancelingRunnable();
   5867    }
   5868    return true;
   5869  }
   5870 
   5871  MOZ_ASSERT(aStatus == Canceling || aStatus == Killing);
   5872 
   5873  LOG(WorkerLog(), ("WorkerPrivate::NotifyInternal [%p] abort script", this));
   5874 
   5875  // Always abort the script.
   5876  return false;
   5877 }
   5878 
   5879 void WorkerPrivate::ReportError(JSContext* aCx,
   5880                                JS::ConstUTF8CharsZ aToStringResult,
   5881                                JSErrorReport* aReport) {
   5882  auto data = mWorkerThreadAccessible.Access();
   5883 
   5884  if (!MayContinueRunning() || data->mErrorHandlerRecursionCount == 2) {
   5885    return;
   5886  }
   5887 
   5888  NS_ASSERTION(data->mErrorHandlerRecursionCount == 0 ||
   5889                   data->mErrorHandlerRecursionCount == 1,
   5890               "Bad recursion logic!");
   5891 
   5892  UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>();
   5893  if (aReport) {
   5894    report->AssignErrorReport(aReport);
   5895  }
   5896 
   5897  JS::ExceptionStack exnStack(aCx);
   5898  // NOTE: This function is used both for errors and warnings, and warnings
   5899  //       can be reported while there's a pending exception.
   5900  //       Warnings are always reported with non-null JSErrorReport.
   5901  if (!aReport || !aReport->isWarning()) {
   5902    MOZ_ASSERT(JS_IsExceptionPending(aCx));
   5903    if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
   5904      JS_ClearPendingException(aCx);
   5905      return;
   5906    }
   5907 
   5908    JS::Rooted<JSObject*> stack(aCx), stackGlobal(aCx);
   5909    xpc::FindExceptionStackForConsoleReport(
   5910        nullptr, exnStack.exception(), exnStack.stack(), &stack, &stackGlobal);
   5911 
   5912    if (stack) {
   5913      JSAutoRealm ar(aCx, stackGlobal);
   5914      report->SerializeWorkerStack(aCx, this, stack);
   5915    }
   5916  }
   5917 
   5918  if (report->mMessage.IsEmpty() && aToStringResult) {
   5919    nsDependentCString toStringResult(aToStringResult.c_str());
   5920    if (!AppendUTF8toUTF16(toStringResult, report->mMessage,
   5921                           mozilla::fallible)) {
   5922      // Try again, with only a 1 KB string. Do this infallibly this time.
   5923      // If the user doesn't have 1 KB to spare we're done anyways.
   5924      size_t index = std::min<size_t>(1024, toStringResult.Length());
   5925 
   5926      // Drop the last code point that may be cropped.
   5927      index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
   5928 
   5929      nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
   5930                                                 index);
   5931      AppendUTF8toUTF16(truncatedToStringResult, report->mMessage);
   5932    }
   5933  }
   5934 
   5935  data->mErrorHandlerRecursionCount++;
   5936 
   5937  // Don't want to run the scope's error handler if this is a recursive error or
   5938  // if we ran out of memory.
   5939  bool fireAtScope = data->mErrorHandlerRecursionCount == 1 &&
   5940                     report->mErrorNumber != JSMSG_OUT_OF_MEMORY &&
   5941                     JS::CurrentGlobalOrNull(aCx);
   5942 
   5943  WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr,
   5944                                 std::move(report), 0, exnStack.exception());
   5945 
   5946  data->mErrorHandlerRecursionCount--;
   5947 }
   5948 
   5949 // static
   5950 void WorkerPrivate::ReportErrorToConsole(
   5951    uint32_t aErrorFlags, const nsCString& aCategory,
   5952    nsContentUtils::PropertiesFile aFile, const nsCString& aMessageName,
   5953    const nsTArray<nsString>& aParams,
   5954    const mozilla::SourceLocation& aLocation) {
   5955  WorkerPrivate* wp = nullptr;
   5956  if (!NS_IsMainThread()) {
   5957    wp = GetCurrentThreadWorkerPrivate();
   5958  }
   5959 
   5960  ReportErrorToConsoleRunnable::Report(wp, aErrorFlags, aCategory, aFile,
   5961                                       aMessageName, aParams, aLocation);
   5962 }
   5963 
   5964 int32_t WorkerPrivate::SetTimeout(JSContext* aCx, TimeoutHandler* aHandler,
   5965                                  int32_t aTimeout, bool aIsInterval,
   5966                                  Timeout::Reason aReason, ErrorResult& aRv) {
   5967  auto data = mWorkerThreadAccessible.Access();
   5968  MOZ_ASSERT(aHandler);
   5969 
   5970  WorkerGlobalScope* globalScope = GlobalScope();
   5971  MOZ_DIAGNOSTIC_ASSERT(globalScope);
   5972  auto* timeoutManager = globalScope->GetTimeoutManager();
   5973  MOZ_DIAGNOSTIC_ASSERT(timeoutManager);
   5974  int32_t timerId = -1;
   5975  WorkerStatus status;
   5976  {
   5977    MutexAutoLock lock(mMutex);
   5978    status = mStatus;
   5979  }
   5980  // If the worker is trying to call setTimeout/setInterval and the
   5981  // worker itself which has initiated the close process.
   5982  if (status >= Closing) {
   5983    return timeoutManager->GetTimeoutId(aReason);
   5984  }
   5985  bool hadTimeouts = timeoutManager->HasTimeouts();
   5986  nsresult rv = timeoutManager->SetTimeout(aHandler, aTimeout, aIsInterval,
   5987                                           aReason, &timerId);
   5988  if (NS_FAILED(rv)) {
   5989    aRv.Throw(NS_ERROR_FAILURE);
   5990    return timerId;
   5991  }
   5992  if (!hadTimeouts) {
   5993    UpdateCCFlag(CCFlag::IneligibleForTimeout);
   5994  }
   5995  return timerId;
   5996 }
   5997 
   5998 void WorkerPrivate::ClearTimeout(int32_t aId, Timeout::Reason aReason) {
   5999  MOZ_ASSERT(aReason == Timeout::Reason::eTimeoutOrInterval,
   6000             "This timeout reason doesn't support cancellation.");
   6001  WorkerGlobalScope* globalScope = GlobalScope();
   6002  MOZ_DIAGNOSTIC_ASSERT(globalScope);
   6003  auto* timeoutManager = globalScope->GetTimeoutManager();
   6004  MOZ_DIAGNOSTIC_ASSERT(timeoutManager);
   6005  timeoutManager->ClearTimeout(aId, aReason);
   6006  if (!timeoutManager->HasTimeouts()) {
   6007    UpdateCCFlag(CCFlag::EligibleForTimeout);
   6008  }
   6009 }
   6010 
   6011 void WorkerPrivate::StartCancelingTimer() {
   6012  AssertIsOnParentThread();
   6013 
   6014  // return if mCancelingTimer has already existed.
   6015  if (mCancelingTimer) {
   6016    return;
   6017  }
   6018 
   6019  auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; });
   6020 
   6021  if (WorkerPrivate* parent = GetParent()) {
   6022    mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());
   6023  } else {
   6024    mCancelingTimer = NS_NewTimer();
   6025  }
   6026 
   6027  if (NS_WARN_IF(!mCancelingTimer)) {
   6028    return;
   6029  }
   6030 
   6031  // This is not needed if we are already in an advanced shutdown state.
   6032  {
   6033    MutexAutoLock lock(mMutex);
   6034    if (ParentStatus() >= Canceling) {
   6035      return;
   6036    }
   6037  }
   6038 
   6039  uint32_t cancelingTimeoutMillis =
   6040      StaticPrefs::dom_worker_canceling_timeoutMilliseconds();
   6041 
   6042  RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this);
   6043  nsresult rv = mCancelingTimer->InitWithCallback(
   6044      callback, cancelingTimeoutMillis, nsITimer::TYPE_ONE_SHOT);
   6045  if (NS_WARN_IF(NS_FAILED(rv))) {
   6046    return;
   6047  }
   6048 
   6049  errorCleanup.release();
   6050 }
   6051 
   6052 void WorkerPrivate::UpdateContextOptionsInternal(
   6053    JSContext* aCx, const JS::ContextOptions& aContextOptions) {
   6054  auto data = mWorkerThreadAccessible.Access();
   6055 
   6056  JS::ContextOptionsRef(aCx) = aContextOptions;
   6057 
   6058  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6059    data->mChildWorkers[index]->UpdateContextOptions(aContextOptions);
   6060  }
   6061 }
   6062 
   6063 void WorkerPrivate::UpdateLanguagesInternal(
   6064    const nsTArray<nsString>& aLanguages) {
   6065  WorkerGlobalScope* globalScope = GlobalScope();
   6066  RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
   6067  if (nav) {
   6068    nav->SetLanguages(aLanguages);
   6069  }
   6070 
   6071  auto data = mWorkerThreadAccessible.Access();
   6072  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6073    data->mChildWorkers[index]->UpdateLanguages(aLanguages);
   6074  }
   6075 
   6076  RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
   6077 
   6078  event->InitEvent(u"languagechange"_ns, false, false);
   6079  event->SetTrusted(true);
   6080 
   6081  globalScope->DispatchEvent(*event);
   6082 }
   6083 
   6084 void WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(
   6085    JSContext* aCx, JSGCParamKey aKey, Maybe<uint32_t> aValue) {
   6086  auto data = mWorkerThreadAccessible.Access();
   6087 
   6088  if (aValue) {
   6089    JS_SetGCParameter(aCx, aKey, *aValue);
   6090  } else {
   6091    JS_ResetGCParameter(aCx, aKey);
   6092  }
   6093 
   6094  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6095    data->mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
   6096  }
   6097 }
   6098 
   6099 #ifdef JS_GC_ZEAL
   6100 void WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
   6101                                         uint32_t aFrequency) {
   6102  auto data = mWorkerThreadAccessible.Access();
   6103 
   6104  JS::SetGCZeal(aCx, aGCZeal, aFrequency);
   6105 
   6106  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6107    data->mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
   6108  }
   6109 }
   6110 #endif
   6111 
   6112 void WorkerPrivate::SetLowMemoryStateInternal(JSContext* aCx, bool aState) {
   6113  auto data = mWorkerThreadAccessible.Access();
   6114 
   6115  JS::SetLowMemoryState(aCx, aState);
   6116 
   6117  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6118    data->mChildWorkers[index]->SetLowMemoryState(aState);
   6119  }
   6120 }
   6121 
   6122 void WorkerPrivate::SetCCCollectedAnything(bool collectedAnything) {
   6123  mWorkerThreadAccessible.Access()->mCCCollectedAnything = collectedAnything;
   6124 }
   6125 
   6126 uint32_t WorkerPrivate::GetCurrentTimerNestingLevel() const {
   6127  auto data = mWorkerThreadAccessible.Access();
   6128  return data->mScope
   6129             ? data->mScope->GetTimeoutManager()->GetNestingLevelForWorker()
   6130             : 0;
   6131 }
   6132 
   6133 bool WorkerPrivate::isLastCCCollectedAnything() {
   6134  return mWorkerThreadAccessible.Access()->mCCCollectedAnything;
   6135 }
   6136 
   6137 void WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
   6138                                           bool aCollectChildren) {
   6139  // Perform GC followed by CC (the CC is triggered by
   6140  // WorkerJSRuntime::CustomGCCallback at the end of the collection).
   6141 
   6142  auto data = mWorkerThreadAccessible.Access();
   6143 
   6144  if (!GlobalScope()) {
   6145    // We haven't compiled anything yet. Just bail out.
   6146    return;
   6147  }
   6148 
   6149  if (aShrinking || aCollectChildren) {
   6150    JS::PrepareForFullGC(aCx);
   6151 
   6152    if (aShrinking && mSyncLoopStack.IsEmpty()) {
   6153      JS::NonIncrementalGC(aCx, JS::GCOptions::Shrink,
   6154                           JS::GCReason::DOM_WORKER);
   6155 
   6156      // Check whether the CC collected anything and if so GC again. This is
   6157      // necessary to collect all garbage.
   6158      if (data->mCCCollectedAnything) {
   6159        JS::NonIncrementalGC(aCx, JS::GCOptions::Normal,
   6160                             JS::GCReason::DOM_WORKER);
   6161      }
   6162 
   6163      if (!aCollectChildren) {
   6164        LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
   6165      }
   6166    } else {
   6167      JS::NonIncrementalGC(aCx, JS::GCOptions::Normal,
   6168                           JS::GCReason::DOM_WORKER);
   6169      LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
   6170    }
   6171  } else {
   6172    JS_MaybeGC(aCx);
   6173    LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
   6174  }
   6175 
   6176  if (aCollectChildren) {
   6177    for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6178      data->mChildWorkers[index]->GarbageCollect(aShrinking);
   6179    }
   6180  }
   6181 }
   6182 
   6183 void WorkerPrivate::CycleCollectInternal(bool aCollectChildren) {
   6184  auto data = mWorkerThreadAccessible.Access();
   6185 
   6186  nsCycleCollector_collect(CCReason::WORKER, nullptr);
   6187 
   6188  if (aCollectChildren) {
   6189    for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6190      data->mChildWorkers[index]->CycleCollect();
   6191    }
   6192  }
   6193 }
   6194 
   6195 void WorkerPrivate::MemoryPressureInternal() {
   6196  auto data = mWorkerThreadAccessible.Access();
   6197 
   6198  if (data->mScope) {
   6199    RefPtr<Console> console = data->mScope->GetConsoleIfExists();
   6200    if (console) {
   6201      console->ClearStorage();
   6202    }
   6203 
   6204    RefPtr<Performance> performance = data->mScope->GetPerformanceIfExists();
   6205    if (performance) {
   6206      performance->MemoryPressure();
   6207    }
   6208 
   6209    data->mScope->RemoveReportRecords();
   6210  }
   6211 
   6212  if (data->mDebuggerScope) {
   6213    RefPtr<Console> console = data->mDebuggerScope->GetConsoleIfExists();
   6214    if (console) {
   6215      console->ClearStorage();
   6216    }
   6217  }
   6218 
   6219  for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
   6220    data->mChildWorkers[index]->MemoryPressure();
   6221  }
   6222 }
   6223 
   6224 void WorkerPrivate::SetThread(WorkerThread* aThread) {
   6225  if (aThread) {
   6226 #ifdef DEBUG
   6227    {
   6228      bool isOnCurrentThread;
   6229      MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
   6230      MOZ_ASSERT(!isOnCurrentThread);
   6231    }
   6232 #endif
   6233 
   6234    MOZ_ASSERT(!mPRThread);
   6235    mPRThread = PRThreadFromThread(aThread);
   6236    MOZ_ASSERT(mPRThread);
   6237 
   6238    mWorkerThreadAccessible.Transfer(mPRThread);
   6239  } else {
   6240    MOZ_ASSERT(mPRThread);
   6241  }
   6242 }
   6243 
   6244 void WorkerPrivate::SetWorkerPrivateInWorkerThread(
   6245    WorkerThread* const aThread) {
   6246  LOG(WorkerLog(),
   6247      ("WorkerPrivate::SetWorkerPrivateInWorkerThread [%p]", this));
   6248  MutexAutoLock lock(mMutex);
   6249 
   6250  MOZ_ASSERT(!mThread);
   6251  MOZ_ASSERT(mStatus == Pending);
   6252 
   6253  mThread = aThread;
   6254  mThread->SetWorker(WorkerThreadFriendKey{}, this);
   6255 
   6256  if (!mPreStartRunnables.IsEmpty()) {
   6257    for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
   6258      MOZ_ALWAYS_SUCCEEDS(mThread->DispatchAnyThread(
   6259          WorkerThreadFriendKey{}, mPreStartRunnables[index]));
   6260    }
   6261    // Don't clear mPreStartRunnables here, it will be cleared in the beginning
   6262    // of WorkerPrivate::DoRunLoop() or when in WorkerPrivate::RunLoopNeverRan()
   6263  }
   6264 }
   6265 
   6266 void WorkerPrivate::ResetWorkerPrivateInWorkerThread() {
   6267  LOG(WorkerLog(),
   6268      ("WorkerPrivate::ResetWorkerPrivateInWorkerThread [%p]", this));
   6269  RefPtr<WorkerThread> doomedThread;
   6270 
   6271  // Release the mutex before doomedThread.
   6272  MutexAutoLock lock(mMutex);
   6273  MOZ_ASSERT(mStatus == Dead);
   6274 
   6275  MOZ_ASSERT(mThread);
   6276 
   6277  mThread->ClearEventQueueAndWorker(WorkerThreadFriendKey{});
   6278  mThread.swap(doomedThread);
   6279 }
   6280 
   6281 void WorkerPrivate::BeginCTypesCall() {
   6282  AssertIsOnWorkerThread();
   6283  auto data = mWorkerThreadAccessible.Access();
   6284 
   6285  // Don't try to GC while we're blocked in a ctypes call.
   6286  SetGCTimerMode(NoTimer);
   6287 
   6288  data->mYieldJSThreadExecution.EmplaceBack();
   6289 }
   6290 
   6291 void WorkerPrivate::EndCTypesCall() {
   6292  AssertIsOnWorkerThread();
   6293  auto data = mWorkerThreadAccessible.Access();
   6294 
   6295  data->mYieldJSThreadExecution.RemoveLastElement();
   6296 
   6297  // Make sure the periodic timer is running before we start running JS again.
   6298  SetGCTimerMode(PeriodicTimer);
   6299 }
   6300 
   6301 void WorkerPrivate::BeginCTypesCallback() {
   6302  AssertIsOnWorkerThread();
   6303 
   6304  // Make sure the periodic timer is running before we start running JS again.
   6305  SetGCTimerMode(PeriodicTimer);
   6306 
   6307  // Re-requesting execution is not needed since the JSRuntime code calling
   6308  // this will do an AutoEntryScript.
   6309 }
   6310 
   6311 void WorkerPrivate::EndCTypesCallback() {
   6312  AssertIsOnWorkerThread();
   6313 
   6314  // Don't try to GC while we're blocked in a ctypes call.
   6315  SetGCTimerMode(NoTimer);
   6316 }
   6317 
   6318 bool WorkerPrivate::ConnectMessagePort(JSContext* aCx,
   6319                                       UniqueMessagePortId& aIdentifier) {
   6320  AssertIsOnWorkerThread();
   6321 
   6322  WorkerGlobalScope* globalScope = GlobalScope();
   6323 
   6324  JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
   6325  MOZ_ASSERT(jsGlobal);
   6326 
   6327  // This UniqueMessagePortId is used to create a new port, still connected
   6328  // with the other one, but in the worker thread.
   6329  ErrorResult rv;
   6330  RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
   6331  if (NS_WARN_IF(rv.Failed())) {
   6332    rv.SuppressException();
   6333    return false;
   6334  }
   6335 
   6336  GlobalObject globalObject(aCx, jsGlobal);
   6337  if (globalObject.Failed()) {
   6338    return false;
   6339  }
   6340 
   6341  RootedDictionary<MessageEventInit> init(aCx);
   6342  init.mData = JS_GetEmptyStringValue(aCx);
   6343  init.mBubbles = false;
   6344  init.mCancelable = false;
   6345  init.mSource.SetValue().SetAsMessagePort() = port;
   6346  if (!init.mPorts.AppendElement(port.forget(), fallible)) {
   6347    return false;
   6348  }
   6349 
   6350  RefPtr<MessageEvent> event =
   6351      MessageEvent::Constructor(globalObject, u"connect"_ns, init);
   6352 
   6353  event->SetTrusted(true);
   6354 
   6355  globalScope->DispatchEvent(*event);
   6356 
   6357  return true;
   6358 }
   6359 
   6360 WorkerGlobalScope* WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx) {
   6361  auto data = mWorkerThreadAccessible.Access();
   6362 
   6363  if (data->mScope) {
   6364    return data->mScope;
   6365  }
   6366 
   6367  if (IsSharedWorker()) {
   6368    data->mScope =
   6369        new SharedWorkerGlobalScope(this, CreateClientSource(), WorkerName());
   6370  } else if (IsServiceWorker()) {
   6371    data->mScope = new ServiceWorkerGlobalScope(
   6372        this, CreateClientSource(), GetServiceWorkerRegistrationDescriptor());
   6373  } else {
   6374    data->mScope = new DedicatedWorkerGlobalScope(this, CreateClientSource(),
   6375                                                  WorkerName());
   6376  }
   6377 
   6378  JS::Rooted<JSObject*> global(aCx);
   6379  NS_ENSURE_TRUE(data->mScope->WrapGlobalObject(aCx, &global), nullptr);
   6380 
   6381  JSAutoRealm ar(aCx, global);
   6382 
   6383  if (!RegisterBindings(aCx, global)) {
   6384    data->mScope = nullptr;
   6385    return nullptr;
   6386  }
   6387 
   6388  // Worker has already in "Canceling", let the WorkerGlobalScope start dying.
   6389  if (data->mCancelBeforeWorkerScopeConstructed) {
   6390    data->mScope->NoteTerminating();
   6391    data->mScope->DisconnectGlobalTeardownObservers();
   6392  }
   6393 
   6394  JS_FireOnNewGlobalObject(aCx, global);
   6395 
   6396  return data->mScope;
   6397 }
   6398 
   6399 WorkerDebuggerGlobalScope* WorkerPrivate::CreateDebuggerGlobalScope(
   6400    JSContext* aCx) {
   6401  auto data = mWorkerThreadAccessible.Access();
   6402  MOZ_ASSERT(!data->mDebuggerScope);
   6403 
   6404  // The debugger global gets a dummy client, not the "real" client used by the
   6405  // debugee worker.
   6406  auto clientSource = ClientManager::CreateSource(
   6407      GetClientType(), HybridEventTarget(), NullPrincipalInfo());
   6408 
   6409  data->mDebuggerScope =
   6410      new WorkerDebuggerGlobalScope(this, std::move(clientSource));
   6411 
   6412  JS::Rooted<JSObject*> global(aCx);
   6413  NS_ENSURE_TRUE(data->mDebuggerScope->WrapGlobalObject(aCx, &global), nullptr);
   6414 
   6415  JSAutoRealm ar(aCx, global);
   6416 
   6417  if (!RegisterDebuggerBindings(aCx, global)) {
   6418    data->mDebuggerScope = nullptr;
   6419    return nullptr;
   6420  }
   6421 
   6422  JS_FireOnNewGlobalObject(aCx, global);
   6423 
   6424  return data->mDebuggerScope;
   6425 }
   6426 
   6427 bool WorkerPrivate::IsOnWorkerThread() const {
   6428  // We can't use mThread because it must be protected by mMutex and sometimes
   6429  // this method is called when mMutex is already locked. This method should
   6430  // always work.
   6431  MOZ_ASSERT(mPRThread,
   6432             "AssertIsOnWorkerThread() called before a thread was assigned!");
   6433 
   6434  return mPRThread == PR_GetCurrentThread();
   6435 }
   6436 
   6437 #ifdef DEBUG
   6438 void WorkerPrivate::AssertIsOnWorkerThread() const {
   6439  MOZ_ASSERT(IsOnWorkerThread());
   6440 }
   6441 #endif  // DEBUG
   6442 
   6443 void WorkerPrivate::DumpCrashInformation(nsACString& aString) {
   6444  auto data = mWorkerThreadAccessible.Access();
   6445 
   6446  aString.Append("IsChromeWorker(");
   6447  if (IsChromeWorker()) {
   6448    aString.Append(NS_ConvertUTF16toUTF8(ScriptURL()));
   6449  } else {
   6450    aString.Append("false");
   6451  }
   6452  aString.Append(")");
   6453  for (const auto* workerRef : data->mWorkerRefs.NonObservingRange()) {
   6454    if (workerRef->IsPreventingShutdown()) {
   6455      aString.Append("|");
   6456      aString.Append(workerRef->Name());
   6457      const nsCString status = GET_WORKERREF_DEBUG_STATUS(workerRef);
   6458      if (!status.IsEmpty()) {
   6459        aString.Append("[");
   6460        aString.Append(status);
   6461        aString.Append("]");
   6462      }
   6463    }
   6464  }
   6465 }
   6466 
   6467 PerformanceStorage* WorkerPrivate::GetPerformanceStorage() {
   6468  MOZ_ASSERT(mPerformanceStorage);
   6469  return mPerformanceStorage;
   6470 }
   6471 
   6472 bool WorkerPrivate::ShouldResistFingerprinting(RFPTarget aTarget) const {
   6473  return mLoadInfo.mShouldResistFingerprinting &&
   6474         nsRFPService::IsRFPEnabledFor(
   6475             mLoadInfo.mOriginAttributes.IsPrivateBrowsing(), aTarget,
   6476             mLoadInfo.mOverriddenFingerprintingSettings);
   6477 }
   6478 
   6479 void WorkerPrivate::SetRemoteWorkerController(RemoteWorkerChild* aController) {
   6480  AssertIsOnMainThread();
   6481  MOZ_ASSERT(aController);
   6482  MOZ_ASSERT(!mRemoteWorkerController);
   6483 
   6484  mRemoteWorkerController = aController;
   6485 }
   6486 
   6487 RemoteWorkerChild* WorkerPrivate::GetRemoteWorkerController() {
   6488  AssertIsOnMainThread();
   6489  MOZ_ASSERT(mRemoteWorkerController);
   6490  return mRemoteWorkerController;
   6491 }
   6492 
   6493 RefPtr<GenericPromise> WorkerPrivate::SetServiceWorkerSkipWaitingFlag() {
   6494  AssertIsOnWorkerThread();
   6495  MOZ_ASSERT(IsServiceWorker());
   6496 
   6497  RefPtr<RemoteWorkerChild> rwc = mRemoteWorkerController;
   6498 
   6499  if (!rwc) {
   6500    return GenericPromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR, __func__);
   6501  }
   6502 
   6503  RefPtr<GenericPromise> promise =
   6504      rwc->MaybeSendSetServiceWorkerSkipWaitingFlag();
   6505 
   6506  return promise;
   6507 }
   6508 
   6509 const nsString& WorkerPrivate::Id() {
   6510  if (mId.IsEmpty()) {
   6511    mId = ComputeWorkerPrivateId();
   6512  }
   6513 
   6514  MOZ_ASSERT(!mId.IsEmpty());
   6515 
   6516  return mId;
   6517 }
   6518 
   6519 bool WorkerPrivate::IsSharedMemoryAllowed() const {
   6520  if (StaticPrefs::
   6521          dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
   6522    return true;
   6523  }
   6524 
   6525  // Allow privileged addons to access shared memory.
   6526  if (mIsPrivilegedAddonGlobal) {
   6527    return true;
   6528  }
   6529 
   6530  return CrossOriginIsolated();
   6531 }
   6532 
   6533 bool WorkerPrivate::CrossOriginIsolated() const {
   6534  if (!StaticPrefs::
   6535          dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup()) {
   6536    return false;
   6537  }
   6538 
   6539  return mAgentClusterOpenerPolicy ==
   6540         nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
   6541 }
   6542 
   6543 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetEmbedderPolicy()
   6544    const {
   6545  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   6546    return nsILoadInfo::EMBEDDER_POLICY_NULL;
   6547  }
   6548 
   6549  return mEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
   6550 }
   6551 
   6552 Result<Ok, nsresult> WorkerPrivate::SetEmbedderPolicy(
   6553    nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
   6554  MOZ_ASSERT(NS_IsMainThread());
   6555  MOZ_ASSERT(mEmbedderPolicy.isNothing());
   6556 
   6557  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   6558    return Ok();
   6559  }
   6560 
   6561  // https://html.spec.whatwg.org/multipage/browsers.html#check-a-global-object's-embedder-policy
   6562  // If ownerPolicy's value is not compatible with cross-origin isolation or
   6563  // policy's value is compatible with cross-origin isolation, then return true.
   6564  EnsureOwnerEmbedderPolicy();
   6565  nsILoadInfo::CrossOriginEmbedderPolicy ownerPolicy =
   6566      mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
   6567  if (nsContentSecurityManager::IsCompatibleWithCrossOriginIsolation(
   6568          ownerPolicy) &&
   6569      !nsContentSecurityManager::IsCompatibleWithCrossOriginIsolation(
   6570          aPolicy)) {
   6571    return Err(NS_ERROR_BLOCKED_BY_POLICY);
   6572  }
   6573 
   6574  mEmbedderPolicy.emplace(aPolicy);
   6575 
   6576  return Ok();
   6577 }
   6578 
   6579 void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) {
   6580  MOZ_ASSERT(NS_IsMainThread());
   6581  MOZ_ASSERT(aRequest);
   6582 
   6583  EnsureOwnerEmbedderPolicy();
   6584 
   6585  if (mOwnerEmbedderPolicy.isSome()) {
   6586    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   6587    MOZ_ASSERT(channel);
   6588 
   6589    nsCOMPtr<nsIURI> scriptURI;
   6590    MOZ_ALWAYS_SUCCEEDS(channel->GetURI(getter_AddRefs(scriptURI)));
   6591 
   6592    bool isLocalScriptURI = false;
   6593    MOZ_ALWAYS_SUCCEEDS(NS_URIChainHasFlags(
   6594        scriptURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
   6595        &isLocalScriptURI));
   6596 
   6597    MOZ_RELEASE_ASSERT(isLocalScriptURI);
   6598  }
   6599 
   6600  mEmbedderPolicy.emplace(
   6601      mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL));
   6602 }
   6603 
   6604 bool WorkerPrivate::MatchEmbedderPolicy(
   6605    nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const {
   6606  MOZ_ASSERT(NS_IsMainThread());
   6607 
   6608  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   6609    return true;
   6610  }
   6611 
   6612  return mEmbedderPolicy.value() == aPolicy;
   6613 }
   6614 
   6615 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetOwnerEmbedderPolicy()
   6616    const {
   6617  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
   6618    return nsILoadInfo::EMBEDDER_POLICY_NULL;
   6619  }
   6620 
   6621  return mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
   6622 }
   6623 
   6624 void WorkerPrivate::EnsureOwnerEmbedderPolicy() {
   6625  MOZ_ASSERT(NS_IsMainThread());
   6626  MOZ_ASSERT(mOwnerEmbedderPolicy.isNothing());
   6627 
   6628  if (GetParent()) {
   6629    mOwnerEmbedderPolicy.emplace(GetParent()->GetEmbedderPolicy());
   6630  } else if (GetWindow() && GetWindow()->GetWindowContext()) {
   6631    mOwnerEmbedderPolicy.emplace(
   6632        GetWindow()->GetWindowContext()->GetEmbedderPolicy());
   6633  }
   6634 }
   6635 
   6636 nsIPrincipal* WorkerPrivate::GetEffectiveStoragePrincipal() const {
   6637  AssertIsOnWorkerThread();
   6638 
   6639  if (mLoadInfo.mUseRegularPrincipal) {
   6640    return mLoadInfo.mPrincipal;
   6641  }
   6642 
   6643  return mLoadInfo.mPartitionedPrincipal;
   6644 }
   6645 
   6646 const mozilla::ipc::PrincipalInfo&
   6647 WorkerPrivate::GetEffectiveStoragePrincipalInfo() const {
   6648  AssertIsOnWorkerThread();
   6649 
   6650  if (mLoadInfo.mUseRegularPrincipal) {
   6651    return *mLoadInfo.mPrincipalInfo;
   6652  }
   6653 
   6654  return *mLoadInfo.mPartitionedPrincipalInfo;
   6655 }
   6656 
   6657 NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
   6658 NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
   6659 
   6660 NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
   6661  NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
   6662  NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
   6663  NS_INTERFACE_MAP_ENTRY(nsISupports)
   6664 #ifdef DEBUG
   6665  // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
   6666  // result.
   6667  if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
   6668    *aInstancePtr = this;
   6669    return NS_OK;
   6670  } else
   6671 #endif
   6672 NS_INTERFACE_MAP_END
   6673 
   6674 NS_IMETHODIMP
   6675 WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
   6676                                               DispatchFlags aFlags) {
   6677  return Dispatch(do_AddRef(aRunnable), aFlags);
   6678 }
   6679 
   6680 NS_IMETHODIMP
   6681 WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
   6682                                     DispatchFlags aFlags) {
   6683  // May be called on any thread!
   6684 
   6685  // NOTE: This nsIEventTarget implementation never leaks aRunnable, even if
   6686  // NS_DISPATCH_FALLIBLE is not set.
   6687  nsCOMPtr<nsIRunnable> event(aRunnable);
   6688 
   6689  RefPtr<WorkerRunnable> workerRunnable;
   6690 
   6691  MutexAutoLock lock(mMutex);
   6692 
   6693  if (mDisabled) {
   6694    NS_WARNING(
   6695        "A runnable was posted to a worker that is already shutting "
   6696        "down!");
   6697    return NS_ERROR_UNEXPECTED;
   6698  }
   6699 
   6700  MOZ_ASSERT(mWorkerPrivate);
   6701  MOZ_ASSERT(mNestedEventTarget);
   6702 
   6703  if (event) {
   6704    workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
   6705  }
   6706 
   6707  nsresult rv =
   6708      mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget);
   6709  if (NS_WARN_IF(NS_FAILED(rv))) {
   6710    return rv;
   6711  }
   6712 
   6713  return NS_OK;
   6714 }
   6715 
   6716 NS_IMETHODIMP
   6717 WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
   6718                                            uint32_t)
   6719 
   6720 {
   6721  return NS_ERROR_NOT_IMPLEMENTED;
   6722 }
   6723 
   6724 NS_IMETHODIMP
   6725 WorkerPrivate::EventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) {
   6726  return NS_ERROR_NOT_IMPLEMENTED;
   6727 }
   6728 
   6729 NS_IMETHODIMP
   6730 WorkerPrivate::EventTarget::UnregisterShutdownTask(
   6731    nsITargetShutdownTask* aTask) {
   6732  return NS_ERROR_NOT_IMPLEMENTED;
   6733 }
   6734 
   6735 NS_IMETHODIMP
   6736 WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
   6737  // May be called on any thread!
   6738 
   6739  MOZ_ASSERT(aIsOnCurrentThread);
   6740 
   6741  MutexAutoLock lock(mMutex);
   6742 
   6743  if (mShutdown) {
   6744    NS_WARNING(
   6745        "A worker's event target was used after the worker has shutdown!");
   6746    return NS_ERROR_UNEXPECTED;
   6747  }
   6748 
   6749  MOZ_ASSERT(mNestedEventTarget);
   6750 
   6751  *aIsOnCurrentThread = mNestedEventTarget->IsOnCurrentThread();
   6752  return NS_OK;
   6753 }
   6754 
   6755 NS_IMETHODIMP_(bool)
   6756 WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible() {
   6757  // May be called on any thread!
   6758 
   6759  MutexAutoLock lock(mMutex);
   6760 
   6761  if (mShutdown) {
   6762    NS_WARNING(
   6763        "A worker's event target was used after the worker has shutdown!");
   6764    return false;
   6765  }
   6766 
   6767  MOZ_ASSERT(mNestedEventTarget);
   6768 
   6769  return mNestedEventTarget->IsOnCurrentThread();
   6770 }
   6771 
   6772 WorkerPrivate::AutoPushEventLoopGlobal::AutoPushEventLoopGlobal(
   6773    WorkerPrivate* aWorkerPrivate, JSContext* aCx) {
   6774  auto data = aWorkerPrivate->mWorkerThreadAccessible.Access();
   6775  mOldEventLoopGlobal = std::move(data->mCurrentEventLoopGlobal);
   6776  if (JSObject* global = JS::CurrentGlobalOrNull(aCx)) {
   6777    data->mCurrentEventLoopGlobal = xpc::NativeGlobal(global);
   6778  }
   6779 #ifdef DEBUG
   6780  mNewEventLoopGlobal = data->mCurrentEventLoopGlobal;
   6781 #endif
   6782 }
   6783 
   6784 WorkerPrivate::AutoPushEventLoopGlobal::~AutoPushEventLoopGlobal() {
   6785  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   6786  // We are popping out the event loop global, WorkerPrivate is supposed to be
   6787  // alive and in a valid status(Running or Canceling)
   6788  MOZ_ASSERT(workerPrivate);
   6789  auto data = workerPrivate->mWorkerThreadAccessible.Access();
   6790 #ifdef DEBUG
   6791  // Saved event loop global should be matched.
   6792  MOZ_ASSERT(data->mCurrentEventLoopGlobal == mNewEventLoopGlobal);
   6793  mNewEventLoopGlobal = nullptr;
   6794 #endif
   6795  data->mCurrentEventLoopGlobal = std::move(mOldEventLoopGlobal);
   6796 }
   6797 
   6798 // FontVisibilityProvider implementation
   6799 FontVisibility WorkerPrivate::GetFontVisibility() const {
   6800  return mFontVisibility;
   6801 }
   6802 
   6803 void WorkerPrivate::ReportBlockedFontFamily(const nsCString& aMsg) const {
   6804  MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, ("%s", aMsg.get()));
   6805  nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16(aMsg),
   6806                                              nsIScriptError::warningFlag,
   6807                                              "Security"_ns, GetDocument());
   6808 }
   6809 
   6810 bool WorkerPrivate::IsChrome() const { return IsChromeWorker(); }
   6811 
   6812 bool WorkerPrivate::IsPrivateBrowsing() const {
   6813  return mLoadInfo.mOriginAttributes.IsPrivateBrowsing();
   6814 }
   6815 
   6816 nsICookieJarSettings* WorkerPrivate::GetCookieJarSettings() const {
   6817  return CookieJarSettings();
   6818 }
   6819 
   6820 Maybe<FontVisibility> WorkerPrivate::MaybeInheritFontVisibility() const {
   6821  if (mParent) {
   6822    // If we have a parent, we inherit the parent's font visibility.
   6823    return Some(mParent->GetFontVisibility());
   6824  }
   6825 
   6826  dom::Document* doc = GetDocument();
   6827  if (!doc) {
   6828    return Nothing();
   6829  }
   6830 
   6831  nsPresContext* presContext = doc->GetPresContext();
   6832  NS_ENSURE_TRUE(presContext, Nothing());
   6833 
   6834  return Some(presContext->GetFontVisibility());
   6835 }
   6836 
   6837 void WorkerPrivate::UserFontSetUpdated(gfxUserFontEntry*) {}
   6838 
   6839 // -----------------------------------------------------------------------------
   6840 // AutoSyncLoopHolder
   6841 
   6842 AutoSyncLoopHolder::AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate,
   6843                                       WorkerStatus aFailStatus,
   6844                                       const char* const aName)
   6845    : mTarget(aWorkerPrivate->CreateNewSyncLoop(aFailStatus)),
   6846      mIndex(aWorkerPrivate->mSyncLoopStack.Length() - 1) {
   6847  aWorkerPrivate->AssertIsOnWorkerThread();
   6848  LOGV(
   6849      ("AutoSyncLoopHolder::AutoSyncLoopHolder [%p] creator: %s", this, aName));
   6850  if (aFailStatus < Canceling) {
   6851    mWorkerRef = StrongWorkerRef::Create(aWorkerPrivate, aName, [aName]() {
   6852      // Do nothing with the shutdown callback here since we need to wait for
   6853      // the underlying SyncLoop to complete by itself.
   6854      LOGV(
   6855          ("AutoSyncLoopHolder::AutoSyncLoopHolder Worker starts to shutdown "
   6856           "with a AutoSyncLoopHolder(%s).",
   6857           aName));
   6858    });
   6859  } else {
   6860    LOGV(
   6861        ("AutoSyncLoopHolder::AutoSyncLoopHolder [%p] Create "
   6862         "AutoSyncLoopHolder(%s) while Worker is shutting down",
   6863         this, aName));
   6864    mWorkerRef = StrongWorkerRef::CreateForcibly(aWorkerPrivate, aName);
   6865  }
   6866  // mWorkerRef can be nullptr here.
   6867 }
   6868 
   6869 AutoSyncLoopHolder::~AutoSyncLoopHolder() {
   6870  if (mWorkerRef && mTarget) {
   6871    mWorkerRef->Private()->AssertIsOnWorkerThread();
   6872    mWorkerRef->Private()->StopSyncLoop(mTarget, NS_ERROR_FAILURE);
   6873    mWorkerRef->Private()->DestroySyncLoop(mIndex);
   6874  }
   6875 }
   6876 
   6877 nsresult AutoSyncLoopHolder::Run() {
   6878  if (mWorkerRef) {
   6879    WorkerPrivate* workerPrivate = mWorkerRef->Private();
   6880    MOZ_ASSERT(workerPrivate);
   6881 
   6882    workerPrivate->AssertIsOnWorkerThread();
   6883 
   6884    nsresult rv = workerPrivate->RunCurrentSyncLoop();
   6885 
   6886    // The sync loop is done, sync loop has already destroyed in the end of
   6887    // WorkerPrivate::RunCurrentSyncLoop(). So, release mWorkerRef here to
   6888    // avoid destroying sync loop again in the ~AutoSyncLoopHolder();
   6889    mWorkerRef = nullptr;
   6890 
   6891    return rv;
   6892  }
   6893  return NS_OK;
   6894 }
   6895 
   6896 nsISerialEventTarget* AutoSyncLoopHolder::GetSerialEventTarget() const {
   6897  // This can be null if CreateNewSyncLoop() fails.
   6898  return mTarget;
   6899 }
   6900 
   6901 // -----------------------------------------------------------------------------
   6902 // WorkerParentRef
   6903 WorkerParentRef::WorkerParentRef(RefPtr<WorkerPrivate>& aWorkerPrivate)
   6904    : mWorkerPrivate(aWorkerPrivate) {
   6905  LOGV(("WorkerParentRef::WorkerParentRef [%p] aWorkerPrivate %p", this,
   6906        aWorkerPrivate.get()));
   6907  MOZ_ASSERT(mWorkerPrivate);
   6908  mWorkerPrivate->AssertIsOnParentThread();
   6909 }
   6910 
   6911 const RefPtr<WorkerPrivate>& WorkerParentRef::Private() const {
   6912  if (mWorkerPrivate) {
   6913    mWorkerPrivate->AssertIsOnParentThread();
   6914  }
   6915  return mWorkerPrivate;
   6916 }
   6917 
   6918 void WorkerParentRef::DropWorkerPrivate() {
   6919  LOGV(("WorkerParentRef::DropWorkerPrivate [%p]", this));
   6920  if (mWorkerPrivate) {
   6921    mWorkerPrivate->AssertIsOnParentThread();
   6922    mWorkerPrivate = nullptr;
   6923  }
   6924 }
   6925 
   6926 WorkerParentRef::~WorkerParentRef() = default;
   6927 
   6928 }  // namespace dom
   6929 }  // namespace mozilla