diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 4eb15bf4784d..b9012f587b01 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1612,15 +1612,21 @@ public: nsresult Cancel() override { - // First run the default cancelation code - WorkerControlRunnable::Cancel(); - - // Attempt to cancel the inner runnable as well nsCOMPtr cr = do_QueryInterface(mInner); - if (cr) { - return cr->Cancel(); + + // If the inner runnable is not cancellable, then just do the normal + // WorkerControlRunnable thing. This will end up calling Run(). + if (!cr) { + WorkerControlRunnable::Cancel(); + return NS_OK; } - return NS_OK; + + // Otherwise call the inner runnable's Cancel() and treat this like + // a WorkerRunnable cancel. We can't call WorkerControlRunnable::Cancel() + // in this case since that would result in both Run() and the inner + // Cancel() being called. + Unused << cr->Cancel(); + return WorkerRunnable::Cancel(); } }; @@ -1628,17 +1634,36 @@ public: BEGIN_WORKERS_NAMESPACE -class WorkerControlEventTarget final : public nsISerialEventTarget +class WorkerEventTarget final : public nsISerialEventTarget { +public: + // The WorkerEventTarget supports different dispatch behaviors: + // + // * Hybrid targets will attempt to dispatch as a normal runnable, + // but fallback to a control runnable if that fails. This is + // often necessary for code that wants normal dispatch order, but + // also needs to execute while the worker is shutting down (possibly + // with a holder in place.) + // + // * ControlOnly targets will simply dispatch a control runnable. + enum class Behavior : uint8_t { + Hybrid, + ControlOnly + }; + +private: mozilla::Mutex mMutex; WorkerPrivate* mWorkerPrivate; + const Behavior mBehavior; - ~WorkerControlEventTarget() = default; + ~WorkerEventTarget() = default; public: - explicit WorkerControlEventTarget(WorkerPrivate* aWorkerPrivate) - : mMutex("WorkerControlEventTarget") + WorkerEventTarget(WorkerPrivate* aWorkerPrivate, + Behavior aBehavior) + : mMutex("WorkerEventTarget") , mWorkerPrivate(aWorkerPrivate) + , mBehavior(aBehavior) { MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate); } @@ -1667,8 +1692,20 @@ public: return NS_ERROR_FAILURE; } + nsCOMPtr runnable(aRunnable); + + if (mBehavior == Behavior::Hybrid) { + RefPtr r = + mWorkerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); + if (r->Dispatch()) { + return NS_OK; + } + + runnable = r.forget(); + } + RefPtr r = new WrappedControlRunnable(mWorkerPrivate, - Move(aRunnable)); + runnable.forget()); if (!r->Dispatch()) { return NS_ERROR_FAILURE; } @@ -1704,7 +1741,7 @@ public: NS_DECL_THREADSAFE_ISUPPORTS }; -NS_IMPL_ISUPPORTS(WorkerControlEventTarget, nsIEventTarget, +NS_IMPL_ISUPPORTS(WorkerEventTarget, nsIEventTarget, nsISerialEventTarget) END_WORKERS_NAMESPACE @@ -4426,7 +4463,8 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent, , mNumHoldersPreventingShutdownStart(0) , mDebuggerEventLoopLevel(0) , mMainThreadEventTarget(GetMainThreadEventTarget()) - , mWorkerControlEventTarget(new WorkerControlEventTarget(this)) + , mWorkerControlEventTarget(new WorkerEventTarget(this, + WorkerEventTarget::Behavior::ControlOnly)) , mErrorHandlerRecursionCount(0) , mNextTimeoutId(1) , mStatus(Pending) diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 772e7c69227b..9ea9036acb22 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -87,7 +87,7 @@ BEGIN_WORKERS_NAMESPACE class AutoSyncLoopHolder; class SharedWorker; class ServiceWorkerClientInfo; -class WorkerControlEventTarget; +class WorkerEventTarget; class WorkerControlRunnable; class WorkerDebugger; class WorkerPrivate; @@ -1004,7 +1004,7 @@ class WorkerPrivate : public WorkerPrivateParent uint32_t mDebuggerEventLoopLevel; RefPtr mMainThreadThrottledEventQueue; nsCOMPtr mMainThreadEventTarget; - RefPtr mWorkerControlEventTarget; + RefPtr mWorkerControlEventTarget; struct SyncLoopInfo {