Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/workers/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 215 kB image not shown  

Quelle  WorkerPrivate.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "WorkerPrivate.h"

#include <utility>

#include "js/CallAndConstruct.h"  // JS_CallFunctionValue
#include "js/CompilationAndEvaluation.h"
#include "js/ContextOptions.h"
#include "js/Exception.h"
#include "js/friend/ErrorMessages.h"  // JSMSG_OUT_OF_MEMORY
#include "js/LocaleSensitive.h"
#include "js/MemoryMetrics.h"
#include "js/SourceText.h"
#include "MessageEventRunnable.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/Mutex.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/Result.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CallbackDebuggerNotification.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/IndexedDatabaseManager.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/nsCSPUtils.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceStorageWorker.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/ReferrerInfo.h"
#include "mozilla/dom/RemoteWorkerChild.h"
#include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/SimpleGlobalObject.h"
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/UseCounterMetrics.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/WorkerStatus.h"
#include "mozilla/dom/WebTaskScheduler.h"
#include "mozilla/dom/JSExecutionManager.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/extensions/ExtensionBrowser.h"  // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/glean/DomUseCounterMetrics.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ThreadEventQueue.h"
#include "mozilla/ThreadSafety.h"
#include "mozilla/ThrottledEventQueue.h"
#include "nsCycleCollector.h"
#include "nsGlobalWindowInner.h"
#include "nsIDUtils.h"
#include "nsNetUtil.h"
#include "nsIFile.h"
#include "nsIMemoryReporter.h"
#include "nsIPermissionManager.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIUUIDGenerator.h"
#include "nsPrintfCString.h"
#include "nsProxyRelease.h"
#include "nsQueryObject.h"
#include "nsRFPService.h"
#include "nsSandboxFlags.h"
#include "nsThreadUtils.h"
#include "nsUTF8Utils.h"

#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "mozilla/dom/ServiceWorkerEvents.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/net/CookieJarSettings.h"
#include "WorkerCSPEventListener.h"
#include "WorkerDebugger.h"
#include "WorkerDebuggerManager.h"
#include "WorkerError.h"
#include "WorkerEventTarget.h"
#include "WorkerNavigator.h"
#include "WorkerRef.h"
#include "WorkerRunnable.h"
#include "WorkerThread.h"
#include "nsContentSecurityManager.h"

#include "nsThreadManager.h"

#ifdef XP_WIN
#  undef PostMessage
#endif

// JS_MaybeGC will run once every second during normal execution.
#define PERIODIC_GC_TIMER_DELAY_SEC 1

// A shrinking GC will run five seconds after the last event is processed.
#define IDLE_GC_TIMER_DELAY_SEC 5

// Arbitrary short grace period for the currently running task to finish.
// There isn't an advantage for us to immediately interrupt JS in the middle of
// execution that might yield soon, especially when there is so much async
// variability in the data flow prior to us deciding to trigger the interrupt.
#define DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS 250

static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");

mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; }

mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; }

#ifdef LOG
#  undef LOG
#endif
#ifdef LOGV
#  undef LOGV
#endif
#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
#define LOGV(args) MOZ_LOG(sWorkerPrivateLog, LogLevel::Verbose, args);

namespace mozilla {

using namespace ipc;

namespace dom {

using namespace workerinternals;

MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)

namespace {

#ifdef DEBUG

const nsIID kDEBUGWorkerEventTargetIID = {
    0xccaba3fa,
    0x5be2,
    0x4de2,
    {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};

#endif

template <class T>
class UniquePtrComparator {
  using A = UniquePtr<T>;
  using B = T*;

 public:
  bool Equals(const A& a, const A& b) const {
    return (a && b) ? (*a == *b) : (!a && !b);
  }
  bool LessThan(const A& a, const A& b) const {
    return (a && b) ? (*a < *b) : !!b;
  }
};

template <class T>
inline UniquePtrComparator<T> GetUniquePtrComparator(
    const nsTArray<UniquePtr<T>>&) {
  return UniquePtrComparator<T>();
}

// This class is used to wrap any runnables that the worker receives via the
// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
// from the worker's EventTarget).
class ExternalRunnableWrapper final : public WorkerThreadRunnable {
  nsCOMPtr<nsIRunnable> mWrappedRunnable;

 public:
  ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
                          nsIRunnable* aWrappedRunnable)
      : WorkerThreadRunnable("ExternalRunnableWrapper"),
        mWrappedRunnable(aWrappedRunnable) {
    MOZ_ASSERT(aWorkerPrivate);
    MOZ_ASSERT(aWrappedRunnable);
  }

  NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper,
                                       WorkerThreadRunnable)

 private:
  ~ExternalRunnableWrapper() = default;

  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    // Silence bad assertions.
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    // Silence bad assertions.
  }

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    nsresult rv = mWrappedRunnable->Run();
    mWrappedRunnable = nullptr;
    if (NS_FAILED(rv)) {
      if (!JS_IsExceptionPending(aCx)) {
        Throw(aCx, rv);
      }
      return false;
    }
    return true;
  }

  nsresult Cancel() override {
    nsCOMPtr<nsIDiscardableRunnable> doomed =
        do_QueryInterface(mWrappedRunnable);
    if (doomed) {
      doomed->OnDiscard();
    }
    mWrappedRunnable = nullptr;
    return NS_OK;
  }

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
  NS_IMETHOD GetName(nsACString& aName) override {
    aName.AssignLiteral("ExternalRunnableWrapper(");
    if (nsCOMPtr<nsINamed> named = do_QueryInterface(mWrappedRunnable)) {
      nsAutoCString containedName;
      named->GetName(containedName);
      aName.Append(containedName);
    } else {
      aName.AppendLiteral("?");
    }
    aName.AppendLiteral(")");
    return NS_OK;
  }
#endif
};

struct WindowAction {
  nsPIDOMWindowInner* mWindow;
  bool mDefaultAction;

  MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
      : mWindow(aWindow), mDefaultAction(true) {}

  bool operator==(const WindowAction& aOther) const {
    return mWindow == aOther.mWindow;
  }
};

class WorkerFinishedRunnable final : public WorkerControlRunnable {
  WorkerPrivate* mFinishedWorker;

 public:
  WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
                         WorkerPrivate* aFinishedWorker)
      : WorkerControlRunnable("WorkerFinishedRunnable"),
        mFinishedWorker(aFinishedWorker) {
    aFinishedWorker->IncreaseWorkerFinishedRunnableCount();
  }

 private:
  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    // Silence bad assertions.
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    // Silence bad assertions.
  }

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    // This may block on the main thread.
    AutoYieldJSThreadExecution yield;

    mFinishedWorker->DecreaseWorkerFinishedRunnableCount();

    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
      NS_WARNING("Failed to dispatch, going to leak!");
    }

    RuntimeService* runtime = RuntimeService::GetService();
    NS_ASSERTION(runtime, "This should never be null!");

    mFinishedWorker->DisableDebugger();

    runtime->UnregisterWorker(*mFinishedWorker);

    mFinishedWorker->ClearSelfAndParentEventTargetRef();
    return true;
  }
};

class TopLevelWorkerFinishedRunnable final : public Runnable {
  WorkerPrivate* mFinishedWorker;

 public:
  explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
      : mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
        mFinishedWorker(aFinishedWorker) {
    aFinishedWorker->AssertIsOnWorkerThread();
    aFinishedWorker->IncreaseTopLevelWorkerFinishedRunnableCount();
  }

  NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)

 private:
  ~TopLevelWorkerFinishedRunnable() = default;

  NS_IMETHOD
  Run() override {
    AssertIsOnMainThread();

    mFinishedWorker->DecreaseTopLevelWorkerFinishedRunnableCount();

    RuntimeService* runtime = RuntimeService::GetService();
    MOZ_ASSERT(runtime);

    mFinishedWorker->DisableDebugger();

    runtime->UnregisterWorker(*mFinishedWorker);

    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
      NS_WARNING("Failed to dispatch, going to leak!");
    }

    mFinishedWorker->ClearSelfAndParentEventTargetRef();
    return NS_OK;
  }
};

class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
  nsString mScriptURL;
  const mozilla::Encoding* mDocumentEncoding;
  UniquePtr<SerializedStackHolder> mOriginStack;

 public:
  explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
                                 UniquePtr<SerializedStackHolder> aOriginStack,
                                 const nsAString& aScriptURL,
                                 const mozilla::Encoding* aDocumentEncoding)
      : WorkerDebuggeeRunnable("CompileScriptRunnable"),
        mScriptURL(aScriptURL),
        mDocumentEncoding(aDocumentEncoding),
        mOriginStack(aOriginStack.release()) {}

 private:
  // We can't implement PreRun effectively, because at the point when that would
  // run we have not yet done our load so don't know things like our final
  // principal and whatnot.

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->AssertIsOnWorkerThread();

    WorkerGlobalScope* globalScope =
        aWorkerPrivate->GetOrCreateGlobalScope(aCx);
    if (NS_WARN_IF(!globalScope)) {
      return false;
    }

    if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
      return false;
    }

    ErrorResult rv;
    workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
                                    mScriptURL, WorkerScript, rv,
                                    mDocumentEncoding);

    if (aWorkerPrivate->ExtensionAPIAllowed()) {
      MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
      RefPtr<Runnable> extWorkerRunnable =
          extensions::CreateWorkerLoadedRunnable(
              aWorkerPrivate->ServiceWorkerID(), aWorkerPrivate->GetBaseURI());
      // Dispatch as a low priority runnable.
      if (NS_FAILED(aWorkerPrivate->DispatchToMainThreadForMessaging(
              extWorkerRunnable.forget()))) {
        NS_WARNING(
            "Failed to dispatch runnable to notify extensions worker loaded");
      }
    }

    rv.WouldReportJSException();
    // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
    // return false and don't SetWorkerScriptExecutedSuccessfully() in that
    // case, but don't throw anything on aCx.  The idea is to not dispatch error
    // events if our load is canceled with that error code.
    if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
      rv.SuppressException();
      return false;
    }

    // Make sure to propagate exceptions from rv onto aCx, so that they will get
    // reported after we return.  We want to propagate just JS exceptions,
    // because all the other errors are handled when the script is loaded.
    // See: https://dom.spec.whatwg.org/#concept-event-fire
    if (rv.Failed() && !rv.IsJSException()) {
      WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
          aWorkerPrivate);
      rv.SuppressException();
      return false;
    }

    // This is a little dumb, but aCx is in the null realm here because we
    // set it up that way in our Run(), since we had not created the global at
    // that point yet.  So we need to enter the realm of our global,
    // because setting a pending exception on aCx involves wrapping into its
    // current compartment.  Luckily we have a global now.
    JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
    if (rv.MaybeSetPendingException(aCx)) {
      // In the event of an uncaught exception, the worker should still keep
      // running (return true) but should not be marked as having executed
      // successfully (which will cause ServiceWorker installation to fail).
      // In previous error handling cases in this method, we return false (to
      // trigger CloseInternal) because the global is not in an operable
      // state at all.
      //
      // For ServiceWorkers, this would correspond to the "Run Service Worker"
      // algorithm returning an "abrupt completion" and _not_ failure.
      //
      // For DedicatedWorkers and SharedWorkers, this would correspond to the
      // "run a worker" algorithm disregarding the return value of "run the
      // classic script"/"run the module script" in step 24:
      //
      // "If script is a classic script, then run the classic script script.
      // Otherwise, it is a module script; run the module script script."
      return true;
    }

    aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
    return true;
  }

  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
               bool aRunResult) override {
    if (!aRunResult) {
      aWorkerPrivate->CloseInternal();
    }
    WorkerThreadRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
  }
};

class NotifyRunnable final : public WorkerControlRunnable {
  WorkerStatus mStatus;

 public:
  NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
      : WorkerControlRunnable("NotifyRunnable"), mStatus(aStatus) {
    MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
               aStatus == Killing);
  }

 private:
  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->AssertIsOnParentThread();
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    aWorkerPrivate->AssertIsOnParentThread();
  }

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    return aWorkerPrivate->NotifyInternal(mStatus);
  }

  virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                       bool aRunResult) override {}
};

class FreezeRunnable final : public WorkerControlRunnable {
 public:
  explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerControlRunnable("FreezeRunnable") {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    return aWorkerPrivate->FreezeInternal();
  }
};

class ThawRunnable final : public WorkerControlRunnable {
 public:
  explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerControlRunnable("ThawRunnable") {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    return aWorkerPrivate->ThawInternal();
  }
};

class ChangeBackgroundStateRunnable final : public WorkerControlRunnable {
 public:
  ChangeBackgroundStateRunnable() = delete;
  explicit ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate) =
      delete;
  ChangeBackgroundStateRunnable(WorkerPrivate* aWorkerPrivate,
                                bool aIsBackground)
      : WorkerControlRunnable("ChangeBackgroundStateRunnable"),
        mIsBackground(aIsBackground) {}

 private:
  bool mIsBackground = false;
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    return aWorkerPrivate->ChangeBackgroundStateInternal(mIsBackground);
  }
};

class PropagateStorageAccessPermissionGrantedRunnable final
    : public WorkerControlRunnable {
 public:
  explicit PropagateStorageAccessPermissionGrantedRunnable(
      WorkerPrivate* aWorkerPrivate)
      : WorkerControlRunnable(
            "PropagateStorageAccessPermissionGrantedRunnable") {}

 private:
  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->PropagateStorageAccessPermissionGrantedInternal();
    return true;
  }
};

class ReportErrorToConsoleRunnable final : public WorkerParentThreadRunnable {
 public:
  // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
  static void Report(WorkerPrivate* aWorkerPrivate, uint32_t aErrorFlags,
                     const nsCString& aCategory,
                     nsContentUtils::PropertiesFile aFile,
                     const nsCString& aMessageName,
                     const nsTArray<nsString>& aParams,
                     const mozilla::SourceLocation& aLocation) {
    if (aWorkerPrivate) {
      aWorkerPrivate->AssertIsOnWorkerThread();
    } else {
      AssertIsOnMainThread();
    }

    // Now fire a runnable to do the same on the parent's thread if we can.
    if (aWorkerPrivate) {
      RefPtr<ReportErrorToConsoleRunnable> runnable =
          new ReportErrorToConsoleRunnable(aWorkerPrivate, aErrorFlags,
                                           aCategory, aFile, aMessageName,
                                           aParams, aLocation);
      runnable->Dispatch(aWorkerPrivate);
      return;
    }

    // Log a warning to the console.
    nsContentUtils::ReportToConsole(aErrorFlags, aCategory, nullptr, aFile,
                                    aMessageName.get(), aParams, aLocation);
  }

 private:
  ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
                               uint32_t aErrorFlags, const nsCString& aCategory,
                               nsContentUtils::PropertiesFile aFile,
                               const nsCString& aMessageName,
                               const nsTArray<nsString>& aParams,
                               const mozilla::SourceLocation& aLocation)
      : WorkerParentThreadRunnable("ReportErrorToConsoleRunnable"),
        mErrorFlags(aErrorFlags),
        mCategory(aCategory),
        mFile(aFile),
        mMessageName(aMessageName),
        mParams(aParams.Clone()),
        mLocation(aLocation) {}

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    aWorkerPrivate->AssertIsOnWorkerThread();

    // Dispatch may fail if the worker was canceled, no need to report that as
    // an error, so don't call base class PostDispatch.
  }

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    WorkerPrivate* parent = aWorkerPrivate->GetParent();
    MOZ_ASSERT_IF(!parent, NS_IsMainThread());
    Report(parent, mErrorFlags, mCategory, mFile, mMessageName, mParams,
           mLocation);
    return true;
  }

  const uint32_t mErrorFlags;
  const nsCString mCategory;
  const nsContentUtils::PropertiesFile mFile;
  const nsCString mMessageName;
  const nsTArray<nsString> mParams;
  const mozilla::SourceLocation mLocation;
};

class RunExpiredTimoutsRunnable final : public WorkerThreadRunnable,
                                        public nsITimerCallback {
 public:
  NS_DECL_ISUPPORTS_INHERITED

  explicit RunExpiredTimoutsRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerThreadRunnable("RunExpiredTimoutsRunnable") {}

 private:
  ~RunExpiredTimoutsRunnable() = default;

  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    // Silence bad assertions.
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    // Silence bad assertions.
  }

  // MOZ_CAN_RUN_SCRIPT_BOUNDARY until worker runnables are generally
  // MOZ_CAN_RUN_SCRIPT.
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    return aWorkerPrivate->RunExpiredTimeouts(aCx);
  }

  NS_IMETHOD
  Notify(nsITimer* aTimer) override { return Run(); }
};

NS_IMPL_ISUPPORTS_INHERITED(RunExpiredTimoutsRunnable, WorkerThreadRunnable,
                            nsITimerCallback)

class DebuggerImmediateRunnable final : public WorkerThreadRunnable {
  RefPtr<dom::Function> mHandler;

 public:
  explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
                                     dom::Function& aHandler)
      : WorkerThreadRunnable("DebuggerImmediateRunnable"),
        mHandler(&aHandler) {}

 private:
  virtual bool IsDebuggerRunnable() const override { return true; }

  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    // Silence bad assertions.
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    // Silence bad assertions.
  }

  // Make as MOZ_CAN_RUN_SCRIPT_BOUNDARY for calling mHandler->Call();
  // Since WorkerRunnable::WorkerRun has not to be MOZ_CAN_RUN_SCRIPT, but
  // DebuggerImmediateRunnable is a special case that must to call the function
  // defined in the debugger script.
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    JS::Rooted<JS::Value> rval(aCx);
    IgnoredErrorResult rv;
    MOZ_KnownLive(mHandler)->Call({}, &rval, rv);

    return !rv.Failed();
  }
};

// GetJSContext() is safe on the worker thread
void PeriodicGCTimerCallback(nsITimer* aTimer,
                             void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS {
  auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure);
  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
  workerPrivate->AssertIsOnWorkerThread();
  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
                                        false /* shrinking */,
                                        false /* collect children */);
  LOG(WorkerLog(), ("Worker %p run periodic GC\n", workerPrivate));
}

void IdleGCTimerCallback(nsITimer* aTimer,
                         void* aClosure) MOZ_NO_THREAD_SAFETY_ANALYSIS {
  auto* workerPrivate = static_cast<WorkerPrivate*>(aClosure);
  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
  workerPrivate->AssertIsOnWorkerThread();
  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
                                        true /* shrinking */,
                                        false /* collect children */);
  LOG(WorkerLog(), ("Worker %p run idle GC\n", workerPrivate));

  // After running idle GC we can cancel the current timers.
  workerPrivate->CancelGCTimers();
}

class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
  JS::ContextOptions mContextOptions;

 public:
  UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
                               const JS::ContextOptions& aContextOptions)
      : WorkerControlRunnable("UpdateContextOptionsRunnable"),
        mContextOptions(aContextOptions) {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
    return true;
  }
};

class UpdateLanguagesRunnable final : public WorkerThreadRunnable {
  nsTArray<nsString> mLanguages;

 public:
  UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
                          const nsTArray<nsString>& aLanguages)
      : WorkerThreadRunnable("UpdateLanguagesRunnable"),
        mLanguages(aLanguages.Clone()) {}

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
    return true;
  }
};

class UpdateJSWorkerMemoryParameterRunnable final
    : public WorkerControlRunnable {
  Maybe<uint32_t> mValue;
  JSGCParamKey mKey;

 public:
  UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
                                        JSGCParamKey aKey,
                                        Maybe<uint32_t> aValue)
      : WorkerControlRunnable("UpdateJSWorkerMemoryParameterRunnable"),
        mValue(aValue),
        mKey(aKey) {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
    return true;
  }
};

#ifdef JS_GC_ZEAL
class UpdateGCZealRunnable final : public WorkerControlRunnable {
  uint8_t mGCZeal;
  uint32_t mFrequency;

 public:
  UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
                       uint32_t aFrequency)
      : WorkerControlRunnable("UpdateGCZealRunnable"),
        mGCZeal(aGCZeal),
        mFrequency(aFrequency) {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
    return true;
  }
};
#endif

class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
  bool mState;

 public:
  SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
      : WorkerControlRunnable("SetLowMemoryStateRunnable"), mState(aState) {}

 private:
  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState);
    return true;
  }
};

class GarbageCollectRunnable final : public WorkerControlRunnable {
  bool mShrinking;
  bool mCollectChildren;

 public:
  GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
                         bool aCollectChildren)
      : WorkerControlRunnable("GarbageCollectRunnable"),
        mShrinking(aShrinking),
        mCollectChildren(aCollectChildren) {}

 private:
  virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    // Silence bad assertions, this can be dispatched from either the main
    // thread or the timer thread..
    return true;
  }

  virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
                            bool aDispatchResult) override {
    // Silence bad assertions, this can be dispatched from either the main
    // thread or the timer thread..
  }

  virtual bool WorkerRun(JSContext* aCx,
                         WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
    if (mShrinking) {
      // Either we've run the idle GC or explicit GC call from the parent,
      // we can cancel the current timers.
      aWorkerPrivate->CancelGCTimers();
    }
    return true;
  }
};

class CycleCollectRunnable final : public WorkerControlRunnable {
  bool mCollectChildren;

 public:
  CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
      : WorkerControlRunnable("CycleCollectRunnable"),
        mCollectChildren(aCollectChildren) {}

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->CycleCollectInternal(mCollectChildren);
    return true;
  }
};

class OfflineStatusChangeRunnable final : public WorkerThreadRunnable {
 public:
  OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
      : WorkerThreadRunnable("OfflineStatusChangeRunnable"),
        mIsOffline(aIsOffline) {}

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
    return true;
  }

 private:
  bool mIsOffline;
};

class MemoryPressureRunnable final : public WorkerControlRunnable {
 public:
  explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerControlRunnable("MemoryPressureRunnable") {}

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->MemoryPressureInternal();
    return true;
  }
};

#ifdef DEBUG
static bool StartsWithExplicit(nsACString& s) {
  return StringBeginsWith(s, "explicit/"_ns);
}
#endif

PRThread* PRThreadFromThread(nsIThread* aThread) {
  MOZ_ASSERT(aThread);

  PRThread* result;
  MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
  MOZ_ASSERT(result);

  return result;
}

// A runnable to cancel the worker from the parent thread when self.close() is
// called. This runnable is executed on the parent process in order to cancel
// the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be
// sure that all the pending WorkerDebuggeeRunnables are executed before this.
class CancelingOnParentRunnable final : public WorkerParentDebuggeeRunnable {
 public:
  explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerParentDebuggeeRunnable("CancelingOnParentRunnable") {}

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->Cancel();
    return true;
  }
};

// A runnable to cancel the worker from the parent process.
class CancelingWithTimeoutOnParentRunnable final
    : public WorkerParentControlRunnable {
 public:
  explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
      : WorkerParentControlRunnable("CancelingWithTimeoutOnParentRunnable") {}

  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    aWorkerPrivate->AssertIsOnParentThread();
    aWorkerPrivate->StartCancelingTimer();
    return true;
  }
};

class CancelingTimerCallback final : public nsITimerCallback {
 public:
  NS_DECL_ISUPPORTS

  explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
      : mWorkerPrivate(aWorkerPrivate) {}

  NS_IMETHOD
  Notify(nsITimer* aTimer) override {
    mWorkerPrivate->AssertIsOnParentThread();
    mWorkerPrivate->Cancel();
    return NS_OK;
  }

 private:
  ~CancelingTimerCallback() = default;

  // Raw pointer here is OK because the timer is canceled during the shutdown
  // steps.
  WorkerPrivate* mWorkerPrivate;
};

NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)

// This runnable starts the canceling of a worker after a self.close().
class CancelingRunnable final : public Runnable {
 public:
  CancelingRunnable() : Runnable("CancelingRunnable") {}

  NS_IMETHOD
  Run() override {
    LOG(WorkerLog(), ("CancelingRunnable::Run [%p]"this));
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    MOZ_ASSERT(workerPrivate);
    workerPrivate->AssertIsOnWorkerThread();

    // Now we can cancel the this worker from the parent process.
    RefPtr<CancelingOnParentRunnable> r =
        new CancelingOnParentRunnable(workerPrivate);
    r->Dispatch(workerPrivate);

    return NS_OK;
  }
};

/* anonymous namespace */

nsString ComputeWorkerPrivateId() {
  nsID uuid = nsID::GenerateUUID();
  return NSID_TrimBracketsUTF16(uuid);
}

class WorkerPrivate::EventTarget final : public nsISerialEventTarget {
  // This mutex protects mWorkerPrivate and must be acquired *before* the
  // WorkerPrivate's mutex whenever they must both be held.
  mozilla::Mutex mMutex;
  WorkerPrivate* mWorkerPrivate MOZ_GUARDED_BY(mMutex);
  nsCOMPtr<nsIEventTarget> mNestedEventTarget MOZ_GUARDED_BY(mMutex);
  bool mDisabled MOZ_GUARDED_BY(mMutex);
  bool mShutdown MOZ_GUARDED_BY(mMutex);

 public:
  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
      : mMutex("WorkerPrivate::EventTarget::mMutex"),
        mWorkerPrivate(aWorkerPrivate),
        mNestedEventTarget(aNestedEventTarget),
        mDisabled(false),
        mShutdown(false) {
    MOZ_ASSERT(aWorkerPrivate);
    MOZ_ASSERT(aNestedEventTarget);
  }

  void Disable() {
    {
      MutexAutoLock lock(mMutex);

      // Note, Disable() can be called more than once safely.
      mDisabled = true;
    }
  }

  void Shutdown() {
    nsCOMPtr<nsIEventTarget> nestedEventTarget;
    {
      MutexAutoLock lock(mMutex);

      mWorkerPrivate = nullptr;
      mNestedEventTarget.swap(nestedEventTarget);
      MOZ_ASSERT(mDisabled);
      mShutdown = true;
    }
  }

  RefPtr<nsIEventTarget> GetNestedEventTarget() {
    RefPtr<nsIEventTarget> nestedEventTarget = nullptr;
    {
      MutexAutoLock lock(mMutex);
      if (mWorkerPrivate) {
        mWorkerPrivate->AssertIsOnWorkerThread();
        nestedEventTarget = mNestedEventTarget.get();
      }
    }
    return nestedEventTarget;
  }

  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIEVENTTARGET_FULL

 private:
  ~EventTarget() = default;
};

struct WorkerPrivate::TimeoutInfo {
  TimeoutInfo()
      : mId(0),
        mNestingLevel(0),
        mReason(Timeout::Reason::eTimeoutOrInterval),
        mIsInterval(false),
        mCanceled(false),
        mOnChromeWorker(false) {
    MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
  }

  ~TimeoutInfo() { MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo); }

  bool operator==(const TimeoutInfo& aOther) const {
    return mTargetTime == aOther.mTargetTime;
  }

  bool operator<(const TimeoutInfo& aOther) const {
    return mTargetTime < aOther.mTargetTime;
  }

  void AccumulateNestingLevel(const uint32_t& aBaseLevel) {
    if (aBaseLevel < StaticPrefs::dom_clamp_timeout_nesting_level_AtStartup()) {
      mNestingLevel = aBaseLevel + 1;
      return;
    }
    mNestingLevel = StaticPrefs::dom_clamp_timeout_nesting_level_AtStartup();
  }

  void CalculateTargetTime() {
    auto target = mInterval;
    // Don't clamp timeout for chrome workers
    if (mNestingLevel >=
            StaticPrefs::dom_clamp_timeout_nesting_level_AtStartup() &&
        !mOnChromeWorker) {
      target = TimeDuration::Max(
          mInterval,
          TimeDuration::FromMilliseconds(StaticPrefs::dom_min_timeout_value()));
    }
    mTargetTime = TimeStamp::Now() + target;
  }

  RefPtr<TimeoutHandler> mHandler;
  mozilla::TimeStamp mTargetTime;
  mozilla::TimeDuration mInterval;
  int32_t mId;
  uint32_t mNestingLevel;
  Timeout::Reason mReason;
  bool mIsInterval;
  bool mCanceled;
  bool mOnChromeWorker;
};

class WorkerJSContextStats final : public JS::RuntimeStats {
  const nsCString mRtPath;

 public:
  explicit WorkerJSContextStats(const nsACString& aRtPath)
      : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {}

  ~WorkerJSContextStats() {
    for (JS::ZoneStats& stats : zoneStatsVector) {
      delete static_cast<xpc::ZoneStatsExtras*>(stats.extra);
    }

    for (JS::RealmStats& stats : realmStatsVector) {
      delete static_cast<xpc::RealmStatsExtras*>(stats.extra);
    }
  }

  const nsCString& Path() const { return mRtPath; }

  virtual void initExtraZoneStats(JS::Zone* aZone, JS::ZoneStats* aZoneStats,
                                  const JS::AutoRequireNoGC& nogc) override {
    MOZ_ASSERT(!aZoneStats->extra);

    // ReportJSRuntimeExplicitTreeStats expects that
    // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
    xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
    extras->pathPrefix = mRtPath;
    extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone);

    MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));

    aZoneStats->extra = extras;
  }

  virtual void initExtraRealmStats(JS::Realm* aRealm,
                                   JS::RealmStats* aRealmStats,
                                   const JS::AutoRequireNoGC& nogc) override {
    MOZ_ASSERT(!aRealmStats->extra);

    // ReportJSRuntimeExplicitTreeStats expects that
    // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
    xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;

    // This is the |jsPathPrefix|.  Each worker has exactly one realm.
    extras->jsPathPrefix.Assign(mRtPath);
    extras->jsPathPrefix +=
        nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm));
    extras->jsPathPrefix += "realm(web-worker)/"_ns;

    // This should never be used when reporting with workers (hence the "?!").
    extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");

    MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
    MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));

    extras->location = nullptr;

    aRealmStats->extra = extras;
  }
};

class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter {
  NS_DECL_THREADSAFE_ISUPPORTS

  friend class WorkerPrivate;

  SharedMutex mMutex;
  WorkerPrivate* mWorkerPrivate;

 public:
  explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
      : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) {
    aWorkerPrivate->AssertIsOnWorkerThread();
  }

  NS_IMETHOD
  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
                 bool aAnonymize) override;

 private:
  class FinishCollectRunnable;

  class CollectReportsRunnable final : public MainThreadWorkerControlRunnable {
    RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
    const bool mAnonymize;

   public:
    CollectReportsRunnable(WorkerPrivate* aWorkerPrivate,
                           nsIHandleReportCallback* aHandleReport,
                           nsISupports* aHandlerData, bool aAnonymize,
                           const nsACString& aPath);

   private:
    bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;

    ~CollectReportsRunnable() {
      if (NS_IsMainThread()) {
        mFinishCollectRunnable->Run();
        return;
      }

      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
      MOZ_ASSERT(workerPrivate);
      MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging(
          mFinishCollectRunnable.forget()));
    }
  };

  class FinishCollectRunnable final : public Runnable {
    nsCOMPtr<nsIHandleReportCallback> mHandleReport;
    nsCOMPtr<nsISupports> mHandlerData;
    size_t mPerformanceUserEntries;
    size_t mPerformanceResourceEntries;
    const bool mAnonymize;
    bool mSuccess;

   public:
    WorkerJSContextStats mCxStats;

    explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport,
                                   nsISupports* aHandlerData, bool aAnonymize,
                                   const nsACString& aPath);

    NS_IMETHOD Run() override;

    void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) {
      mPerformanceUserEntries = userEntries;
      mPerformanceResourceEntries = resourceEntries;
    }

    void SetSuccess(bool success) { mSuccess = success; }

    FinishCollectRunnable(const FinishCollectRunnable&) = delete;
    FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
    FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;

   private:
    ~FinishCollectRunnable() {
      // mHandleReport and mHandlerData are released on the main thread.
      AssertIsOnMainThread();
    }
  };

  ~MemoryReporter() = default;

  void Disable() {
    // Called from WorkerPrivate::DisableMemoryReporter.
    mMutex.AssertCurrentThreadOwns();

    NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
    mWorkerPrivate = nullptr;
  }
};

NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)

NS_IMETHODIMP
WorkerPrivate::MemoryReporter::CollectReports(
    nsIHandleReportCallback* aHandleReport, nsISupports* aData,
    bool aAnonymize) {
  AssertIsOnMainThread();

  RefPtr<CollectReportsRunnable> runnable;

  {
    MutexAutoLock lock(mMutex);

    if (!mWorkerPrivate) {
      // This will effectively report 0 memory.
      nsCOMPtr<nsIMemoryReporterManager> manager =
          do_GetService("@mozilla.org/memory-reporter-manager;1");
      if (manager) {
        manager->EndReport();
      }
      return NS_OK;
    }

    nsAutoCString path;
    path.AppendLiteral("explicit/workers/workers(");
    if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
      path.AppendLiteral(")/worker(");
    } else {
      nsAutoCString escapedDomain(mWorkerPrivate->Domain());
      if (escapedDomain.IsEmpty()) {
        escapedDomain += "chrome";
      } else {
        escapedDomain.ReplaceChar('/''\\');
      }
      path.Append(escapedDomain);
      path.AppendLiteral(")/worker(");
      NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
      escapedURL.ReplaceChar('/''\\');
      path.Append(escapedURL);
    }
    path.AppendPrintf(", 0x%p)/"static_cast<void*>(mWorkerPrivate));

    runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData,
                                          aAnonymize, path);
  }

  if (!runnable->Dispatch(mWorkerPrivate)) {
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
    WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport,
    nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath)
    : MainThreadWorkerControlRunnable("CollectReportsRunnable"),
      mFinishCollectRunnable(new FinishCollectRunnable(
          aHandleReport, aHandlerData, aAnonymize, aPath)),
      mAnonymize(aAnonymize) {}

bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(
    JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
  aWorkerPrivate->AssertIsOnWorkerThread();

  RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
  RefPtr<Performance> performance =
      scope ? scope->GetPerformanceIfExists() : nullptr;
  if (performance) {
    size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
    size_t resourceEntries =
        performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
    mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
  }

  mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(
      &mFinishCollectRunnable->mCxStats, mAnonymize));

  return true;
}

WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
    nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData,
    bool aAnonymize, const nsACString& aPath)
    : mozilla::Runnable(
          "dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),
      mHandleReport(aHandleReport),
      mHandlerData(aHandlerData),
      mPerformanceUserEntries(0),
      mPerformanceResourceEntries(0),
      mAnonymize(aAnonymize),
      mSuccess(false),
      mCxStats(aPath) {}

NS_IMETHODIMP
WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() {
  AssertIsOnMainThread();

  nsCOMPtr<nsIMemoryReporterManager> manager =
      do_GetService("@mozilla.org/memory-reporter-manager;1");

  if (!manager) return NS_OK;

  if (mSuccess) {
    xpc::ReportJSRuntimeExplicitTreeStats(
        mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize);

    if (mPerformanceUserEntries) {
      nsCString path = mCxStats.Path();
      path.AppendLiteral("dom/performance/user-entries");
      mHandleReport->Callback(""_ns, path, nsIMemoryReporter::KIND_HEAP,
                              nsIMemoryReporter::UNITS_BYTES,
                              static_cast<int64_t>(mPerformanceUserEntries),
                              "Memory used for performance user entries."_ns,
                              mHandlerData);
    }

    if (mPerformanceResourceEntries) {
      nsCString path = mCxStats.Path();
      path.AppendLiteral("dom/performance/resource-entries");
      mHandleReport->Callback(
          ""_ns, path, nsIMemoryReporter::KIND_HEAP,
          nsIMemoryReporter::UNITS_BYTES,
          static_cast<int64_t>(mPerformanceResourceEntries),
          "Memory used for performance resource entries."_ns, mHandlerData);
    }
  }

  manager->EndReport();

  return NS_OK;
}

WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
    : mEventTarget(aEventTarget),
      mResult(NS_ERROR_FAILURE),
      mCompleted(false)
#ifdef DEBUG
      ,
      mHasRun(false)
#endif
{
}

Document* WorkerPrivate::GetDocument() const {
  AssertIsOnMainThread();
  if (nsPIDOMWindowInner* window = GetAncestorWindow()) {
    return window->GetExtantDoc();
  }
  // couldn't query a document, give up and return nullptr
  return nullptr;
}

nsPIDOMWindowInner* WorkerPrivate::GetAncestorWindow() const {
  AssertIsOnMainThread();

  // We should query the window from the top level worker in case of a nested
  // worker, as only the top level one can have a window.
  WorkerPrivate* top = GetTopLevelWorker();
  return top->GetWindow();
}

class EvictFromBFCacheRunnable final : public WorkerProxyToMainThreadRunnable {
 public:
  void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override {
    MOZ_ASSERT(aWorkerPrivate);
    AssertIsOnMainThread();
    if (nsCOMPtr<nsPIDOMWindowInner> win =
            aWorkerPrivate->GetAncestorWindow()) {
      win->RemoveFromBFCacheSync();
    }
  }

  void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override {
    MOZ_ASSERT(aWorkerPrivate);
    aWorkerPrivate->AssertIsOnWorkerThread();
  }
};

void WorkerPrivate::EvictFromBFCache() {
  AssertIsOnWorkerThread();
  RefPtr<EvictFromBFCacheRunnable> runnable = new EvictFromBFCacheRunnable();
  runnable->Dispatch(this);
}

void WorkerPrivate::SetCsp(nsIContentSecurityPolicy* aCSP) {
  AssertIsOnMainThread();
  if (!aCSP) {
    return;
  }
  aCSP->EnsureEventTarget(mMainThreadEventTarget);

  mLoadInfo.mCSP = aCSP;
  mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
  nsresult rv = CSPToCSPInfo(mLoadInfo.mCSP, mLoadInfo.mCSPInfo.get());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }
}

nsresult WorkerPrivate::SetCSPFromHeaderValues(
    const nsACString& aCSPHeaderValue,
    const nsACString& aCSPReportOnlyHeaderValue) {
  AssertIsOnMainThread();
  MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);

  NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
  NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);

  nsresult rv;
  nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();

  // First, we try to query the URI from the Principal, but
  // in case selfURI remains empty (e.g in case the Principal
  // is a SystemPrincipal) then we fall back and use the
  // base URI as selfURI for CSP.
  nsCOMPtr<nsIURI> selfURI;
  // Its not recommended to use the BasePrincipal to get the URI
  // but in this case we need to make an exception
  auto* basePrin = BasePrincipal::Cast(mLoadInfo.mPrincipal);
  if (basePrin) {
    basePrin->GetURI(getter_AddRefs(selfURI));
  }
  if (!selfURI) {
    selfURI = mLoadInfo.mBaseURI;
  }
  MOZ_ASSERT(selfURI, "need a self URI for CSP");

  rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI, ""_ns,
                                           0);
  NS_ENSURE_SUCCESS(rv, rv);

  csp->EnsureEventTarget(mMainThreadEventTarget);

  // If there's a CSP header, apply it.
  if (!cspHeaderValue.IsEmpty()) {
    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  // If there's a report-only CSP header, apply it.
  if (!cspROHeaderValue.IsEmpty()) {
    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  RefPtr<extensions::WebExtensionPolicy> addonPolicy;

  if (basePrin) {
    addonPolicy = basePrin->AddonPolicy();
  }

  // For extension workers there aren't any csp header values,
  // instead it will inherit the Extension CSP.
  if (addonPolicy) {
    csp->AppendPolicy(addonPolicy->BaseCSP(), falsefalse);
    csp->AppendPolicy(addonPolicy->ExtensionPageCSP(), falsefalse);
  }

  mLoadInfo.mCSP = csp;

  // Set evalAllowed, default value is set in GetAllowsEval
  bool evalAllowed = false;
  bool reportEvalViolations = false;
  rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
  NS_ENSURE_SUCCESS(rv, rv);

  mLoadInfo.mEvalAllowed = evalAllowed;
  mLoadInfo.mReportEvalCSPViolations = reportEvalViolations;

  // Set wasmEvalAllowed
  bool wasmEvalAllowed = false;
  bool reportWasmEvalViolations = false;
  rv = csp->GetAllowsWasmEval(&reportWasmEvalViolations, &wasmEvalAllowed);
  NS_ENSURE_SUCCESS(rv, rv);

  // As for nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction,
  // for MV2 extensions we have to allow wasm by default and report violations
  // for historical reasons.
  // TODO bug 1770909: remove this exception.
  if (!wasmEvalAllowed && addonPolicy && addonPolicy->ManifestVersion() == 2) {
    wasmEvalAllowed = true;
    reportWasmEvalViolations = true;
  }

  mLoadInfo.mWasmEvalAllowed = wasmEvalAllowed;
  mLoadInfo.mReportWasmEvalCSPViolations = reportWasmEvalViolations;

  mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
  rv = CSPToCSPInfo(csp, mLoadInfo.mCSPInfo.get());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  return NS_OK;
}

bool WorkerPrivate::IsFrozenForWorkerThread() const {
  auto data = mWorkerThreadAccessible.Access();
  return data->mFrozen;
}

bool WorkerPrivate::IsFrozen() const {
  AssertIsOnParentThread();
  return mParentFrozen;
}

void WorkerPrivate::StoreCSPOnClient() {
  auto data = mWorkerThreadAccessible.Access();
  MOZ_ASSERT(data->mScope);
  if (mLoadInfo.mCSPInfo) {
    data->mScope->MutableClientSourceRef().SetCspInfo(*mLoadInfo.mCSPInfo);
  }
}

void WorkerPrivate::UpdateReferrerInfoFromHeader(
    const nsACString& aReferrerPolicyHeaderValue) {
  NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);

  if (headerValue.IsEmpty()) {
    return;
  }

  ReferrerPolicy policy =
      ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue);
  if (policy == ReferrerPolicy::_empty) {
    return;
  }

  nsCOMPtr<nsIReferrerInfo> referrerInfo =
      static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy);
  SetReferrerInfo(referrerInfo);
}

void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
  AssertIsOnParentThread();

  // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
  // Worker object, which is really held by the worker thread.  We traverse this
  // reference if and only if all main thread event queues are empty, no
  // shutdown tasks, no StrongWorkerRefs, no child workers, no timeouts, no
  // blocking background actors, and we have not released the main thread
  // reference.  We do not unlink it. This allows the CC to break cycles
  // involving the Worker and begin shutting it down (which does happen in
  // unlink) but ensures that the WorkerPrivate won't be deleted before we're
  // done shutting down the thread.
  if (IsEligibleForCC() && !mMainThreadObjectsForgotten) {
    nsCycleCollectionTraversalCallback& cb = aCb;
    WorkerPrivate* tmp = this;
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
  }
}

nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
                                 nsIEventTarget* aSyncLoopTarget) {
  // May be called on any thread!
  RefPtr<WorkerRunnable> runnable(aRunnable);

  LOGV(("WorkerPrivate::Dispatch [%p] runnable %p"this, runnable.get()));
  if (!aSyncLoopTarget) {
    // Dispatch control runnable
    if (runnable->IsControlRunnable()) {
      return DispatchControlRunnable(runnable.forget());
    }

    // Dispatch debugger runnable
    if (runnable->IsDebuggerRunnable()) {
      return DispatchDebuggerRunnable(runnable.forget());
    }
  }
  MutexAutoLock lock(mMutex);
  return DispatchLockHeld(runnable.forget(), aSyncLoopTarget, lock);
}

nsresult WorkerPrivate::DispatchToParent(
    already_AddRefed<WorkerRunnable> aRunnable) {
  RefPtr<WorkerRunnable> runnable(aRunnable);

  LOGV(("WorkerPrivate::DispatchToParent [%p] runnable %p"this,
        runnable.get()));

  WorkerPrivate* parent = GetParent();
  // Dispatch to parent worker
  if (parent) {
    if (runnable->IsControlRunnable()) {
      return parent->DispatchControlRunnable(runnable.forget());
    }
    return parent->Dispatch(runnable.forget());
  }

  // Dispatch to main thread
  if (runnable->IsDebuggeeRunnable()) {
    RefPtr<WorkerParentDebuggeeRunnable> debuggeeRunnable =
        runnable.forget().downcast<WorkerParentDebuggeeRunnable>();
    return DispatchDebuggeeToMainThread(debuggeeRunnable.forget(),
                                        NS_DISPATCH_NORMAL);
  }
  return DispatchToMainThread(runnable.forget());
}

nsresult WorkerPrivate::DispatchLockHeld(
    already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget,
    const MutexAutoLock& aProofOfLock) {
  // May be called on any thread!
  RefPtr<WorkerRunnable> runnable(aRunnable);
  LOGV(("WorkerPrivate::DispatchLockHeld [%p] runnable: %p"this,
        runnable.get()));

  MOZ_ASSERT_IF(aSyncLoopTarget, mThread);

  // Dispatch normal worker runnable
  if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Canceling)) {
    NS_WARNING(
        "A runnable was posted to a worker that is already shutting "
        "down!");
    return NS_ERROR_UNEXPECTED;
  }

  if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
    MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
    mDelayedDebuggeeRunnables.AppendElement(runnable);
    return NS_OK;
  }

  if (!mThread) {
    if (ParentStatus() == Pending || mStatus == Pending) {
      LOGV(
          ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is queued in "
           "mPreStartRunnables",
           this, runnable.get()));
      RefPtr<WorkerThreadRunnable> workerThreadRunnable =
          static_cast<WorkerThreadRunnable*>(runnable.get());
      mPreStartRunnables.AppendElement(workerThreadRunnable);
      return NS_OK;
    }

    NS_WARNING(
        "Using a worker event target after the thread has already"
        "been released!");
    return NS_ERROR_UNEXPECTED;
  }

  nsresult rv;
  if (aSyncLoopTarget) {
    LOGV(
        ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to a "
         "SyncLoop(%p)",
         this, runnable.get(), aSyncLoopTarget));
    rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
  } else {
    // If mStatus is Pending, the WorkerPrivate initialization still can fail.
    // Append this WorkerThreadRunnable to WorkerPrivate::mPreStartRunnables,
    // such that this WorkerThreadRunnable can get the correct value of
    // mCleanPreStartDispatching in WorkerPrivate::RunLoopNeverRan().
    if (mStatus == Pending) {
      LOGV(
          ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is append in "
           "mPreStartRunnables",
           this, runnable.get()));
      RefPtr<WorkerThreadRunnable> workerThreadRunnable =
          static_cast<WorkerThreadRunnable*>(runnable.get());
      mPreStartRunnables.AppendElement(workerThreadRunnable);
    }

    // WorkerDebuggeeRunnables don't need any special treatment here. True,
    // they should not be delivered to a frozen worker. But frozen workers
    // aren't drawing from the thread's main event queue anyway, only from
    // mControlQueue.
    LOGV(
        ("WorkerPrivate::DispatchLockHeld [%p] runnable %p dispatch to the "
         "main event queue",
         this, runnable.get()));
    rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
  }

  if (NS_WARN_IF(NS_FAILED(rv))) {
    LOGV(("WorkerPrivate::Dispatch Failed [%p]"this));
    return rv;
  }

  mCondVar.Notify();
  return NS_OK;
}

void WorkerPrivate::EnableDebugger() {
  AssertIsOnParentThread();

  if (NS_FAILED(RegisterWorkerDebugger(this))) {
    NS_WARNING("Failed to register worker debugger!");
    return;
  }
}

void WorkerPrivate::DisableDebugger() {
  AssertIsOnParentThread();

  // RegisterDebuggerMainThreadRunnable might be dispatched but not executed.
  // Wait for its execution before unregistraion.
  if (!NS_IsMainThread()) {
    WaitForIsDebuggerRegistered(true);
  }

  if (NS_FAILED(UnregisterWorkerDebugger(this))) {
    NS_WARNING("Failed to unregister worker debugger!");
  }
}

nsresult WorkerPrivate::DispatchControlRunnable(
    already_AddRefed<WorkerRunnable> aWorkerRunnable) {
  // May be called on any thread!
  RefPtr<WorkerRunnable> runnable(aWorkerRunnable);
  MOZ_ASSERT_DEBUG_OR_FUZZING(runnable && runnable->IsControlRunnable());

  LOG(WorkerLog(), ("WorkerPrivate::DispatchControlRunnable [%p] runnable %p",
                    this, runnable.get()));

  {
    MutexAutoLock lock(mMutex);

    if (mStatus == Dead) {
      return NS_ERROR_UNEXPECTED;
    }

    // Transfer ownership to the control queue.
    mControlQueue.Push(runnable.forget().take());

    if (JSContext* cx = mJSContext) {
      MOZ_ASSERT(mThread);
      JS_RequestInterruptCallback(cx);
    }

    mCondVar.Notify();
  }

  return NS_OK;
}

void DebuggerInterruptTimerCallback(nsITimer* aTimer, void* aClosure)
    MOZ_NO_THREAD_SAFETY_ANALYSIS {
  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
  workerPrivate->DebuggerInterruptRequest();
}

nsresult WorkerPrivate::DispatchDebuggerRunnable(
    already_AddRefed<WorkerRunnable> aDebuggerRunnable) {
  // May be called on any thread!

  RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);

  MOZ_ASSERT(runnable);

  MutexAutoLock lock(mMutex);
  if (!mDebuggerInterruptTimer) {
    // There is no timer, so we need to create one.  For locking discipline
    // purposes we can't manipulate the timer while our mutex is held so
    // drop the mutex while we build and configure the timer.  Only this
    // function here on the main thread will create a timer, so we're not
    // racing anyone to create or assign the timer.
    nsCOMPtr<nsITimer> timer;
    {
      MutexAutoUnlock unlock(mMutex);
      timer = NS_NewTimer();
      MOZ_ALWAYS_SUCCEEDS(timer->SetTarget(mWorkerControlEventTarget));

      // Whenever an event is scheduled on the WorkerControlEventTarget an
      // interrupt is automatically requested which causes us to yield JS
      // execution and the next JS execution in the queue to execute. This
      // allows for simple code reuse of the existing interrupt callback code
      // used for control events.
      MOZ_ALWAYS_SUCCEEDS(timer->InitWithNamedFuncCallback(
          DebuggerInterruptTimerCallback, nullptr,
          DEBUGGER_RUNNABLE_INTERRUPT_AFTER_MS, nsITimer::TYPE_ONE_SHOT,
          "dom:DebuggerInterruptTimer"));
    }

    // okay, we have our mutex back now, put the timer in place.
    mDebuggerInterruptTimer.swap(timer);
  }

  if (mStatus == Dead) {
    NS_WARNING(
        "A debugger runnable was posted to a worker that is already "
        "shutting down!");
    return NS_ERROR_UNEXPECTED;
  }

  // Transfer ownership to the debugger queue.
  mDebuggerQueue.Push(runnable.forget().take());

  mCondVar.Notify();

  return NS_OK;
}

void WorkerPrivate::DebuggerInterruptRequest() {
  AssertIsOnWorkerThread();

  auto data = mWorkerThreadAccessible.Access();
  data->mDebuggerInterruptRequested = true;
}

already_AddRefed<WorkerRunnable> WorkerPrivate::MaybeWrapAsWorkerRunnable(
    already_AddRefed<nsIRunnable> aRunnable) {
  // May be called on any thread!

  nsCOMPtr<nsIRunnable> runnable(aRunnable);
  MOZ_ASSERT(runnable);

  LOGV(("WorkerPrivate::MaybeWrapAsWorkerRunnable [%p] runnable: %p"this,
        runnable.get()));

  RefPtr<WorkerRunnable> workerRunnable =
      WorkerRunnable::FromRunnable(runnable);
  if (workerRunnable) {
    return workerRunnable.forget();
  }

  workerRunnable = new ExternalRunnableWrapper(this, runnable);
  return workerRunnable.forget();
}

bool WorkerPrivate::Start() {
  // May be called on any thread!
  LOG(WorkerLog(), ("WorkerPrivate::Start [%p]"this));
  {
    MutexAutoLock lock(mMutex);
    NS_ASSERTION(mParentStatus != Running, "How can this be?!");

    if (mParentStatus == Pending) {
      mParentStatus = Running;
      return true;
    }
  }

  return false;
}

// aCx is null when called from the finalizer
bool WorkerPrivate::Notify(WorkerStatus aStatus) {
  AssertIsOnParentThread();
  // This method is only called for Canceling or later.
  MOZ_DIAGNOSTIC_ASSERT(aStatus >= Canceling);

  bool pending;
  {
    MutexAutoLock lock(mMutex);

    if (mParentStatus >= aStatus) {
      return true;
    }

    pending = mParentStatus == Pending;
    mParentStatus = aStatus;
  }

  if (mCancellationCallback) {
    mCancellationCallback(!pending);
    mCancellationCallback = nullptr;
  }

  mParentRef->DropWorkerPrivate();

  if (pending) {
#ifdef DEBUG
    {
      // Fake a thread here just so that our assertions don't go off for no
      // reason.
      nsIThread* currentThread = NS_GetCurrentThread();
      MOZ_ASSERT(currentThread);

      MOZ_ASSERT(!mPRThread);
      mPRThread = PRThreadFromThread(currentThread);
      MOZ_ASSERT(mPRThread);
    }
#endif

    // Worker never got a chance to run, go ahead and delete it.
    ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
    return true;
  }

  // No Canceling timeout is needed.
  if (mCancelingTimer) {
    mCancelingTimer->Cancel();
    mCancelingTimer = nullptr;
  }

  // The NotifyRunnable kicks off a series of events that need the
  // CancelingOnParentRunnable to be executed always.
  // Note that we already advanced mParentStatus above and we check that
  // status in all other (asynchronous) call sites of SetIsPaused.
  if (!mParent) {
    MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(false));
  }

  RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
  return runnable->Dispatch(this);
}

bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) {
  AssertIsOnParentThread();

  mParentFrozen = true;

  bool isCanceling = false;
  {
    MutexAutoLock lock(mMutex);

    isCanceling = mParentStatus >= Canceling;
  }

  // WorkerDebuggeeRunnables sent from a worker to content must not be
  // delivered while the worker is frozen.
  //
  // Since a top-level worker and all its children share the same
  // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
  // top-level worker.
  if (aWindow) {
    // This is called from WorkerPrivate construction, and We may not have
    // allocated mMainThreadDebuggeeEventTarget yet.
    if (mMainThreadDebuggeeEventTarget) {
      // Pausing a ThrottledEventQueue is infallible.
      MOZ_ALWAYS_SUCCEEDS(
          mMainThreadDebuggeeEventTarget->SetIsPaused(!isCanceling));
    }
  }

  if (isCanceling) {
    return true;
  }

  DisableDebugger();

  RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
  return runnable->Dispatch(this);
}

bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) {
  AssertIsOnParentThread();
  MOZ_ASSERT(mParentFrozen);

  mParentFrozen = false;

  {
    bool isCanceling = false;

    {
      MutexAutoLock lock(mMutex);

      isCanceling = mParentStatus >= Canceling;
    }

    // Delivery of WorkerDebuggeeRunnables to the window may resume.
    //
    // Since a top-level worker and all its children share the same
    // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
    // top-level worker.
    if (aWindow) {
      // Since the worker is no longer frozen, only a paused parent window
      // should require the queue to remain paused.
      //
      // This can only fail if the ThrottledEventQueue cannot dispatch its
      // executor to the main thread, in which case the main thread was never
      // going to draw runnables from it anyway, so the failure doesn't matter.
      Unused << mMainThreadDebuggeeEventTarget->SetIsPaused(
          IsParentWindowPaused() && !isCanceling);
    }

    if (isCanceling) {
      return true;
    }
  }

  EnableDebugger();

  RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
  return runnable->Dispatch(this);
}

void WorkerPrivate::ParentWindowPaused() {
  AssertIsOnMainThread();
  MOZ_ASSERT(!mParentWindowPaused);
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.