/* -*- 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/RemoteWorkerOp.h" #include "mozilla/dom/PRemoteWorkerNonLifeCycleOpControllerChild.h" #include "mozilla/dom/ServiceWorkerOpArgs.h" #include "mozilla/dom/SharedWorkerOpArgs.h" class nsISerialEventTarget; class nsIConsoleReportCollector; namespace mozilla::dom { using remoteworker::RemoteWorkerState; class ErrorValue; class FetchEventOpProxyChild; class RemoteWorkerData; class RemoteWorkerServiceKeepAlive; class ServiceWorkerOp; class SharedWorkerOp; 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; friend class SharedWorkerOp; ~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, mozilla::ipc::Endpoint&& aChildEp); void ErrorPropagationOnMainThread(const WorkerErrorReport* aReport, bool aIsErrorEvent); void CSPViolationPropagationOnMainThread(const nsAString& aJSON); void NotifyLock(bool aCreated); void NotifyWebTransport(bool aCreated); void FlushReportsOnMainThread(nsIConsoleReportCollector* aReporter); RefPtr GetTerminationPromise(); RefPtr MaybeSendSetServiceWorkerSkipWaitingFlag(); const nsTArray& WindowIDs() const { return mWindowIDs; } void SetIsThawing(const bool aIsThawing) { mIsThawing = aIsThawing; } bool IsThawing() const { return mIsThawing; } void PendRemoteWorkerOp(RefPtr aOp); void RunAllPendingOpsOnMainThread(); private: class InitializeWorkerRunnable; // 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 mState; const RefPtr mServiceKeepAlive; void ActorDestroy(ActorDestroyReason) override; mozilla::ipc::IPCResult RecvExecOp(SharedWorkerOpArgs&& aOpArgs); mozilla::ipc::IPCResult RecvExecServiceWorkerOp( ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve); already_AddRefed AllocPFetchEventOpProxyChild( const ParentToChildServiceWorkerFetchEventOpArgs& aArgs); mozilla::ipc::IPCResult RecvPFetchEventOpProxyConstructor( PFetchEventOpProxyChild* aActor, const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) override; nsresult ExecWorkerOnMainThread( RemoteWorkerData&& aData, mozilla::ipc::Endpoint&& aChildEp); 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(RemoteWorkerState& aState); void TransitionStateFromCanceledToKilled(); void TransitionStateToRunning(); void TransitionStateToTerminated(); void TransitionStateToTerminated(RemoteWorkerState& aState); void CancelAllPendingOps(RemoteWorkerState& aState); void MaybeStartOp(RefPtr&& aOp); const bool mIsServiceWorker; // Touched on main-thread only. nsTArray mWindowIDs; struct LauncherBoundData { MozPromiseHolder mTerminationPromise; // Flag to ensure we report creation at most once. This could be cleaned up // further. bool mDidSendCreated = false; }; ThreadBound mLauncherData; // Thaw operation holds mState.lock. It means other operations will be blocked // until mState.lock is released. However, Thaw operation is blocked by // RemoteWorkerDebugger registration that needs WorkerLauncher thread to send // IPC to continue the registration on the parent process. If a RemoteWorkerOp // is received on WorkerLauncher thread when the RemoteWorker is thawing, a // deadlock could be happen between WorkerLauncher thread and RemoteWorker's // parent thread. So mIsThawing and mPendingOps are introduced to avoid the // deadlock by pending the operations when RemoteWorker is thawing. // // Note that these could be removed once RemoteWorkerChild off-main-thread // done since the RemoteWorker's parent thread will be WorkerLauncher thread. // And it means when executing WorkerPrivate::Thaw on WorkerLauncher thread, // it is impossible to handle the IPC callback on WorkerLauncher thread at the // same time. Atomic mIsThawing{false}; DataMutex>> mPendingOps; }; } // namespace mozilla::dom #endif // mozilla_dom_RemoteWorkerChild_h