summaryrefslogtreecommitdiffstats
path: root/dom/workers/WorkerPrivate.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/WorkerPrivate.h')
-rw-r--r--dom/workers/WorkerPrivate.h1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
new file mode 100644
index 0000000000..a670d00975
--- /dev/null
+++ b/dom/workers/WorkerPrivate.h
@@ -0,0 +1,1666 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_workers_workerprivate_h__
+#define mozilla_dom_workers_workerprivate_h__
+
+#include <bitset>
+#include "MainThreadUtils.h"
+#include "ScriptLoader.h"
+#include "js/ContextOptions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/OriginTrials.h"
+#include "mozilla/RelativeTimeline.h"
+#include "mozilla/Result.h"
+#include "mozilla/StorageAccess.h"
+#include "mozilla/ThreadBound.h"
+#include "mozilla/ThreadSafeWeakPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UseCounter.h"
+#include "mozilla/dom/ClientSource.h"
+#include "mozilla/dom/FlippedOnce.h"
+#include "mozilla/dom/Timeout.h"
+#include "mozilla/dom/quota/CheckedUnsafePtr.h"
+#include "mozilla/dom/Worker.h"
+#include "mozilla/dom/WorkerBinding.h"
+#include "mozilla/dom/WorkerCommon.h"
+#include "mozilla/dom/WorkerLoadInfo.h"
+#include "mozilla/dom/WorkerStatus.h"
+#include "mozilla/dom/workerinternals/JSSettings.h"
+#include "mozilla/dom/workerinternals/Queue.h"
+#include "mozilla/dom/JSExecutionManager.h"
+#include "mozilla/net/NeckoChannelParams.h"
+#include "mozilla/StaticPrefs_extensions.h"
+#include "nsContentUtils.h"
+#include "nsIChannel.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIEventTarget.h"
+#include "nsILoadInfo.h"
+#include "nsRFPService.h"
+#include "nsTObserverArray.h"
+#include "stdint.h"
+
+class nsIThreadInternal;
+
+namespace JS {
+struct RuntimeStats;
+}
+
+namespace mozilla {
+class ThrottledEventQueue;
+namespace dom {
+
+class RemoteWorkerChild;
+
+// If you change this, the corresponding list in nsIWorkerDebugger.idl needs
+// to be updated too. And histograms enum for worker use counters uses the same
+// order of worker kind. Please also update dom/base/usecounters.py.
+enum WorkerKind : uint8_t {
+ WorkerKindDedicated,
+ WorkerKindShared,
+ WorkerKindService
+};
+
+class ClientInfo;
+class ClientSource;
+class Function;
+class JSExecutionManager;
+class MessagePort;
+class UniqueMessagePortId;
+class PerformanceStorage;
+class TimeoutHandler;
+class WorkerControlRunnable;
+class WorkerCSPEventListener;
+class WorkerDebugger;
+class WorkerDebuggerGlobalScope;
+class WorkerErrorReport;
+class WorkerEventTarget;
+class WorkerGlobalScope;
+class WorkerRef;
+class WorkerRunnable;
+class WorkerDebuggeeRunnable;
+class WorkerThread;
+
+// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
+// object. It exists to avoid changing a lot of code to use Mutex* instead of
+// Mutex&.
+class MOZ_CAPABILITY("mutex") SharedMutex {
+ using Mutex = mozilla::Mutex;
+
+ class MOZ_CAPABILITY("mutex") RefCountedMutex final : public Mutex {
+ public:
+ explicit RefCountedMutex(const char* aName) : Mutex(aName) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMutex)
+
+ private:
+ ~RefCountedMutex() = default;
+ };
+
+ const RefPtr<RefCountedMutex> mMutex;
+
+ public:
+ explicit SharedMutex(const char* aName)
+ : mMutex(new RefCountedMutex(aName)) {}
+
+ SharedMutex(const SharedMutex& aOther) = default;
+
+ operator Mutex&() MOZ_RETURN_CAPABILITY(this) { return *mMutex; }
+
+ operator const Mutex&() const MOZ_RETURN_CAPABILITY(this) { return *mMutex; }
+
+ // We need these to make thread-safety analysis work
+ void Lock() MOZ_CAPABILITY_ACQUIRE() { mMutex->Lock(); }
+ void Unlock() MOZ_CAPABILITY_RELEASE() { mMutex->Unlock(); }
+
+ // We can assert we own 'this', but we can't assert we hold mMutex
+ void AssertCurrentThreadOwns() const
+ MOZ_ASSERT_CAPABILITY(this) MOZ_NO_THREAD_SAFETY_ANALYSIS {
+ mMutex->AssertCurrentThreadOwns();
+ }
+};
+
+nsString ComputeWorkerPrivateId();
+
+class WorkerPrivate final
+ : public RelativeTimeline,
+ public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
+ public:
+ // Callback invoked on the parent thread when the worker's cancellation is
+ // about to be requested. This covers both calls to
+ // WorkerPrivate::Cancel() by the owner as well as self-initiated cancellation
+ // due to top-level script evaluation failing or close() being invoked on the
+ // global scope for Dedicated and Shared workers, but not Service Workers as
+ // they do not expose a close() method.
+ //
+ // ### Parent-Initiated Cancellation
+ //
+ // When WorkerPrivate::Cancel is invoked on the parent thread (by the binding
+ // exposed Worker::Terminate), this callback is invoked synchronously inside
+ // that call.
+ //
+ // ### Worker Self-Cancellation
+ //
+ // When a worker initiates self-cancellation, the worker's notification to the
+ // parent thread is a non-blocking, async mechanism triggered by
+ // `WorkerPrivate::DispatchCancelingRunnable`.
+ //
+ // Self-cancellation races a normally scheduled runnable against a timer that
+ // is scheduled against the parent. The 2 paths initiated by
+ // DispatchCancelingRunnable are:
+ //
+ // 1. A CancelingRunnable is dispatched at the worker's normal event target to
+ // wait for the event loop to be clear of runnables. When the
+ // CancelingRunnable runs it will dispatch a CancelingOnParentRunnable to
+ // its parent which is a normal, non-control WorkerDebuggeeRunnable to
+ // ensure that any postMessages to the parent or similar events get a
+ // chance to be processed prior to cancellation. The timer scheduled in
+ // the next bullet will not be canceled unless
+ //
+ // 2. A CancelingWithTimeoutOnParentRunnable control runnable is dispatched
+ // to the parent to schedule a timer which will (also) fire on the parent
+ // thread. This handles the case where the worker does not yield
+ // control-flow, and so the normal runnable scheduled above does not get to
+ // run in a timely fashion. Because this is a control runnable, if the
+ // parent is a worker then the runnable will be processed with urgency.
+ // However, if the worker is top-level, then the control-like throttled
+ // WorkerPrivate::mMainThreadEventTarget will end up getting used which is
+ // nsIRunnablePriority::PRIORITY_MEDIUMHIGH and distinct from the
+ // mMainThreadDebuggeeEventTarget which most runnables (like postMessage)
+ // use.
+ //
+ // The timer will explicitly use the control event target if the parent is
+ // a worker and the implicit event target (via `NS_NewTimer()`) otherwise.
+ // The callback is CancelingTimerCallback which just calls
+ // WorkerPrivate::Cancel.
+ using CancellationCallback = std::function<void(bool aEverRan)>;
+
+ // Callback invoked on the parent just prior to dropping the worker thread's
+ // strong reference that keeps the WorkerPrivate alive while the worker thread
+ // is running. This does not provide a guarantee that the underlying thread
+ // has fully shutdown, just that the worker logic has fully shutdown.
+ //
+ // ### Details
+ //
+ // The last thing the worker thread's WorkerThreadPrimaryRunnable does before
+ // initiating the shutdown of the underlying thread is call ScheduleDeletion.
+ // ScheduleDeletion dispatches a runnable to the parent to notify it that the
+ // worker has completed its work and will never touch the WorkerPrivate again
+ // and that the strong self-reference can be dropped.
+ //
+ // For parents that are themselves workers, this will be done by
+ // WorkerFinishedRunnable which is a WorkerControlRunnable, ensuring that this
+ // is processed in a timely fashion. For main-thread parents,
+ // TopLevelWorkerFinishedRunnable will be used and sent via
+ // mMainThreadEventTargetForMessaging which is a weird ThrottledEventQueue
+ // which does not provide any ordering guarantees relative to
+ // mMainThreadDebuggeeEventTarget, so if you want those, you need to enhance
+ // things.
+ using TerminationCallback = std::function<void(void)>;
+
+ struct LocationInfo {
+ nsCString mHref;
+ nsCString mProtocol;
+ nsCString mHost;
+ nsCString mHostname;
+ nsCString mPort;
+ nsCString mPathname;
+ nsCString mSearch;
+ nsCString mHash;
+ nsString mOrigin;
+ };
+
+ NS_INLINE_DECL_REFCOUNTING(WorkerPrivate)
+
+ static already_AddRefed<WorkerPrivate> Constructor(
+ JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
+ WorkerKind aWorkerKind, RequestCredentials aRequestCredentials,
+ const WorkerType aWorkerType, const nsAString& aWorkerName,
+ const nsACString& aServiceWorkerScope, WorkerLoadInfo* aLoadInfo,
+ ErrorResult& aRv, nsString aId = u""_ns,
+ CancellationCallback&& aCancellationCallback = {},
+ TerminationCallback&& aTerminationCallback = {});
+
+ enum LoadGroupBehavior { InheritLoadGroup, OverrideLoadGroup };
+
+ static nsresult GetLoadInfo(
+ JSContext* aCx, nsPIDOMWindowInner* aWindow, WorkerPrivate* aParent,
+ const nsAString& aScriptURL, const enum WorkerType& aWorkerType,
+ const RequestCredentials& aCredentials, bool aIsChromeWorker,
+ LoadGroupBehavior aLoadGroupBehavior, WorkerKind aWorkerKind,
+ WorkerLoadInfo* aLoadInfo);
+
+ void Traverse(nsCycleCollectionTraversalCallback& aCb);
+
+ void ClearSelfAndParentEventTargetRef() {
+ AssertIsOnParentThread();
+ MOZ_ASSERT(mSelfRef);
+
+ if (mTerminationCallback) {
+ mTerminationCallback();
+ mTerminationCallback = nullptr;
+ }
+
+ mParentEventTargetRef = nullptr;
+ mSelfRef = nullptr;
+ }
+
+ // May be called on any thread...
+ bool Start();
+
+ // Called on the parent thread.
+ bool Notify(WorkerStatus aStatus);
+
+ bool Cancel() { return Notify(Canceling); }
+
+ bool Close() MOZ_REQUIRES(mMutex);
+
+ // The passed principal must be the Worker principal in case of a
+ // ServiceWorker and the loading principal for any other type.
+ static void OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
+ nsIPrincipal* aPrincipal);
+
+ bool IsDebuggerRegistered() MOZ_NO_THREAD_SAFETY_ANALYSIS {
+ AssertIsOnMainThread();
+
+ // No need to lock here since this is only ever modified by the same thread.
+ return mDebuggerRegistered; // would give a thread-safety warning
+ }
+
+ bool ExtensionAPIAllowed() {
+ return (
+ StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup() &&
+ mExtensionAPIAllowed);
+ }
+
+ void SetIsDebuggerRegistered(bool aDebuggerRegistered) {
+ AssertIsOnMainThread();
+
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mDebuggerRegistered != aDebuggerRegistered);
+ mDebuggerRegistered = aDebuggerRegistered;
+
+ mCondVar.Notify();
+ }
+
+ void WaitForIsDebuggerRegistered(bool aDebuggerRegistered) {
+ AssertIsOnParentThread();
+
+ // Yield so that the main thread won't be blocked.
+ AutoYieldJSThreadExecution yield;
+
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ MutexAutoLock lock(mMutex);
+
+ while (mDebuggerRegistered != aDebuggerRegistered) {
+ mCondVar.Wait();
+ }
+ }
+
+ nsresult SetIsDebuggerReady(bool aReady);
+
+ WorkerDebugger* Debugger() const {
+ AssertIsOnMainThread();
+
+ MOZ_ASSERT(mDebugger);
+ return mDebugger;
+ }
+
+ const OriginTrials& Trials() const { return mLoadInfo.mTrials; }
+
+ void SetDebugger(WorkerDebugger* aDebugger) {
+ AssertIsOnMainThread();
+
+ MOZ_ASSERT(mDebugger != aDebugger);
+ mDebugger = aDebugger;
+ }
+
+ JS::UniqueChars AdoptDefaultLocale() {
+ MOZ_ASSERT(mDefaultLocale,
+ "the default locale must have been successfully set for anyone "
+ "to be trying to adopt it");
+ return std::move(mDefaultLocale);
+ }
+
+ /**
+ * Invoked by WorkerThreadPrimaryRunnable::Run if it already called
+ * SetWorkerPrivateInWorkerThread but has to bail out on initialization before
+ * calling DoRunLoop because PBackground failed to initialize or something
+ * like that. Note that there's currently no point earlier than this that
+ * failure can be reported.
+ *
+ * When this happens, the worker will need to be deleted, plus the call to
+ * SetWorkerPrivateInWorkerThread will have scheduled all the
+ * mPreStartRunnables which need to be cleaned up after, as well as any
+ * scheduled control runnables. We're somewhat punting on debugger runnables
+ * for now, which may leak, but the intent is to moot this whole scenario via
+ * shutdown blockers, so we don't want the extra complexity right now.
+ */
+ void RunLoopNeverRan();
+
+ MOZ_CAN_RUN_SCRIPT
+ void DoRunLoop(JSContext* aCx);
+
+ void UnrootGlobalScopes();
+
+ bool InterruptCallback(JSContext* aCx);
+
+ bool IsOnCurrentThread();
+
+ void CloseInternal();
+
+ bool FreezeInternal();
+
+ bool ThawInternal();
+
+ void PropagateStorageAccessPermissionGrantedInternal();
+
+ void TraverseTimeouts(nsCycleCollectionTraversalCallback& aCallback);
+
+ void UnlinkTimeouts();
+
+ bool AddChildWorker(WorkerPrivate& aChildWorker);
+
+ void RemoveChildWorker(WorkerPrivate& aChildWorker);
+
+ void PostMessageToParent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void PostMessageToParentMessagePort(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ MOZ_CAN_RUN_SCRIPT void EnterDebuggerEventLoop();
+
+ void LeaveDebuggerEventLoop();
+
+ void PostMessageToDebugger(const nsAString& aMessage);
+
+ void SetDebuggerImmediate(Function& aHandler, ErrorResult& aRv);
+
+ void ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
+ const nsAString& aMessage);
+
+ bool NotifyInternal(WorkerStatus aStatus);
+
+ void ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
+ JSErrorReport* aReport);
+
+ static void ReportErrorToConsole(const char* aMessage);
+
+ static void ReportErrorToConsole(const char* aMessage,
+ const nsTArray<nsString>& aParams);
+
+ int32_t SetTimeout(JSContext* aCx, TimeoutHandler* aHandler, int32_t aTimeout,
+ bool aIsInterval, Timeout::Reason aReason,
+ ErrorResult& aRv);
+
+ void ClearTimeout(int32_t aId, Timeout::Reason aReason);
+
+ MOZ_CAN_RUN_SCRIPT bool RunExpiredTimeouts(JSContext* aCx);
+
+ bool RescheduleTimeoutTimer(JSContext* aCx);
+
+ void UpdateContextOptionsInternal(JSContext* aCx,
+ const JS::ContextOptions& aContextOptions);
+
+ void UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
+
+ void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key,
+ Maybe<uint32_t> aValue);
+
+ enum WorkerRanOrNot { WorkerNeverRan = 0, WorkerRan };
+
+ void ScheduleDeletion(WorkerRanOrNot aRanOrNot);
+
+ bool CollectRuntimeStats(JS::RuntimeStats* aRtStats, bool aAnonymize);
+
+#ifdef JS_GC_ZEAL
+ void UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
+ uint32_t aFrequency);
+#endif
+
+ void SetLowMemoryStateInternal(JSContext* aCx, bool aState);
+
+ void GarbageCollectInternal(JSContext* aCx, bool aShrinking,
+ bool aCollectChildren);
+
+ void CycleCollectInternal(bool aCollectChildren);
+
+ void OfflineStatusChangeEventInternal(bool aIsOffline);
+
+ void MemoryPressureInternal();
+
+ typedef MozPromise<uint64_t, nsresult, true> JSMemoryUsagePromise;
+ RefPtr<JSMemoryUsagePromise> GetJSMemoryUsage();
+
+ void SetFetchHandlerWasAdded() {
+ MOZ_ASSERT(IsServiceWorker());
+ AssertIsOnWorkerThread();
+ mFetchHandlerWasAdded = true;
+ }
+
+ bool FetchHandlerWasAdded() const {
+ MOZ_ASSERT(IsServiceWorker());
+ AssertIsOnWorkerThread();
+ return mFetchHandlerWasAdded;
+ }
+
+ JSContext* GetJSContext() const MOZ_NO_THREAD_SAFETY_ANALYSIS {
+ // mJSContext is only modified on the worker thread, so workerthread code
+ // can safely read it without a lock
+ AssertIsOnWorkerThread();
+ return mJSContext;
+ }
+
+ WorkerGlobalScope* GlobalScope() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mScope;
+ }
+
+ WorkerDebuggerGlobalScope* DebuggerGlobalScope() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mDebuggerScope;
+ }
+
+ // Get the global associated with the current nested event loop. Will return
+ // null if we're not in a nested event loop or that nested event loop does not
+ // have an associated global.
+ nsIGlobalObject* GetCurrentEventLoopGlobal() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mCurrentEventLoopGlobal;
+ }
+
+ nsICSPEventListener* CSPEventListener() const;
+
+ void SetThread(WorkerThread* aThread);
+
+ void SetWorkerPrivateInWorkerThread(WorkerThread* aThread);
+
+ void ResetWorkerPrivateInWorkerThread();
+
+ bool IsOnWorkerThread() const;
+
+ void AssertIsOnWorkerThread() const
+#ifdef DEBUG
+ ;
+#else
+ {
+ }
+#endif
+
+ // This may block!
+ void BeginCTypesCall();
+
+ // This may block!
+ void EndCTypesCall();
+
+ void BeginCTypesCallback();
+
+ void EndCTypesCallback();
+
+ bool ConnectMessagePort(JSContext* aCx, UniqueMessagePortId& aIdentifier);
+
+ WorkerGlobalScope* GetOrCreateGlobalScope(JSContext* aCx);
+
+ WorkerDebuggerGlobalScope* CreateDebuggerGlobalScope(JSContext* aCx);
+
+ bool RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+ bool RegisterDebuggerBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+ bool OnLine() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mOnLine;
+ }
+
+ void StopSyncLoop(nsIEventTarget* aSyncLoopTarget, nsresult aResult);
+
+ bool MaybeStopSyncLoop(nsIEventTarget* aSyncLoopTarget, nsresult aResult);
+
+ void ShutdownModuleLoader();
+
+ void ClearPreStartRunnables();
+
+ void ClearDebuggerEventQueue();
+
+ void OnProcessNextEvent();
+
+ void AfterProcessNextEvent();
+
+ void AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
+#ifdef DEBUG
+ ;
+#else
+ {
+ }
+#endif
+
+ void AssertIsNotPotentiallyLastGCCCRunning() {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ auto data = mWorkerThreadAccessible.Access();
+ MOZ_DIAGNOSTIC_ASSERT(!data->mIsPotentiallyLastGCCCRunning);
+#endif
+ }
+
+ void SetWorkerScriptExecutedSuccessfully() {
+ AssertIsOnWorkerThread();
+ // Should only be called once!
+ MOZ_ASSERT(!mWorkerScriptExecutedSuccessfully);
+ mWorkerScriptExecutedSuccessfully = true;
+ }
+
+ // Only valid after CompileScriptRunnable has finished running!
+ bool WorkerScriptExecutedSuccessfully() const {
+ AssertIsOnWorkerThread();
+ return mWorkerScriptExecutedSuccessfully;
+ }
+
+ // Get the event target to use when dispatching to the main thread
+ // from this Worker thread. This may be the main thread itself or
+ // a ThrottledEventQueue to the main thread.
+ nsISerialEventTarget* MainThreadEventTargetForMessaging();
+
+ nsresult DispatchToMainThreadForMessaging(
+ nsIRunnable* aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsresult DispatchToMainThreadForMessaging(
+ already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsISerialEventTarget* MainThreadEventTarget();
+
+ nsresult DispatchToMainThread(nsIRunnable* aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsresult DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsresult DispatchDebuggeeToMainThread(
+ already_AddRefed<WorkerDebuggeeRunnable> aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ // Get an event target that will dispatch runnables as control runnables on
+ // the worker thread. Implement nsICancelableRunnable if you wish to take
+ // action on cancelation.
+ nsISerialEventTarget* ControlEventTarget();
+
+ // Get an event target that will attempt to dispatch a normal WorkerRunnable,
+ // but if that fails will then fall back to a control runnable.
+ nsISerialEventTarget* HybridEventTarget();
+
+ void DumpCrashInformation(nsACString& aString);
+
+ ClientType GetClientType() const;
+
+ bool EnsureCSPEventListener();
+
+ void EnsurePerformanceStorage();
+
+ bool GetExecutionGranted() const;
+ void SetExecutionGranted(bool aGranted);
+
+ void ScheduleTimeSliceExpiration(uint32_t aDelay);
+ void CancelTimeSliceExpiration();
+
+ JSExecutionManager* GetExecutionManager() const;
+ void SetExecutionManager(JSExecutionManager* aManager);
+
+ void ExecutionReady();
+
+ PerformanceStorage* GetPerformanceStorage();
+
+ bool IsAcceptingEvents() MOZ_EXCLUDES(mMutex) {
+ AssertIsOnParentThread();
+
+ MutexAutoLock lock(mMutex);
+ return mParentStatus < Canceling;
+ }
+
+ WorkerStatus ParentStatusProtected() {
+ AssertIsOnParentThread();
+ MutexAutoLock lock(mMutex);
+ return mParentStatus;
+ }
+
+ WorkerStatus ParentStatus() const MOZ_REQUIRES(mMutex) {
+ mMutex.AssertCurrentThreadOwns();
+ return mParentStatus;
+ }
+
+ Worker* ParentEventTargetRef() const {
+ MOZ_DIAGNOSTIC_ASSERT(mParentEventTargetRef);
+ return mParentEventTargetRef;
+ }
+
+ void SetParentEventTargetRef(Worker* aParentEventTargetRef) {
+ MOZ_DIAGNOSTIC_ASSERT(aParentEventTargetRef);
+ MOZ_DIAGNOSTIC_ASSERT(!mParentEventTargetRef);
+ mParentEventTargetRef = aParentEventTargetRef;
+ }
+
+ // Check whether this worker is a secure context. For use from the parent
+ // thread only; the canonical "is secure context" boolean is stored on the
+ // compartment of the worker global. The only reason we don't
+ // AssertIsOnParentThread() here is so we can assert that this value matches
+ // the one on the compartment, which has to be done from the worker thread.
+ bool IsSecureContext() const { return mIsSecureContext; }
+
+ // Check whether we're running in automation.
+ bool IsInAutomation() const { return mIsInAutomation; }
+
+ bool IsPrivilegedAddonGlobal() const { return mIsPrivilegedAddonGlobal; }
+
+ TimeStamp CreationTimeStamp() const { return mCreationTimeStamp; }
+
+ DOMHighResTimeStamp CreationTime() const { return mCreationTimeHighRes; }
+
+ DOMHighResTimeStamp TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const {
+ MOZ_ASSERT(!aTimeStamp.IsNull());
+ TimeDuration duration = aTimeStamp - mCreationTimeStamp;
+ return duration.ToMilliseconds();
+ }
+
+ LocationInfo& GetLocationInfo() { return mLocationInfo; }
+
+ void CopyJSSettings(workerinternals::JSSettings& aSettings) {
+ mozilla::MutexAutoLock lock(mMutex);
+ aSettings = mJSSettings;
+ }
+
+ void CopyJSRealmOptions(JS::RealmOptions& aOptions) {
+ mozilla::MutexAutoLock lock(mMutex);
+ aOptions = IsChromeWorker() ? mJSSettings.chromeRealmOptions
+ : mJSSettings.contentRealmOptions;
+ }
+
+ // The ability to be a chrome worker is orthogonal to the type of
+ // worker [Dedicated|Shared|Service].
+ bool IsChromeWorker() const { return mIsChromeWorker; }
+
+ // TODO: Invariants require that the parent worker out-live any child
+ // worker, so WorkerPrivate* should be safe in the moment of calling.
+ // We would like to have stronger type-system annotated/enforced handling.
+ WorkerPrivate* GetParent() const { return mParent; }
+
+ bool IsFrozen() const {
+ AssertIsOnParentThread();
+ return mParentFrozen;
+ }
+
+ bool IsParentWindowPaused() const {
+ AssertIsOnParentThread();
+ return mParentWindowPaused;
+ }
+
+ // When we debug a worker, we want to disconnect the window and the worker
+ // communication. This happens calling this method.
+ // Note: this method doesn't suspend the worker! Use Freeze/Thaw instead.
+ void ParentWindowPaused();
+
+ void ParentWindowResumed();
+
+ const nsString& ScriptURL() const { return mScriptURL; }
+
+ const nsString& WorkerName() const { return mWorkerName; }
+ RequestCredentials WorkerCredentials() const { return mCredentialsMode; }
+ enum WorkerType WorkerType() const { return mWorkerType; }
+
+ WorkerKind Kind() const { return mWorkerKind; }
+
+ bool IsDedicatedWorker() const { return mWorkerKind == WorkerKindDedicated; }
+
+ bool IsSharedWorker() const { return mWorkerKind == WorkerKindShared; }
+
+ bool IsServiceWorker() const { return mWorkerKind == WorkerKindService; }
+
+ nsContentPolicyType ContentPolicyType() const {
+ return ContentPolicyType(mWorkerKind);
+ }
+
+ static nsContentPolicyType ContentPolicyType(WorkerKind aWorkerKind) {
+ switch (aWorkerKind) {
+ case WorkerKindDedicated:
+ return nsIContentPolicy::TYPE_INTERNAL_WORKER;
+ case WorkerKindShared:
+ return nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+ case WorkerKindService:
+ return nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid worker type");
+ return nsIContentPolicy::TYPE_INVALID;
+ }
+ }
+
+ nsIScriptContext* GetScriptContext() const {
+ AssertIsOnMainThread();
+ return mLoadInfo.mScriptContext;
+ }
+
+ const nsCString& Domain() const { return mLoadInfo.mDomain; }
+
+ bool IsFromWindow() const { return mLoadInfo.mFromWindow; }
+
+ nsLoadFlags GetLoadFlags() const { return mLoadInfo.mLoadFlags; }
+
+ uint64_t WindowID() const { return mLoadInfo.mWindowID; }
+
+ uint64_t AssociatedBrowsingContextID() const {
+ return mLoadInfo.mAssociatedBrowsingContextID;
+ }
+
+ uint64_t ServiceWorkerID() const { return GetServiceWorkerDescriptor().Id(); }
+
+ const nsCString& ServiceWorkerScope() const {
+ return GetServiceWorkerDescriptor().Scope();
+ }
+
+ // This value should never change after the script load completes. Before
+ // then, it may only be called on the main thread.
+ nsIURI* GetBaseURI() const { return mLoadInfo.mBaseURI; }
+
+ void SetBaseURI(nsIURI* aBaseURI);
+
+ nsIURI* GetResolvedScriptURI() const { return mLoadInfo.mResolvedScriptURI; }
+
+ const nsString& ServiceWorkerCacheName() const {
+ MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+ AssertIsOnMainThread();
+ return mLoadInfo.mServiceWorkerCacheName;
+ }
+
+ const ServiceWorkerDescriptor& GetServiceWorkerDescriptor() const {
+ MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+ MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
+ return mLoadInfo.mServiceWorkerDescriptor.ref();
+ }
+
+ const ServiceWorkerRegistrationDescriptor&
+ GetServiceWorkerRegistrationDescriptor() const {
+ MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+ MOZ_DIAGNOSTIC_ASSERT(
+ mLoadInfo.mServiceWorkerRegistrationDescriptor.isSome());
+ return mLoadInfo.mServiceWorkerRegistrationDescriptor.ref();
+ }
+
+ void UpdateServiceWorkerState(ServiceWorkerState aState) {
+ MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+ MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
+ return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState);
+ }
+
+ const Maybe<ServiceWorkerDescriptor>& GetParentController() const {
+ return mLoadInfo.mParentController;
+ }
+
+ const ChannelInfo& GetChannelInfo() const { return mLoadInfo.mChannelInfo; }
+
+ void SetChannelInfo(const ChannelInfo& aChannelInfo) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
+ MOZ_ASSERT(aChannelInfo.IsInitialized());
+ mLoadInfo.mChannelInfo = aChannelInfo;
+ }
+
+ void InitChannelInfo(nsIChannel* aChannel) {
+ mLoadInfo.mChannelInfo.InitFromChannel(aChannel);
+ }
+
+ void InitChannelInfo(const ChannelInfo& aChannelInfo) {
+ mLoadInfo.mChannelInfo = aChannelInfo;
+ }
+
+ nsIPrincipal* GetPrincipal() const { return mLoadInfo.mPrincipal; }
+
+ nsIPrincipal* GetLoadingPrincipal() const {
+ return mLoadInfo.mLoadingPrincipal;
+ }
+
+ nsIPrincipal* GetPartitionedPrincipal() const {
+ return mLoadInfo.mPartitionedPrincipal;
+ }
+
+ nsIPrincipal* GetEffectiveStoragePrincipal() const;
+
+ nsILoadGroup* GetLoadGroup() const {
+ AssertIsOnMainThread();
+ return mLoadInfo.mLoadGroup;
+ }
+
+ bool UsesSystemPrincipal() const {
+ return GetPrincipal()->IsSystemPrincipal();
+ }
+ bool UsesAddonOrExpandedAddonPrincipal() const {
+ return GetPrincipal()->GetIsAddonOrExpandedAddonPrincipal();
+ }
+
+ const mozilla::ipc::PrincipalInfo& GetPrincipalInfo() const {
+ return *mLoadInfo.mPrincipalInfo;
+ }
+
+ const mozilla::ipc::PrincipalInfo& GetPartitionedPrincipalInfo() const {
+ return *mLoadInfo.mPartitionedPrincipalInfo;
+ }
+
+ const mozilla::ipc::PrincipalInfo& GetEffectiveStoragePrincipalInfo() const;
+
+ already_AddRefed<nsIChannel> ForgetWorkerChannel() {
+ AssertIsOnMainThread();
+ return mLoadInfo.mChannel.forget();
+ }
+
+ nsPIDOMWindowInner* GetWindow() const {
+ AssertIsOnMainThread();
+ return mLoadInfo.mWindow;
+ }
+
+ nsPIDOMWindowInner* GetAncestorWindow() const;
+
+ void EvictFromBFCache();
+
+ nsIContentSecurityPolicy* GetCsp() const {
+ AssertIsOnMainThread();
+ return mLoadInfo.mCSP;
+ }
+
+ void SetCsp(nsIContentSecurityPolicy* aCSP);
+
+ nsresult SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
+ const nsACString& aCSPReportOnlyHeaderValue);
+
+ void StoreCSPOnClient();
+
+ const mozilla::ipc::CSPInfo& GetCSPInfo() const {
+ return *mLoadInfo.mCSPInfo;
+ }
+
+ void UpdateReferrerInfoFromHeader(
+ const nsACString& aReferrerPolicyHeaderValue);
+
+ nsIReferrerInfo* GetReferrerInfo() const { return mLoadInfo.mReferrerInfo; }
+
+ ReferrerPolicy GetReferrerPolicy() const {
+ return mLoadInfo.mReferrerInfo->ReferrerPolicy();
+ }
+
+ void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+ mLoadInfo.mReferrerInfo = aReferrerInfo;
+ }
+
+ bool IsEvalAllowed() const { return mLoadInfo.mEvalAllowed; }
+
+ void SetEvalAllowed(bool aAllowed) { mLoadInfo.mEvalAllowed = aAllowed; }
+
+ bool GetReportEvalCSPViolations() const {
+ return mLoadInfo.mReportEvalCSPViolations;
+ }
+
+ void SetReportEvalCSPViolations(bool aReport) {
+ mLoadInfo.mReportEvalCSPViolations = aReport;
+ }
+
+ bool IsWasmEvalAllowed() const { return mLoadInfo.mWasmEvalAllowed; }
+
+ void SetWasmEvalAllowed(bool aAllowed) {
+ mLoadInfo.mWasmEvalAllowed = aAllowed;
+ }
+
+ bool GetReportWasmEvalCSPViolations() const {
+ return mLoadInfo.mReportWasmEvalCSPViolations;
+ }
+
+ void SetReportWasmEvalCSPViolations(bool aReport) {
+ mLoadInfo.mReportWasmEvalCSPViolations = aReport;
+ }
+
+ bool XHRParamsAllowed() const { return mLoadInfo.mXHRParamsAllowed; }
+
+ void SetXHRParamsAllowed(bool aAllowed) {
+ mLoadInfo.mXHRParamsAllowed = aAllowed;
+ }
+
+ mozilla::StorageAccess StorageAccess() const {
+ AssertIsOnWorkerThread();
+ if (mLoadInfo.mUsingStorageAccess) {
+ return mozilla::StorageAccess::eAllow;
+ }
+
+ return mLoadInfo.mStorageAccess;
+ }
+
+ bool UseRegularPrincipal() const {
+ AssertIsOnWorkerThread();
+ return mLoadInfo.mUseRegularPrincipal;
+ }
+
+ bool UsingStorageAccess() const {
+ AssertIsOnWorkerThread();
+ return mLoadInfo.mUsingStorageAccess;
+ }
+
+ nsICookieJarSettings* CookieJarSettings() const {
+ // Any thread.
+ MOZ_ASSERT(mLoadInfo.mCookieJarSettings);
+ return mLoadInfo.mCookieJarSettings;
+ }
+
+ const net::CookieJarSettingsArgs& CookieJarSettingsArgs() const {
+ MOZ_ASSERT(mLoadInfo.mCookieJarSettings);
+ return mLoadInfo.mCookieJarSettingsArgs;
+ }
+
+ const OriginAttributes& GetOriginAttributes() const {
+ return mLoadInfo.mOriginAttributes;
+ }
+
+ // Determine if the SW testing per-window flag is set by devtools
+ bool ServiceWorkersTestingInWindow() const {
+ return mLoadInfo.mServiceWorkersTestingInWindow;
+ }
+
+ // Determine if the worker was created under a third-party context.
+ bool IsThirdPartyContextToTopWindow() const {
+ return mLoadInfo.mIsThirdPartyContextToTopWindow;
+ }
+
+ bool IsWatchedByDevTools() const { return mLoadInfo.mWatchedByDevTools; }
+
+ bool ShouldResistFingerprinting(RFPTarget aTarget) const;
+
+ const Maybe<RFPTarget>& GetOverriddenFingerprintingSettings() const {
+ return mLoadInfo.mOverriddenFingerprintingSettings;
+ }
+
+ RemoteWorkerChild* GetRemoteWorkerController();
+
+ void SetRemoteWorkerController(RemoteWorkerChild* aController);
+
+ RefPtr<GenericPromise> SetServiceWorkerSkipWaitingFlag();
+
+ // We can assume that an nsPIDOMWindow will be available for Freeze, Thaw
+ // as these are only used for globals going in and out of the bfcache.
+ bool Freeze(const nsPIDOMWindowInner* aWindow);
+
+ bool Thaw(const nsPIDOMWindowInner* aWindow);
+
+ void PropagateStorageAccessPermissionGranted();
+
+ void EnableDebugger();
+
+ void DisableDebugger();
+
+ already_AddRefed<WorkerRunnable> MaybeWrapAsWorkerRunnable(
+ already_AddRefed<nsIRunnable> aRunnable);
+
+ bool ProxyReleaseMainThreadObjects();
+
+ void SetLowMemoryState(bool aState);
+
+ void GarbageCollect(bool aShrinking);
+
+ void CycleCollect();
+
+ nsresult SetPrincipalsAndCSPOnMainThread(nsIPrincipal* aPrincipal,
+ nsIPrincipal* aPartitionedPrincipal,
+ nsILoadGroup* aLoadGroup,
+ nsIContentSecurityPolicy* aCsp);
+
+ nsresult SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel);
+
+ bool FinalChannelPrincipalIsValid(nsIChannel* aChannel);
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool PrincipalURIMatchesScriptURL();
+#endif
+
+ void UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
+
+ void WorkerScriptLoaded();
+
+ Document* GetDocument() const;
+
+ void MemoryPressure();
+
+ void UpdateContextOptions(const JS::ContextOptions& aContextOptions);
+
+ void UpdateLanguages(const nsTArray<nsString>& aLanguages);
+
+ void UpdateJSWorkerMemoryParameter(JSGCParamKey key, Maybe<uint32_t> value);
+
+#ifdef JS_GC_ZEAL
+ void UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency);
+#endif
+
+ void OfflineStatusChangeEvent(bool aIsOffline);
+
+ nsresult Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
+ nsIEventTarget* aSyncLoopTarget = nullptr);
+
+ nsresult DispatchControlRunnable(
+ already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable);
+
+ nsresult DispatchDebuggerRunnable(
+ already_AddRefed<WorkerRunnable> aDebuggerRunnable);
+
+ bool IsOnParentThread() const;
+
+#ifdef DEBUG
+ void AssertIsOnParentThread() const;
+
+ void AssertInnerWindowIsCorrect() const;
+#else
+ void AssertIsOnParentThread() const {}
+
+ void AssertInnerWindowIsCorrect() const {}
+#endif
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool PrincipalIsValid() const;
+#endif
+
+ void StartCancelingTimer();
+
+ const nsAString& Id();
+
+ const nsID& AgentClusterId() const { return mAgentClusterId; }
+
+ bool IsSharedMemoryAllowed() const;
+
+ // https://whatpr.org/html/4734/structured-data.html#cross-origin-isolated
+ bool CrossOriginIsolated() const;
+
+ void SetUseCounter(UseCounterWorker aUseCounter) {
+ MOZ_ASSERT(!mReportedUseCounters);
+ MOZ_ASSERT(aUseCounter > UseCounterWorker::Unknown);
+ AssertIsOnWorkerThread();
+ mUseCounters[static_cast<size_t>(aUseCounter)] = true;
+ }
+
+ /**
+ * COEP Methods
+ *
+ * If browser.tabs.remote.useCrossOriginEmbedderPolicy=false, these methods
+ * will, depending on the return type, return a value that will avoid
+ * assertion failures or a value that won't block loads.
+ */
+ nsILoadInfo::CrossOriginEmbedderPolicy GetEmbedderPolicy() const;
+
+ // Fails if a policy has already been set or if `aPolicy` violates the owner's
+ // policy, if an owner exists.
+ mozilla::Result<Ok, nsresult> SetEmbedderPolicy(
+ nsILoadInfo::CrossOriginEmbedderPolicy aPolicy);
+
+ // `aRequest` is the request loading the worker and must be QI-able to
+ // `nsIChannel*`. It's used to verify that the worker can indeed inherit its
+ // owner's COEP (when an owner exists).
+ //
+ // TODO: remove `aRequest`; currently, it's required because instances may not
+ // always know its final, resolved script URL or have access internally to
+ // `aRequest`.
+ void InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest);
+
+ // Requires a policy to already have been set.
+ bool MatchEmbedderPolicy(
+ nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const;
+
+ nsILoadInfo::CrossOriginEmbedderPolicy GetOwnerEmbedderPolicy() const;
+
+ void SetCCCollectedAnything(bool collectedAnything);
+ bool isLastCCCollectedAnything();
+
+ uint32_t GetCurrentTimerNestingLevel() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mCurrentTimerNestingLevel;
+ }
+
+ void IncreaseTopLevelWorkerFinishedRunnableCount() {
+ ++mTopLevelWorkerFinishedRunnableCount;
+ }
+ void DecreaseTopLevelWorkerFinishedRunnableCount() {
+ --mTopLevelWorkerFinishedRunnableCount;
+ }
+ void IncreaseWorkerFinishedRunnableCount() { ++mWorkerFinishedRunnableCount; }
+ void DecreaseWorkerFinishedRunnableCount() { --mWorkerFinishedRunnableCount; }
+
+ void RunShutdownTasks();
+
+ bool CancelBeforeWorkerScopeConstructed() const {
+ auto data = mWorkerThreadAccessible.Access();
+ return data->mCancelBeforeWorkerScopeConstructed;
+ }
+
+ enum class CCFlag : uint8_t {
+ EligibleForWorkerRef,
+ IneligibleForWorkerRef,
+ EligibleForChildWorker,
+ IneligibleForChildWorker,
+ EligibleForTimeout,
+ IneligibleForTimeout,
+ CheckBackgroundActors,
+ };
+
+ // When create/release a StrongWorkerRef, child worker, and timeout, this
+ // method is used to setup if mParentEventTargetRef can get into
+ // cycle-collection.
+ // When this method is called, it will also checks if any background actor
+ // should block the mParentEventTargetRef cycle-collection when there is no
+ // StrongWorkerRef/ChildWorker/Timeout.
+ // Worker thread only.
+ void UpdateCCFlag(const CCFlag);
+
+ // This is used in WorkerPrivate::Traverse() to checking if
+ // mParentEventTargetRef should get into cycle-collection.
+ // Parent thread only method.
+ bool IsEligibleForCC();
+
+ // A method which adjusts the count of background actors which should not
+ // block WorkerPrivate::mParentEventTargetRef cycle-collection.
+ // Worker thread only.
+ void AdjustNonblockingCCBackgroundActorCount(int32_t aCount);
+
+ private:
+ WorkerPrivate(
+ WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
+ WorkerKind aWorkerKind, RequestCredentials aRequestCredentials,
+ enum WorkerType aWorkerType, const nsAString& aWorkerName,
+ const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
+ nsString&& aId, const nsID& aAgentClusterId,
+ const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy,
+ CancellationCallback&& aCancellationCallback,
+ TerminationCallback&& aTerminationCallback);
+
+ ~WorkerPrivate();
+
+ struct AgentClusterIdAndCoop {
+ nsID mId;
+ nsILoadInfo::CrossOriginOpenerPolicy mCoop;
+ };
+
+ static AgentClusterIdAndCoop ComputeAgentClusterIdAndCoop(
+ WorkerPrivate* aParent, WorkerKind aWorkerKind,
+ WorkerLoadInfo* aLoadInfo);
+
+ bool MayContinueRunning() {
+ AssertIsOnWorkerThread();
+
+ WorkerStatus status;
+ {
+ MutexAutoLock lock(mMutex);
+ status = mStatus;
+ }
+
+ if (status < Canceling) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void CancelAllTimeouts();
+
+ enum class ProcessAllControlRunnablesResult {
+ // We did not process anything.
+ Nothing,
+ // We did process something, states may have changed, but we can keep
+ // executing script.
+ MayContinue,
+ // We did process something, and should not continue executing script.
+ Abort
+ };
+
+ ProcessAllControlRunnablesResult ProcessAllControlRunnables() {
+ MutexAutoLock lock(mMutex);
+ return ProcessAllControlRunnablesLocked();
+ }
+
+ ProcessAllControlRunnablesResult ProcessAllControlRunnablesLocked()
+ MOZ_REQUIRES(mMutex);
+
+ void EnableMemoryReporter();
+
+ void DisableMemoryReporter();
+
+ void WaitForWorkerEvents() MOZ_REQUIRES(mMutex);
+
+ // If the worker shutdown status is equal or greater then aFailStatus, this
+ // operation will fail and nullptr will be returned. See WorkerStatus.h for
+ // more information about the correct value to use.
+ already_AddRefed<nsISerialEventTarget> CreateNewSyncLoop(
+ WorkerStatus aFailStatus);
+
+ nsresult RunCurrentSyncLoop();
+
+ nsresult DestroySyncLoop(uint32_t aLoopIndex);
+
+ void InitializeGCTimers();
+
+ enum GCTimerMode { PeriodicTimer = 0, IdleTimer, NoTimer };
+
+ void SetGCTimerMode(GCTimerMode aMode);
+
+ public:
+ void CancelGCTimers() { SetGCTimerMode(NoTimer); }
+
+ private:
+ void ShutdownGCTimers();
+
+ friend class WorkerRef;
+
+ bool AddWorkerRef(WorkerRef* aWorkerRefer, WorkerStatus aFailStatus);
+
+ void RemoveWorkerRef(WorkerRef* aWorkerRef);
+
+ void NotifyWorkerRefs(WorkerStatus aStatus);
+
+ bool HasActiveWorkerRefs() {
+ auto data = mWorkerThreadAccessible.Access();
+ return !(data->mChildWorkers.IsEmpty() && data->mTimeouts.IsEmpty() &&
+ data->mWorkerRefs.IsEmpty());
+ }
+
+ friend class WorkerEventTarget;
+
+ nsresult RegisterShutdownTask(nsITargetShutdownTask* aTask);
+
+ nsresult UnregisterShutdownTask(nsITargetShutdownTask* aTask);
+
+ // Internal logic to dispatch a runnable. This is separate from Dispatch()
+ // to allow runnables to be atomically dispatched in bulk.
+ nsresult DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
+ nsIEventTarget* aSyncLoopTarget,
+ const MutexAutoLock& aProofOfLock)
+ MOZ_REQUIRES(mMutex);
+
+ // This method dispatches a simple runnable that starts the shutdown procedure
+ // after a self.close(). This method is called after a ClearMainEventQueue()
+ // to be sure that the canceling runnable is the only one in the queue. We
+ // need this async operation to be sure that all the current JS code is
+ // executed.
+ void DispatchCancelingRunnable();
+
+ bool GetUseCounter(UseCounterWorker aUseCounter) {
+ MOZ_ASSERT(aUseCounter > UseCounterWorker::Unknown);
+ AssertIsOnWorkerThread();
+ return mUseCounters[static_cast<size_t>(aUseCounter)];
+ }
+
+ void ReportUseCounters();
+
+ UniquePtr<ClientSource> CreateClientSource();
+
+ // This method is called when corresponding script loader processes the COEP
+ // header for the worker.
+ // This method should be called only once in the main thread.
+ // After this method is called the COEP value owner(window/parent worker) is
+ // cached in mOwnerEmbedderPolicy such that it can be accessed in other
+ // threads, i.e. WorkerThread.
+ void EnsureOwnerEmbedderPolicy();
+
+ class EventTarget;
+ friend class EventTarget;
+ friend class AutoSyncLoopHolder;
+
+ struct TimeoutInfo;
+
+ class MemoryReporter;
+ friend class MemoryReporter;
+
+ friend class mozilla::dom::WorkerThread;
+
+ SharedMutex mMutex;
+ mozilla::CondVar mCondVar MOZ_GUARDED_BY(mMutex);
+
+ // We cannot make this CheckedUnsafePtr<WorkerPrivate> as this would violate
+ // our static assert
+ MOZ_NON_OWNING_REF WorkerPrivate* const mParent;
+
+ const nsString mScriptURL;
+
+ // This is the worker name for shared workers and dedicated workers.
+ const nsString mWorkerName;
+ const RequestCredentials mCredentialsMode;
+ enum WorkerType mWorkerType;
+
+ const WorkerKind mWorkerKind;
+
+ // The worker is owned by its thread, which is represented here. This is set
+ // in Constructor() and emptied by WorkerFinishedRunnable, and conditionally
+ // traversed by the cycle collector if no other things preventing shutdown.
+ //
+ // There are 4 ways a worker can be terminated:
+ // 1. GC/CC - When the worker is in idle state (busycount == 0), it allows to
+ // traverse the 'hidden' mParentEventTargetRef pointer. This is the exposed
+ // Worker webidl object. Doing this, CC will be able to detect a cycle and
+ // Unlink is called. In Unlink, Worker calls Cancel().
+ // 2. Worker::Cancel() is called - the shutdown procedure starts immediately.
+ // 3. WorkerScope::Close() is called - Similar to point 2.
+ // 4. xpcom-shutdown notification - We call Kill().
+ RefPtr<Worker> mParentEventTargetRef;
+ RefPtr<WorkerPrivate> mSelfRef;
+
+ CancellationCallback mCancellationCallback;
+
+ // The termination callback is passed into the constructor on the parent
+ // thread and invoked by `ClearSelfAndParentEventTargetRef` just before it
+ // drops its self-ref.
+ TerminationCallback mTerminationCallback;
+
+ // The lifetime of these objects within LoadInfo is managed explicitly;
+ // they do not need to be cycle collected.
+ WorkerLoadInfo mLoadInfo;
+ LocationInfo mLocationInfo;
+
+ // Protected by mMutex.
+ workerinternals::JSSettings mJSSettings MOZ_GUARDED_BY(mMutex);
+
+ WorkerDebugger* mDebugger;
+
+ workerinternals::Queue<WorkerControlRunnable*, 4> mControlQueue;
+ workerinternals::Queue<WorkerRunnable*, 4> mDebuggerQueue;
+
+ // Touched on multiple threads, protected with mMutex. Only modified on the
+ // worker thread
+ JSContext* mJSContext MOZ_GUARDED_BY(mMutex);
+ // mThread is only modified on the Worker thread, before calling DoRunLoop
+ RefPtr<WorkerThread> mThread MOZ_GUARDED_BY(mMutex);
+ // mPRThread is only modified on another thread in ScheduleWorker(), and is
+ // constant for the duration of DoRunLoop. Static mutex analysis doesn't help
+ // here
+ PRThread* mPRThread;
+
+ // Accessed from main thread
+ RefPtr<ThrottledEventQueue> mMainThreadEventTargetForMessaging;
+ RefPtr<ThrottledEventQueue> mMainThreadEventTarget;
+
+ // Accessed from worker thread and destructing thread
+ RefPtr<WorkerEventTarget> mWorkerControlEventTarget;
+ RefPtr<WorkerEventTarget> mWorkerHybridEventTarget;
+
+ // A pauseable queue for WorkerDebuggeeRunnables directed at the main thread.
+ // See WorkerDebuggeeRunnable for details.
+ RefPtr<ThrottledEventQueue> mMainThreadDebuggeeEventTarget;
+
+ struct SyncLoopInfo {
+ explicit SyncLoopInfo(EventTarget* aEventTarget);
+
+ RefPtr<EventTarget> mEventTarget;
+ nsresult mResult;
+ bool mCompleted;
+#ifdef DEBUG
+ bool mHasRun;
+#endif
+ };
+
+ // This is only modified on the worker thread, but in DEBUG builds
+ // AssertValidSyncLoop function iterates it on other threads. Therefore
+ // modifications are done with mMutex held *only* in DEBUG builds.
+ nsTArray<UniquePtr<SyncLoopInfo>> mSyncLoopStack;
+
+ nsCOMPtr<nsITimer> mCancelingTimer;
+
+ // fired on the main thread if the worker script fails to load
+ nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
+
+ RefPtr<PerformanceStorage> mPerformanceStorage;
+
+ RefPtr<WorkerCSPEventListener> mCSPEventListener;
+
+ // Protected by mMutex.
+ nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables MOZ_GUARDED_BY(mMutex);
+
+ // Only touched on the parent thread. Used for both SharedWorker and
+ // ServiceWorker RemoteWorkers.
+ RefPtr<RemoteWorkerChild> mRemoteWorkerController;
+
+ JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
+ TimeStamp mKillTime;
+ WorkerStatus mParentStatus MOZ_GUARDED_BY(mMutex);
+ WorkerStatus mStatus MOZ_GUARDED_BY(mMutex);
+
+ TimeStamp mCreationTimeStamp;
+ DOMHighResTimeStamp mCreationTimeHighRes;
+
+ // Flags for use counters used directly by this worker.
+ static_assert(sizeof(UseCounterWorker) <= sizeof(size_t),
+ "UseCounterWorker is too big");
+ static_assert(UseCounterWorker::Count >= static_cast<UseCounterWorker>(0),
+ "Should be non-negative value and safe to cast to unsigned");
+ std::bitset<static_cast<size_t>(UseCounterWorker::Count)> mUseCounters;
+ bool mReportedUseCounters;
+
+ // This is created while creating the WorkerPrivate, so it's safe to be
+ // touched on any thread.
+ const nsID mAgentClusterId;
+
+ // Things touched on worker thread only.
+ struct WorkerThreadAccessible {
+ explicit WorkerThreadAccessible(WorkerPrivate* aParent);
+
+ RefPtr<WorkerGlobalScope> mScope;
+ RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
+ // We cannot make this CheckedUnsafePtr<WorkerPrivate> as this would violate
+ // our static assert
+ nsTArray<WorkerPrivate*> mChildWorkers;
+ nsTObserverArray<WorkerRef*> mWorkerRefs;
+ nsTArray<UniquePtr<TimeoutInfo>> mTimeouts;
+
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsITimerCallback> mTimerRunnable;
+
+ nsCOMPtr<nsITimer> mPeriodicGCTimer;
+ nsCOMPtr<nsITimer> mIdleGCTimer;
+
+ RefPtr<MemoryReporter> mMemoryReporter;
+
+ // While running a nested event loop, whether a sync loop or a debugger
+ // event loop we want to keep track of which global is running it, if any,
+ // so runnables that run off that event loop can get at that information. In
+ // practice this only matters for various worker debugger runnables running
+ // against sandboxes, because all other runnables know which globals they
+ // belong to already. We could also address this by threading the relevant
+ // global through the chains of runnables involved, but we'd need to thread
+ // it through some runnables that run on the main thread, and that would
+ // require some care to make sure things get released on the correct thread,
+ // which we'd rather avoid. This member is only accessed on the worker
+ // thread.
+ nsCOMPtr<nsIGlobalObject> mCurrentEventLoopGlobal;
+
+ // Timer that triggers an interrupt on expiration of the current time slice
+ nsCOMPtr<nsITimer> mTSTimer;
+
+ // Execution manager used to regulate execution for this worker.
+ RefPtr<JSExecutionManager> mExecutionManager;
+
+ // Used to relinguish clearance for CTypes Callbacks.
+ nsTArray<AutoYieldJSThreadExecution> mYieldJSThreadExecution;
+
+ uint32_t mNumWorkerRefsPreventingShutdownStart;
+ uint32_t mDebuggerEventLoopLevel;
+
+ // This is the count of background actors that binding with IPCWorkerRefs.
+ // This count would be used in WorkerPrivate::UpdateCCFlag for checking if
+ // CC should be blocked by background actors.
+ uint32_t mNonblockingCCBackgroundActorCount;
+
+ uint32_t mErrorHandlerRecursionCount;
+ int32_t mNextTimeoutId;
+
+ // Tracks the current setTimeout/setInterval nesting level.
+ // When there isn't a TimeoutHandler on the stack, this will be 0.
+ // Whenever setTimeout/setInterval are called, a new TimeoutInfo will be
+ // created with a nesting level one more than the current nesting level,
+ // saturating at the kClampTimeoutNestingLevel.
+ //
+ // When RunExpiredTimeouts is run, it sets this value to the
+ // TimeoutInfo::mNestingLevel for the duration of
+ // the WorkerScriptTimeoutHandler::Call which will explicitly trigger a
+ // microtask checkpoint so that any immediately-resolved promises will
+ // still see the nesting level.
+ uint32_t mCurrentTimerNestingLevel;
+
+ bool mFrozen;
+ bool mTimerRunning;
+ bool mRunningExpiredTimeouts;
+ bool mPeriodicGCTimerRunning;
+ bool mIdleGCTimerRunning;
+ bool mOnLine;
+ bool mJSThreadExecutionGranted;
+ bool mCCCollectedAnything;
+ FlippedOnce<false> mDeletionScheduled;
+ FlippedOnce<false> mCancelBeforeWorkerScopeConstructed;
+ FlippedOnce<false> mPerformedShutdownAfterLastContentTaskExecuted;
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool mIsPotentiallyLastGCCCRunning = false;
+#endif
+ };
+ ThreadBound<WorkerThreadAccessible> mWorkerThreadAccessible;
+
+ class MOZ_RAII AutoPushEventLoopGlobal {
+ public:
+ AutoPushEventLoopGlobal(WorkerPrivate* aWorkerPrivate, JSContext* aCx);
+ ~AutoPushEventLoopGlobal();
+
+ private:
+ // We cannot make this CheckedUnsafePtr<WorkerPrivate> as this would violate
+ // our static assert
+ MOZ_NON_OWNING_REF WorkerPrivate* mWorkerPrivate;
+ nsCOMPtr<nsIGlobalObject> mOldEventLoopGlobal;
+ };
+ friend class AutoPushEventLoopGlobal;
+
+ uint32_t mPostSyncLoopOperations;
+
+ // List of operations to do at the end of the last sync event loop.
+ enum {
+ eDispatchCancelingRunnable = 0x02,
+ };
+
+ bool mParentWindowPaused;
+
+ bool mWorkerScriptExecutedSuccessfully;
+ bool mFetchHandlerWasAdded;
+ bool mMainThreadObjectsForgotten;
+ bool mIsChromeWorker;
+ bool mParentFrozen;
+
+ // mIsSecureContext is set once in our constructor; after that it can be read
+ // from various threads.
+ //
+ // It's a bit unfortunate that we have to have an out-of-band boolean for
+ // this, but we need access to this state from the parent thread, and we can't
+ // use our global object's secure state there.
+ const bool mIsSecureContext;
+
+ bool mDebuggerRegistered MOZ_GUARDED_BY(mMutex);
+
+ // During registration, this worker may be marked as not being ready to
+ // execute debuggee runnables or content.
+ //
+ // Protected by mMutex.
+ bool mDebuggerReady;
+ nsTArray<RefPtr<WorkerRunnable>> mDelayedDebuggeeRunnables;
+
+ // Whether this worker should have access to the WebExtension API bindings
+ // (currently only the Extension Background ServiceWorker declared in the
+ // extension manifest is allowed to access any WebExtension API bindings).
+ // This default to false, and it is eventually set to true by
+ // RemoteWorkerChild::ExecWorkerOnMainThread if the needed conditions
+ // are met.
+ bool mExtensionAPIAllowed;
+
+ // mIsInAutomation is true when we're running in test automation.
+ // We expose some extra testing functions in that case.
+ bool mIsInAutomation;
+
+ nsString mId;
+
+ // This is used to check if it's allowed to share the memory across the agent
+ // cluster.
+ const nsILoadInfo::CrossOriginOpenerPolicy mAgentClusterOpenerPolicy;
+
+ // Member variable of this class rather than the worker global scope because
+ // it's received on the main thread, but the global scope is thread-bound
+ // to the worker thread, so storing the value in the global scope would
+ // involve sacrificing the thread-bound-ness or using a WorkerRunnable, and
+ // there isn't a strong reason to store it on the global scope other than
+ // better consistency with the COEP spec.
+ Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> mEmbedderPolicy;
+ Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> mOwnerEmbedderPolicy;
+
+ /* Privileged add-on flag extracted from the AddonPolicy on the nsIPrincipal
+ * on the main thread when constructing a top-level worker. The flag is
+ * propagated to nested workers. The flag is only allowed to take effect in
+ * extension processes and is forbidden in content scripts in content
+ * processes. The flag may be read on either the parent/owner thread as well
+ * as on the worker thread itself. When bug 1443925 is fixed allowing
+ * nsIPrincipal to be used OMT, it may be possible to remove this flag. */
+ bool mIsPrivilegedAddonGlobal;
+
+ Atomic<uint32_t> mTopLevelWorkerFinishedRunnableCount;
+ Atomic<uint32_t> mWorkerFinishedRunnableCount;
+
+ nsTArray<nsCOMPtr<nsITargetShutdownTask>> mShutdownTasks
+ MOZ_GUARDED_BY(mMutex);
+ bool mShutdownTasksRun MOZ_GUARDED_BY(mMutex) = false;
+
+ bool mCCFlagSaysEligible MOZ_GUARDED_BY(mMutex){true};
+
+ // The flag indicates if the worke is idle for events in the main event loop.
+ bool mWorkerLoopIsIdle MOZ_GUARDED_BY(mMutex){false};
+};
+
+class AutoSyncLoopHolder {
+ CheckedUnsafePtr<WorkerPrivate> mWorkerPrivate;
+ nsCOMPtr<nsISerialEventTarget> mTarget;
+ uint32_t mIndex;
+
+ public:
+ // See CreateNewSyncLoop() for more information about the correct value to use
+ // for aFailStatus.
+ AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate, WorkerStatus aFailStatus)
+ : mWorkerPrivate(aWorkerPrivate),
+ mTarget(aWorkerPrivate->CreateNewSyncLoop(aFailStatus)),
+ mIndex(aWorkerPrivate->mSyncLoopStack.Length() - 1) {
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ ~AutoSyncLoopHolder() {
+ if (mWorkerPrivate && mTarget) {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ mWorkerPrivate->StopSyncLoop(mTarget, NS_ERROR_FAILURE);
+ mWorkerPrivate->DestroySyncLoop(mIndex);
+ }
+ }
+
+ nsresult Run() {
+ CheckedUnsafePtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
+ mWorkerPrivate = nullptr;
+
+ workerPrivate->AssertIsOnWorkerThread();
+
+ return workerPrivate->RunCurrentSyncLoop();
+ }
+
+ nsISerialEventTarget* GetSerialEventTarget() const {
+ // This can be null if CreateNewSyncLoop() fails.
+ return mTarget;
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_workers_workerprivate_h__ */