summaryrefslogtreecommitdiffstats
path: root/dom/workers/remoteworkers/RemoteWorkerChild.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/remoteworkers/RemoteWorkerChild.h')
-rw-r--r--dom/workers/remoteworkers/RemoteWorkerChild.h239
1 files changed, 239 insertions, 0 deletions
diff --git a/dom/workers/remoteworkers/RemoteWorkerChild.h b/dom/workers/remoteworkers/RemoteWorkerChild.h
new file mode 100644
index 0000000000..cdb2fb32d7
--- /dev/null
+++ b/dom/workers/remoteworkers/RemoteWorkerChild.h
@@ -0,0 +1,239 @@
+/* -*- 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_RemoteWorkerChild_h
+#define mozilla_dom_RemoteWorkerChild_h
+
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+#include "mozilla/DataMutex.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/ThreadBound.h"
+#include "mozilla/dom/PRemoteWorkerChild.h"
+#include "mozilla/dom/ServiceWorkerOpArgs.h"
+
+class nsISerialEventTarget;
+class nsIConsoleReportCollector;
+
+namespace mozilla::dom {
+
+class ErrorValue;
+class FetchEventOpProxyChild;
+class RemoteWorkerData;
+class RemoteWorkerServiceKeepAlive;
+class ServiceWorkerOp;
+class UniqueMessagePortId;
+class WeakWorkerRef;
+class WorkerErrorReport;
+class WorkerPrivate;
+
+/**
+ * Background-managed "Worker Launcher"-thread-resident created via the
+ * RemoteWorkerManager to actually spawn the worker. Currently, the worker will
+ * be spawned from the main thread due to nsIPrincipal not being able to be
+ * created on background threads and other ownership invariants, most of which
+ * can be relaxed in the future.
+ */
+class RemoteWorkerChild final : public PRemoteWorkerChild {
+ friend class FetchEventOpProxyChild;
+ friend class PRemoteWorkerChild;
+ friend class ServiceWorkerOp;
+
+ ~RemoteWorkerChild();
+
+ public:
+ // Note that all IPC-using methods must only be invoked on the
+ // RemoteWorkerService thread which the inherited
+ // IProtocol::GetActorEventTarget() will return for us.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteWorkerChild, final)
+
+ explicit RemoteWorkerChild(const RemoteWorkerData& aData);
+
+ void ExecWorker(const RemoteWorkerData& aData);
+
+ void ErrorPropagationOnMainThread(const WorkerErrorReport* aReport,
+ bool aIsErrorEvent);
+
+ void CSPViolationPropagationOnMainThread(const nsAString& aJSON);
+
+ void NotifyLock(bool aCreated);
+
+ void NotifyWebTransport(bool aCreated);
+
+ void FlushReportsOnMainThread(nsIConsoleReportCollector* aReporter);
+
+ void AddPortIdentifier(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+ UniqueMessagePortId& aPortIdentifier);
+
+ RefPtr<GenericNonExclusivePromise> GetTerminationPromise();
+
+ RefPtr<GenericPromise> MaybeSendSetServiceWorkerSkipWaitingFlag();
+
+ const nsTArray<uint64_t>& WindowIDs() const { return mWindowIDs; }
+
+ private:
+ class InitializeWorkerRunnable;
+
+ class Op;
+ class SharedWorkerOp;
+
+ struct WorkerPrivateAccessibleState {
+ ~WorkerPrivateAccessibleState();
+ RefPtr<WorkerPrivate> mWorkerPrivate;
+ };
+
+ // Initial state, mWorkerPrivate is initially null but will be initialized on
+ // the main thread by ExecWorkerOnMainThread when the WorkerPrivate is
+ // created. The state will transition to Running or Canceled, also from the
+ // main thread.
+ struct Pending : WorkerPrivateAccessibleState {
+ nsTArray<RefPtr<Op>> mPendingOps;
+ };
+
+ // Running, with the state transition happening on the main thread as a result
+ // of the worker successfully processing our initialization runnable,
+ // indicating that top-level script execution successfully completed. Because
+ // all of our state transitions happen on the main thread and are posed in
+ // terms of the main thread's perspective of the worker's state, it's very
+ // possible for us to skip directly from Pending to Canceled because we decide
+ // to cancel/terminate the worker prior to it finishing script loading or
+ // reporting back to us.
+ struct Running : WorkerPrivateAccessibleState {};
+
+ // Cancel() has been called on the WorkerPrivate on the main thread by a
+ // TerminationOp, top-level script evaluation has failed and canceled the
+ // worker, or in the case of a SharedWorker, close() has been called on
+ // the global scope by content code and the worker has advanced to the
+ // Canceling state. (Dedicated Workers can also self close, but they will
+ // never be RemoteWorkers. Although a SharedWorker can own DedicatedWorkers.)
+ // Browser shutdown will result in a TerminationOp thanks to use of a shutdown
+ // blocker in the parent, so the RuntimeService shouldn't get involved, but we
+ // would also handle that case acceptably too.
+ //
+ // Because worker self-closing is still handled by dispatching a runnable to
+ // the main thread to effectively call WorkerPrivate::Cancel(), there isn't
+ // a race between a worker deciding to self-close and our termination ops.
+ //
+ // In this state, we have dropped the reference to the WorkerPrivate and will
+ // no longer be dispatching runnables to the worker. We wait in this state
+ // until the termination lambda is invoked letting us know that the worker has
+ // entirely shutdown and we can advanced to the Killed state.
+ struct Canceled {};
+
+ // The worker termination lambda has been invoked and we know the Worker is
+ // entirely shutdown. (Inherently it is possible for us to advance to this
+ // state while the nsThread for the worker is still in the process of
+ // shutting down, but no more worker code will run on it.)
+ //
+ // This name is chosen to match the Worker's own state model.
+ struct Killed {};
+
+ using State = Variant<Pending, Running, Canceled, Killed>;
+
+ // The state of the WorkerPrivate as perceived by the owner on the main
+ // thread. All state transitions now happen on the main thread, but the
+ // Worker Launcher thread will consult the state and will directly append ops
+ // to the Pending queue
+ DataMutex<State> mState;
+
+ const RefPtr<RemoteWorkerServiceKeepAlive> mServiceKeepAlive;
+
+ class Op {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ virtual ~Op() = default;
+
+ virtual bool MaybeStart(RemoteWorkerChild* aOwner, State& aState) = 0;
+
+ virtual void StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) = 0;
+
+ virtual void Cancel() = 0;
+ };
+
+ void ActorDestroy(ActorDestroyReason) override;
+
+ mozilla::ipc::IPCResult RecvExecOp(RemoteWorkerOp&& aOp);
+
+ mozilla::ipc::IPCResult RecvExecServiceWorkerOp(
+ ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve);
+
+ already_AddRefed<PFetchEventOpProxyChild> AllocPFetchEventOpProxyChild(
+ const ParentToChildServiceWorkerFetchEventOpArgs& aArgs);
+
+ mozilla::ipc::IPCResult RecvPFetchEventOpProxyConstructor(
+ PFetchEventOpProxyChild* aActor,
+ const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) override;
+
+ nsresult ExecWorkerOnMainThread(RemoteWorkerData&& aData);
+
+ void ExceptionalErrorTransitionDuringExecWorker();
+
+ void RequestWorkerCancellation();
+
+ void InitializeOnWorker();
+
+ void CreationSucceededOnAnyThread();
+
+ void CreationFailedOnAnyThread();
+
+ void CreationSucceededOrFailedOnAnyThread(bool aDidCreationSucceed);
+
+ // Cancels the worker if it has been started and ensures that we transition
+ // to the Terminated state once the worker has been terminated or we have
+ // ensured that it will never start.
+ void CloseWorkerOnMainThread();
+
+ void ErrorPropagation(const ErrorValue& aValue);
+
+ void ErrorPropagationDispatch(nsresult aError);
+
+ // When the WorkerPrivate Cancellation lambda is invoked, it's possible that
+ // we have not yet advanced to running from pending, so we could be in either
+ // state. This method is expected to be called by the Workers' cancellation
+ // lambda and will obtain the lock and call the
+ // TransitionStateFromPendingToCanceled if appropriate. Otherwise it will
+ // directly move from the running state to the canceled state which does not
+ // require additional cleanup.
+ void OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled();
+ // A helper used by the above method by the worker cancellation lambda if the
+ // the worker hasn't started running, or in exceptional cases where we bail
+ // out of the ExecWorker method early. The caller must be holding the lock
+ // (in order to pass in the state).
+ void TransitionStateFromPendingToCanceled(State& aState);
+ void TransitionStateFromCanceledToKilled();
+
+ void TransitionStateToRunning();
+
+ void TransitionStateToTerminated();
+
+ void TransitionStateToTerminated(State& aState);
+
+ void CancelAllPendingOps(State& aState);
+
+ void MaybeStartOp(RefPtr<Op>&& aOp);
+
+ const bool mIsServiceWorker;
+
+ // Touched on main-thread only.
+ nsTArray<uint64_t> mWindowIDs;
+
+ struct LauncherBoundData {
+ MozPromiseHolder<GenericNonExclusivePromise> mTerminationPromise;
+ // Flag to ensure we report creation at most once. This could be cleaned up
+ // further.
+ bool mDidSendCreated = false;
+ };
+
+ ThreadBound<LauncherBoundData> mLauncherData;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_RemoteWorkerChild_h