diff options
Diffstat (limited to 'dom/workers')
23 files changed, 950 insertions, 617 deletions
diff --git a/dom/workers/EventWithOptionsRunnable.cpp b/dom/workers/EventWithOptionsRunnable.cpp index 2ddc56d946..5c4988c9a7 100644 --- a/dom/workers/EventWithOptionsRunnable.cpp +++ b/dom/workers/EventWithOptionsRunnable.cpp @@ -33,8 +33,7 @@ namespace mozilla::dom { EventWithOptionsRunnable::EventWithOptionsRunnable(Worker& aWorker, const char* aName) - : WorkerDebuggeeRunnable(aWorker.mWorkerPrivate, aName, - WorkerRunnable::WorkerThread), + : WorkerDebuggeeRunnable(aName), StructuredCloneHolder(CloningSupported, TransferringSupported, StructuredCloneScope::SameProcess) {} @@ -132,19 +131,6 @@ bool EventWithOptionsRunnable::BuildAndFireEvent( bool EventWithOptionsRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { - if (mTarget == ParentThread) { - // Don't fire this event if the JS object has been disconnected from the - // private object. - if (!aWorkerPrivate->IsAcceptingEvents()) { - return true; - } - - aWorkerPrivate->AssertInnerWindowIsCorrect(); - - return BuildAndFireEvent(aCx, aWorkerPrivate, - aWorkerPrivate->ParentEventTargetRef()); - } - MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx)); MOZ_ASSERT(aWorkerPrivate->GlobalScope()); diff --git a/dom/workers/MessageEventRunnable.cpp b/dom/workers/MessageEventRunnable.cpp index 8edc7037cd..33efc966b4 100644 --- a/dom/workers/MessageEventRunnable.cpp +++ b/dom/workers/MessageEventRunnable.cpp @@ -14,9 +14,8 @@ namespace mozilla::dom { -MessageEventRunnable::MessageEventRunnable(WorkerPrivate* aWorkerPrivate, - Target aTarget) - : WorkerDebuggeeRunnable(aWorkerPrivate, "MessageEventRunnable", aTarget), +MessageEventRunnable::MessageEventRunnable(WorkerPrivate* aWorkerPrivate) + : WorkerDebuggeeRunnable("MessageEventRunnable"), StructuredCloneHolder(CloningSupported, TransferringSupported, StructuredCloneScope::SameProcess) {} @@ -84,20 +83,6 @@ bool MessageEventRunnable::DispatchDOMEvent(JSContext* aCx, bool MessageEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { - if (mTarget == ParentThread) { - // Don't fire this event if the JS object has been disconnected from the - // private object. - if (!aWorkerPrivate->IsAcceptingEvents()) { - return true; - } - - aWorkerPrivate->AssertInnerWindowIsCorrect(); - - return DispatchDOMEvent(aCx, aWorkerPrivate, - aWorkerPrivate->ParentEventTargetRef(), - !aWorkerPrivate->GetParent()); - } - MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx)); MOZ_ASSERT(aWorkerPrivate->GlobalScope()); @@ -124,4 +109,97 @@ void MessageEventRunnable::DispatchError(JSContext* aCx, aTarget->DispatchEvent(*event); } +MessageEventToParentRunnable::MessageEventToParentRunnable( + WorkerPrivate* aWorkerPrivate) + : WorkerParentDebuggeeRunnable("MessageEventToParentRunnable"), + StructuredCloneHolder(CloningSupported, TransferringSupported, + StructuredCloneScope::SameProcess) {} + +bool MessageEventToParentRunnable::DispatchDOMEvent( + JSContext* aCx, WorkerPrivate* aWorkerPrivate, + DOMEventTargetHelper* aTarget, bool aIsMainThread) { + nsCOMPtr<nsIGlobalObject> parent = aTarget->GetParentObject(); + + // For some workers without window, parent is null and we try to find it + // from the JS Context. + if (!parent) { + JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx)); + if (NS_WARN_IF(!globalObject)) { + return false; + } + + parent = xpc::NativeGlobal(globalObject); + if (NS_WARN_IF(!parent)) { + return false; + } + } + + MOZ_ASSERT(parent); + + JS::Rooted<JS::Value> messageData(aCx); + IgnoredErrorResult rv; + + JS::CloneDataPolicy cloneDataPolicy; + if (parent->GetClientInfo().isSome() && + parent->GetClientInfo()->AgentClusterId().isSome() && + parent->GetClientInfo()->AgentClusterId()->Equals( + aWorkerPrivate->AgentClusterId())) { + cloneDataPolicy.allowIntraClusterClonableSharedObjects(); + } + + if (aWorkerPrivate->IsSharedMemoryAllowed()) { + cloneDataPolicy.allowSharedMemoryObjects(); + } + + Read(parent, aCx, &messageData, cloneDataPolicy, rv); + + if (NS_WARN_IF(rv.Failed())) { + DispatchError(aCx, aTarget); + return false; + } + + Sequence<OwningNonNull<MessagePort>> ports; + if (!TakeTransferredPortsAsSequence(ports)) { + DispatchError(aCx, aTarget); + return false; + } + + RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr); + event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo, + Cancelable::eNo, messageData, u""_ns, u""_ns, nullptr, + ports); + + event->SetTrusted(true); + + aTarget->DispatchEvent(*event); + + return true; +} + +bool MessageEventToParentRunnable::WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) { + if (!aWorkerPrivate->IsAcceptingEvents()) { + return true; + } + + aWorkerPrivate->AssertInnerWindowIsCorrect(); + + return DispatchDOMEvent(aCx, aWorkerPrivate, + aWorkerPrivate->ParentEventTargetRef(), + !aWorkerPrivate->GetParent()); +} + +void MessageEventToParentRunnable::DispatchError( + JSContext* aCx, DOMEventTargetHelper* aTarget) { + RootedDictionary<MessageEventInit> init(aCx); + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = + MessageEvent::Constructor(aTarget, u"messageerror"_ns, init); + event->SetTrusted(true); + + aTarget->DispatchEvent(*event); +} + } // namespace mozilla::dom diff --git a/dom/workers/MessageEventRunnable.h b/dom/workers/MessageEventRunnable.h index c20efb4c55..45bce9949f 100644 --- a/dom/workers/MessageEventRunnable.h +++ b/dom/workers/MessageEventRunnable.h @@ -20,7 +20,21 @@ namespace dom { class MessageEventRunnable final : public WorkerDebuggeeRunnable, public StructuredCloneHolder { public: - MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget); + explicit MessageEventRunnable(WorkerPrivate* aWorkerPrivate); + + bool DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + DOMEventTargetHelper* aTarget, bool aIsMainThread); + + private: + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; + + void DispatchError(JSContext* aCx, DOMEventTargetHelper* aTarget); +}; + +class MessageEventToParentRunnable final : public WorkerParentDebuggeeRunnable, + public StructuredCloneHolder { + public: + explicit MessageEventToParentRunnable(WorkerPrivate* aWorkerPrivate); bool DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate, DOMEventTargetHelper* aTarget, bool aIsMainThread); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 321895e700..047384b420 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -343,7 +343,7 @@ void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) { #define PREF(suffix_, key_) \ { \ - nsLiteralCString(PREF_MEM_OPTIONS_PREFIX suffix_), \ + nsLiteralCString(suffix_), \ PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX suffix_, key_ \ } constexpr WorkerGCPref kWorkerPrefs[] = { @@ -372,6 +372,7 @@ void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) { PREF("gc_parallel_marking", JSGC_PARALLEL_MARKING_ENABLED), PREF("gc_parallel_marking_threshold_mb", JSGC_PARALLEL_MARKING_THRESHOLD_MB), + PREF("gc_max_parallel_marking_threads", JSGC_MAX_MARKING_THREADS), #ifdef NIGHTLY_BUILD PREF("gc_experimental_semispace_nursery", JSGC_SEMISPACE_NURSERY_ENABLED), #endif @@ -454,6 +455,7 @@ void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) { case JSGC_MAX_EMPTY_CHUNK_COUNT: case JSGC_HEAP_GROWTH_FACTOR: case JSGC_PARALLEL_MARKING_THRESHOLD_MB: + case JSGC_MAX_MARKING_THREADS: UpdateCommonJSGCMemoryOption(rts, pref->fullName, pref->key); break; default: @@ -596,7 +598,7 @@ void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) { // do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run // being called, DispatchToEventLoopCallback failure is expected to happen // during shutdown. -class JSDispatchableRunnable final : public WorkerRunnable { +class JSDispatchableRunnable final : public WorkerThreadRunnable { JS::Dispatchable* mDispatchable; ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); } @@ -617,21 +619,19 @@ class JSDispatchableRunnable final : public WorkerRunnable { public: JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate, JS::Dispatchable* aDispatchable) - : WorkerRunnable(aWorkerPrivate, "JSDispatchableRunnable", - WorkerRunnable::WorkerThread), + : WorkerThreadRunnable("JSDispatchableRunnable"), mDispatchable(aDispatchable) { MOZ_ASSERT(mDispatchable); } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); - MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext()); + MOZ_ASSERT(aCx == aWorkerPrivate->GetJSContext()); MOZ_ASSERT(mDispatchable); AutoJSAPI jsapi; jsapi.Init(); - mDispatchable->run(mWorkerPrivate->GetJSContext(), + mDispatchable->run(aWorkerPrivate->GetJSContext(), JS::Dispatchable::NotShuttingDown); mDispatchable = nullptr; // mDispatchable may delete itself @@ -644,7 +644,7 @@ class JSDispatchableRunnable final : public WorkerRunnable { AutoJSAPI jsapi; jsapi.Init(); - mDispatchable->run(mWorkerPrivate->GetJSContext(), + mDispatchable->run(GetCurrentThreadWorkerPrivate()->GetJSContext(), JS::Dispatchable::ShuttingDown); mDispatchable = nullptr; // mDispatchable may delete itself @@ -665,7 +665,7 @@ static bool DispatchToEventLoop(void* aClosure, // the JSDispatchableRunnable comment above. RefPtr<JSDispatchableRunnable> r = new JSDispatchableRunnable(workerPrivate, aDispatchable); - return r->Dispatch(); + return r->Dispatch(workerPrivate); } static bool ConsumeStream(JSContext* aCx, JS::Handle<JSObject*> aObj, @@ -1482,9 +1482,9 @@ namespace { class DumpCrashInfoRunnable final : public WorkerControlRunnable { public: explicit DumpCrashInfoRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, "DumpCrashInfoRunnable", - WorkerThread), - mMonitor("DumpCrashInfoRunnable::mMonitor") {} + : WorkerControlRunnable("DumpCrashInfoRunnable"), + mMonitor("DumpCrashInfoRunnable::mMonitor"), + mWorkerPrivate(aWorkerPrivate) {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MonitorAutoLock lock(mMonitor); @@ -1510,7 +1510,7 @@ class DumpCrashInfoRunnable final : public WorkerControlRunnable { bool DispatchAndWait() { MonitorAutoLock lock(mMonitor); - if (!Dispatch()) { + if (!Dispatch(mWorkerPrivate)) { // The worker is already dead but the main thread still didn't remove it // from RuntimeService's registry. return false; @@ -1537,6 +1537,7 @@ class DumpCrashInfoRunnable final : public WorkerControlRunnable { Monitor mMonitor MOZ_UNANNOTATED; nsCString mMsg; FlippedOnce<false> mHasMsg; + WorkerPrivate* mWorkerPrivate; }; struct ActiveWorkerStats { @@ -2066,21 +2067,24 @@ WorkerThreadPrimaryRunnable::Run() { url.get()); using mozilla::ipc::BackgroundChild; - { + bool runLoopRan = false; auto failureCleanup = MakeScopeExit([&]() { - // The creation of threadHelper above is the point at which a worker is - // considered to have run, because the `mPreStartRunnables` are all - // re-dispatched after `mThread` is set. We need to let the WorkerPrivate - // know so it can clean up the various event loops and delete the worker. - mWorkerPrivate->RunLoopNeverRan(); + // If Worker initialization fails, call WorkerPrivate::ScheduleDeletion() + // to release the WorkerPrivate in the parent thread. + mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan); }); mWorkerPrivate->SetWorkerPrivateInWorkerThread(mThread.unsafeGetRawPtr()); const auto threadCleanup = MakeScopeExit([&] { - // This must be called before ScheduleDeletion, which is either called - // from failureCleanup leaving scope, or from the outer scope. + // If Worker initialization fails, such as creating a BackgroundChild or + // the worker's JSContext initialization failing, call + // WorkerPrivate::RunLoopNeverRan() to set the Worker to the correct + // status, which means "Dead," to forbid WorkerThreadRunnable dispatching. + if (!runLoopRan) { + mWorkerPrivate->RunLoopNeverRan(); + } mWorkerPrivate->ResetWorkerPrivateInWorkerThread(); }); @@ -2117,6 +2121,7 @@ WorkerThreadPrimaryRunnable::Run() { } failureCleanup.release(); + runLoopRan = true; { PROFILER_SET_JS_CONTEXT(cx); diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 9dcb1d0c9a..b841a6e357 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1641,7 +1641,8 @@ void ScriptLoaderRunnable::DispatchProcessPendingRequests() { Span<RefPtr<ThreadSafeRequestHandle>>{maybeRangeToExecute->first, maybeRangeToExecute->second}); - if (!runnable->Dispatch() && mScriptLoader->mSyncLoopTarget) { + if (!runnable->Dispatch(mWorkerRef->Private()) && + mScriptLoader->mSyncLoopTarget) { MOZ_ASSERT(false, "This should never fail!"); } } @@ -1651,8 +1652,7 @@ ScriptExecutorRunnable::ScriptExecutorRunnable( WorkerScriptLoader* aScriptLoader, WorkerPrivate* aWorkerPrivate, nsISerialEventTarget* aSyncLoopTarget, Span<RefPtr<ThreadSafeRequestHandle>> aLoadedRequests) - : MainThreadWorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget, - "ScriptExecutorRunnable"), + : MainThreadWorkerSyncRunnable(aSyncLoopTarget, "ScriptExecutorRunnable"), mScriptLoader(aScriptLoader), mLoadedRequests(aLoadedRequests) {} diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp index 88df53b877..6d85948d96 100644 --- a/dom/workers/Worker.cpp +++ b/dom/workers/Worker.cpp @@ -126,7 +126,7 @@ void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, JS::ProfilingCategoryPair::DOM, flags); RefPtr<MessageEventRunnable> runnable = - new MessageEventRunnable(mWorkerPrivate, WorkerRunnable::WorkerThread); + new MessageEventRunnable(mWorkerPrivate); JS::CloneDataPolicy clonePolicy; // DedicatedWorkers are always part of the same agent cluster. @@ -157,7 +157,7 @@ void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, // The worker could have closed between the time we entered this function and // checked ParentStatusProtected and now, which could cause the dispatch to // fail. - Unused << NS_WARN_IF(!runnable->Dispatch()); + Unused << NS_WARN_IF(!runnable->Dispatch(mWorkerPrivate)); } void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, @@ -191,7 +191,7 @@ void Worker::PostEventWithOptions(JSContext* aCx, return; } - Unused << NS_WARN_IF(!aRunnable->Dispatch()); + Unused << NS_WARN_IF(!aRunnable->Dispatch(mWorkerPrivate)); } void Worker::Terminate() { diff --git a/dom/workers/WorkerCSPEventListener.cpp b/dom/workers/WorkerCSPEventListener.cpp index c693928486..fa17ba0dcb 100644 --- a/dom/workers/WorkerCSPEventListener.cpp +++ b/dom/workers/WorkerCSPEventListener.cpp @@ -19,8 +19,7 @@ namespace { class WorkerCSPEventRunnable final : public MainThreadWorkerRunnable { public: WorkerCSPEventRunnable(WorkerPrivate* aWorkerPrivate, const nsAString& aJSON) - : MainThreadWorkerRunnable(aWorkerPrivate, "WorkerCSPEventRunnable"), - mJSON(aJSON) {} + : MainThreadWorkerRunnable("WorkerCSPEventRunnable"), mJSON(aJSON) {} private: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { @@ -83,7 +82,7 @@ WorkerCSPEventListener::OnCSPViolationEvent(const nsAString& aJSON) { if (NS_IsMainThread()) { RefPtr<WorkerCSPEventRunnable> runnable = new WorkerCSPEventRunnable(workerPrivate, aJSON); - runnable->Dispatch(); + runnable->Dispatch(workerPrivate); return NS_OK; } diff --git a/dom/workers/WorkerDebugger.cpp b/dom/workers/WorkerDebugger.cpp index a3e0af7a38..43cccf1102 100644 --- a/dom/workers/WorkerDebugger.cpp +++ b/dom/workers/WorkerDebugger.cpp @@ -37,7 +37,7 @@ class DebuggerMessageEventRunnable final : public WorkerDebuggerRunnable { public: DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate, const nsAString& aMessage) - : WorkerDebuggerRunnable(aWorkerPrivate, "DebuggerMessageEventRunnable"), + : WorkerDebuggerRunnable("DebuggerMessageEventRunnable"), mMessage(aMessage) {} private: @@ -74,7 +74,7 @@ class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable { CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate, const nsAString& aScriptURL, const mozilla::Encoding* aDocumentEncoding) - : WorkerDebuggerRunnable(aWorkerPrivate, "CompileDebuggerScriptRunnable"), + : WorkerDebuggerRunnable("CompileDebuggerScriptRunnable"), mScriptURL(aScriptURL), mDocumentEncoding(aDocumentEncoding) {} @@ -376,7 +376,7 @@ WorkerDebugger::Initialize(const nsAString& aURL) { RefPtr<CompileDebuggerScriptRunnable> runnable = new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL, aDocumentEncoding); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(mWorkerPrivate)) { return NS_ERROR_FAILURE; } @@ -396,7 +396,7 @@ WorkerDebugger::PostMessageMoz(const nsAString& aMessage) { RefPtr<DebuggerMessageEventRunnable> runnable = new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(mWorkerPrivate)) { return NS_ERROR_FAILURE; } diff --git a/dom/workers/WorkerDocumentListener.cpp b/dom/workers/WorkerDocumentListener.cpp index cdbe30e072..a5285f7ad9 100644 --- a/dom/workers/WorkerDocumentListener.cpp +++ b/dom/workers/WorkerDocumentListener.cpp @@ -52,11 +52,10 @@ void WorkerDocumentListener::OnVisible(bool aVisible) { return; } - class VisibleRunnable final : public WorkerRunnable { + class VisibleRunnable final : public WorkerThreadRunnable { public: VisibleRunnable(WorkerPrivate* aWorkerPrivate, bool aVisible) - : WorkerRunnable(aWorkerPrivate, "VisibleRunnable"), - mVisible(aVisible) {} + : WorkerThreadRunnable("VisibleRunnable"), mVisible(aVisible) {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { WorkerGlobalScope* scope = aWorkerPrivate->GlobalScope(); @@ -70,7 +69,7 @@ void WorkerDocumentListener::OnVisible(bool aVisible) { }; auto runnable = MakeRefPtr<VisibleRunnable>(mWorkerRef->Private(), aVisible); - runnable->Dispatch(); + runnable->Dispatch(mWorkerRef->Private()); } void WorkerDocumentListener::SetListening(uint64_t aWindowID, bool aListen) { diff --git a/dom/workers/WorkerError.cpp b/dom/workers/WorkerError.cpp index 1b75599211..4fe9b237fe 100644 --- a/dom/workers/WorkerError.cpp +++ b/dom/workers/WorkerError.cpp @@ -62,13 +62,13 @@ namespace mozilla::dom { namespace { -class ReportErrorRunnable final : public WorkerDebuggeeRunnable { +class ReportErrorRunnable final : public WorkerParentDebuggeeRunnable { UniquePtr<WorkerErrorReport> mReport; public: ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, UniquePtr<WorkerErrorReport> aReport) - : WorkerDebuggeeRunnable(aWorkerPrivate, "ReportErrorRunnable"), + : WorkerParentDebuggeeRunnable("ReportErrorRunnable"), mReport(std::move(aReport)) {} private: @@ -138,7 +138,7 @@ class ReportErrorRunnable final : public WorkerDebuggeeRunnable { } }; -class ReportGenericErrorRunnable final : public WorkerDebuggeeRunnable { +class ReportGenericErrorRunnable final : public WorkerParentDebuggeeRunnable { public: static void CreateAndDispatch(WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(aWorkerPrivate); @@ -146,12 +146,12 @@ class ReportGenericErrorRunnable final : public WorkerDebuggeeRunnable { RefPtr<ReportGenericErrorRunnable> runnable = new ReportGenericErrorRunnable(aWorkerPrivate); - runnable->Dispatch(); + runnable->Dispatch(aWorkerPrivate); } private: explicit ReportGenericErrorRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerDebuggeeRunnable(aWorkerPrivate, "ReportGenericErrorRunnable") { + : WorkerParentDebuggeeRunnable("ReportGenericErrorRunnable") { aWorkerPrivate->AssertIsOnWorkerThread(); } @@ -354,7 +354,7 @@ void WorkerErrorReport::ReportError( if (aWorkerPrivate) { RefPtr<ReportErrorRunnable> runnable = new ReportErrorRunnable(aWorkerPrivate, std::move(aReport)); - runnable->Dispatch(); + runnable->Dispatch(aWorkerPrivate); return; } diff --git a/dom/workers/WorkerEventTarget.cpp b/dom/workers/WorkerEventTarget.cpp index cb58b4f8ed..94ba7a8097 100644 --- a/dom/workers/WorkerEventTarget.cpp +++ b/dom/workers/WorkerEventTarget.cpp @@ -34,8 +34,7 @@ class WrappedControlRunnable final : public WorkerControlRunnable { public: WrappedControlRunnable(WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIRunnable>&& aInner) - : WorkerControlRunnable(aWorkerPrivate, "WrappedControlRunnable", - WorkerThread), + : WorkerControlRunnable("WrappedControlRunnable"), mInner(std::move(aInner)) {} virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { @@ -119,7 +118,7 @@ WorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, RefPtr<WorkerRunnable> r = mWorkerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); - if (r->Dispatch()) { + if (r->Dispatch(mWorkerPrivate)) { return NS_OK; } runnable = std::move(r); @@ -134,7 +133,7 @@ WorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, ("WorkerEventTarget::Dispatch [%p] Wrapped runnable as control " "runnable(%p)", this, r.get())); - if (!r->Dispatch()) { + if (!r->Dispatch(mWorkerPrivate)) { LOGV( ("WorkerEventTarget::Dispatch [%p] Dispatch as control runnable(%p) " "fail", diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index 1be36bc7d4..0f9dada965 100644 --- a/dom/workers/WorkerNavigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -227,6 +227,8 @@ StorageManager* WorkerNavigator::Storage() { MOZ_ASSERT(global); mStorageManager = new StorageManager(global); + + workerPrivate->NotifyStorageKeyUsed(); } return mStorageManager; diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index df248acda4..d9bf764312 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -180,19 +180,20 @@ inline UniquePtrComparator<T> GetUniquePtrComparator( // 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 WorkerRunnable { +class ExternalRunnableWrapper final : public WorkerThreadRunnable { nsCOMPtr<nsIRunnable> mWrappedRunnable; public: ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate, nsIRunnable* aWrappedRunnable) - : WorkerRunnable(aWorkerPrivate, "ExternalRunnableWrapper", WorkerThread), + : WorkerThreadRunnable("ExternalRunnableWrapper"), mWrappedRunnable(aWrappedRunnable) { MOZ_ASSERT(aWorkerPrivate); MOZ_ASSERT(aWrappedRunnable); } - NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable) + NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, + WorkerThreadRunnable) private: ~ExternalRunnableWrapper() = default; @@ -249,8 +250,7 @@ class WorkerFinishedRunnable final : public WorkerControlRunnable { public: WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate, WorkerPrivate* aFinishedWorker) - : WorkerControlRunnable(aWorkerPrivate, "WorkerFinishedRunnable", - WorkerThread), + : WorkerControlRunnable("WorkerFinishedRunnable"), mFinishedWorker(aFinishedWorker) { aFinishedWorker->IncreaseWorkerFinishedRunnableCount(); } @@ -337,8 +337,7 @@ class CompileScriptRunnable final : public WorkerDebuggeeRunnable { UniquePtr<SerializedStackHolder> aOriginStack, const nsAString& aScriptURL, const mozilla::Encoding* aDocumentEncoding) - : WorkerDebuggeeRunnable(aWorkerPrivate, "CompileScriptRunnable", - WorkerThread), + : WorkerDebuggeeRunnable("CompileScriptRunnable"), mScriptURL(aScriptURL), mDocumentEncoding(aDocumentEncoding), mOriginStack(aOriginStack.release()) {} @@ -436,7 +435,7 @@ class CompileScriptRunnable final : public WorkerDebuggeeRunnable { if (!aRunResult) { aWorkerPrivate->CloseInternal(); } - WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); + WorkerThreadRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); } }; @@ -445,8 +444,7 @@ class NotifyRunnable final : public WorkerControlRunnable { public: NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus) - : WorkerControlRunnable(aWorkerPrivate, "NotifyRunnable", WorkerThread), - mStatus(aStatus) { + : WorkerControlRunnable("NotifyRunnable"), mStatus(aStatus) { MOZ_ASSERT(aStatus == Closing || aStatus == Canceling || aStatus == Killing); } @@ -474,7 +472,7 @@ class NotifyRunnable final : public WorkerControlRunnable { class FreezeRunnable final : public WorkerControlRunnable { public: explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, "FreezeRunnable", WorkerThread) {} + : WorkerControlRunnable("FreezeRunnable") {} private: virtual bool WorkerRun(JSContext* aCx, @@ -486,7 +484,7 @@ class FreezeRunnable final : public WorkerControlRunnable { class ThawRunnable final : public WorkerControlRunnable { public: explicit ThawRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, "ThawRunnable", WorkerThread) {} + : WorkerControlRunnable("ThawRunnable") {} private: virtual bool WorkerRun(JSContext* aCx, @@ -500,9 +498,8 @@ class PropagateStorageAccessPermissionGrantedRunnable final public: explicit PropagateStorageAccessPermissionGrantedRunnable( WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, - "PropagateStorageAccessPermissionGrantedRunnable", - WorkerThread) {} + : WorkerControlRunnable( + "PropagateStorageAccessPermissionGrantedRunnable") {} private: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { @@ -511,7 +508,7 @@ class PropagateStorageAccessPermissionGrantedRunnable final } }; -class ReportErrorToConsoleRunnable final : public WorkerRunnable { +class ReportErrorToConsoleRunnable final : public WorkerParentThreadRunnable { const char* mMessage; const nsTArray<nsString> mParams; @@ -529,7 +526,7 @@ class ReportErrorToConsoleRunnable final : public WorkerRunnable { if (aWorkerPrivate) { RefPtr<ReportErrorToConsoleRunnable> runnable = new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage, aParams); - runnable->Dispatch(); + runnable->Dispatch(aWorkerPrivate); return; } @@ -543,8 +540,7 @@ class ReportErrorToConsoleRunnable final : public WorkerRunnable { ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage, const nsTArray<nsString>& aParams) - : WorkerRunnable(aWorkerPrivate, "ReportErrorToConsoleRunnable", - ParentThread), + : WorkerParentThreadRunnable("ReportErrorToConsoleRunnable"), mMessage(aMessage), mParams(aParams.Clone()) {} @@ -565,14 +561,13 @@ class ReportErrorToConsoleRunnable final : public WorkerRunnable { } }; -class RunExpiredTimoutsRunnable final : public WorkerRunnable, +class RunExpiredTimoutsRunnable final : public WorkerThreadRunnable, public nsITimerCallback { public: NS_DECL_ISUPPORTS_INHERITED explicit RunExpiredTimoutsRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerRunnable(aWorkerPrivate, "RunExpiredTimoutsRunnable", - WorkerThread) {} + : WorkerThreadRunnable("RunExpiredTimoutsRunnable") {} private: ~RunExpiredTimoutsRunnable() = default; @@ -599,17 +594,16 @@ class RunExpiredTimoutsRunnable final : public WorkerRunnable, Notify(nsITimer* aTimer) override { return Run(); } }; -NS_IMPL_ISUPPORTS_INHERITED(RunExpiredTimoutsRunnable, WorkerRunnable, +NS_IMPL_ISUPPORTS_INHERITED(RunExpiredTimoutsRunnable, WorkerThreadRunnable, nsITimerCallback) -class DebuggerImmediateRunnable final : public WorkerRunnable { +class DebuggerImmediateRunnable final : public WorkerThreadRunnable { RefPtr<dom::Function> mHandler; public: explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate, dom::Function& aHandler) - : WorkerRunnable(aWorkerPrivate, "DebuggerImmediateRunnable", - WorkerThread), + : WorkerThreadRunnable("DebuggerImmediateRunnable"), mHandler(&aHandler) {} private: @@ -670,8 +664,7 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { public: UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate, const JS::ContextOptions& aContextOptions) - : WorkerControlRunnable(aWorkerPrivate, "UpdateContextOptionsRunnable", - WorkerThread), + : WorkerControlRunnable("UpdateContextOptionsRunnable"), mContextOptions(aContextOptions) {} private: @@ -682,13 +675,13 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { } }; -class UpdateLanguagesRunnable final : public WorkerRunnable { +class UpdateLanguagesRunnable final : public WorkerThreadRunnable { nsTArray<nsString> mLanguages; public: UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate, const nsTArray<nsString>& aLanguages) - : WorkerRunnable(aWorkerPrivate, "UpdateLanguagesRunnable"), + : WorkerThreadRunnable("UpdateLanguagesRunnable"), mLanguages(aLanguages.Clone()) {} virtual bool WorkerRun(JSContext* aCx, @@ -707,9 +700,7 @@ class UpdateJSWorkerMemoryParameterRunnable final UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate, JSGCParamKey aKey, Maybe<uint32_t> aValue) - : WorkerControlRunnable(aWorkerPrivate, - "UpdateJSWorkerMemoryParameterRunnable", - WorkerThread), + : WorkerControlRunnable("UpdateJSWorkerMemoryParameterRunnable"), mValue(aValue), mKey(aKey) {} @@ -729,8 +720,7 @@ class UpdateGCZealRunnable final : public WorkerControlRunnable { public: UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal, uint32_t aFrequency) - : WorkerControlRunnable(aWorkerPrivate, "UpdateGCZealRunnable", - WorkerThread), + : WorkerControlRunnable("UpdateGCZealRunnable"), mGCZeal(aGCZeal), mFrequency(aFrequency) {} @@ -748,9 +738,7 @@ class SetLowMemoryStateRunnable final : public WorkerControlRunnable { public: SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState) - : WorkerControlRunnable(aWorkerPrivate, "SetLowMemoryStateRunnable", - WorkerThread), - mState(aState) {} + : WorkerControlRunnable("SetLowMemoryStateRunnable"), mState(aState) {} private: virtual bool WorkerRun(JSContext* aCx, @@ -767,8 +755,7 @@ class GarbageCollectRunnable final : public WorkerControlRunnable { public: GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking, bool aCollectChildren) - : WorkerControlRunnable(aWorkerPrivate, "GarbageCollectRunnable", - WorkerThread), + : WorkerControlRunnable("GarbageCollectRunnable"), mShrinking(aShrinking), mCollectChildren(aCollectChildren) {} @@ -802,8 +789,7 @@ class CycleCollectRunnable final : public WorkerControlRunnable { public: CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren) - : WorkerControlRunnable(aWorkerPrivate, "CycleCollectRunnable", - WorkerThread), + : WorkerControlRunnable("CycleCollectRunnable"), mCollectChildren(aCollectChildren) {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { @@ -812,10 +798,10 @@ class CycleCollectRunnable final : public WorkerControlRunnable { } }; -class OfflineStatusChangeRunnable final : public WorkerRunnable { +class OfflineStatusChangeRunnable final : public WorkerThreadRunnable { public: OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline) - : WorkerRunnable(aWorkerPrivate, "OfflineStatusChangeRunnable"), + : WorkerThreadRunnable("OfflineStatusChangeRunnable"), mIsOffline(aIsOffline) {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { @@ -830,8 +816,7 @@ class OfflineStatusChangeRunnable final : public WorkerRunnable { class MemoryPressureRunnable final : public WorkerControlRunnable { public: explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, "MemoryPressureRunnable", - WorkerThread) {} + : WorkerControlRunnable("MemoryPressureRunnable") {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { aWorkerPrivate->MemoryPressureInternal(); @@ -859,11 +844,10 @@ PRThread* PRThreadFromThread(nsIThread* aThread) { // 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 WorkerDebuggeeRunnable { +class CancelingOnParentRunnable final : public WorkerParentDebuggeeRunnable { public: explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerDebuggeeRunnable(aWorkerPrivate, "CancelingOnParentRunnable", - ParentThread) {} + : WorkerParentDebuggeeRunnable("CancelingOnParentRunnable") {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { aWorkerPrivate->Cancel(); @@ -873,12 +857,10 @@ class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable { // A runnable to cancel the worker from the parent process. class CancelingWithTimeoutOnParentRunnable final - : public WorkerControlRunnable { + : public WorkerParentControlRunnable { public: explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate) - : WorkerControlRunnable(aWorkerPrivate, - "CancelingWithTimeoutOnParentRunnable", - ParentThread) {} + : WorkerParentControlRunnable("CancelingWithTimeoutOnParentRunnable") {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { aWorkerPrivate->AssertIsOnParentThread(); @@ -926,7 +908,7 @@ class CancelingRunnable final : public Runnable { // Now we can cancel the this worker from the parent process. RefPtr<CancelingOnParentRunnable> r = new CancelingOnParentRunnable(workerPrivate); - r->Dispatch(); + r->Dispatch(workerPrivate); return NS_OK; } @@ -1252,7 +1234,7 @@ WorkerPrivate::MemoryReporter::CollectReports( aAnonymize, path); } - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(mWorkerPrivate)) { return NS_ERROR_UNEXPECTED; } @@ -1262,7 +1244,7 @@ WorkerPrivate::MemoryReporter::CollectReports( WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable( WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath) - : MainThreadWorkerControlRunnable(aWorkerPrivate), + : MainThreadWorkerControlRunnable("CollectReportsRunnable"), mFinishCollectRunnable(new FinishCollectRunnable( aHandleReport, aHandlerData, aAnonymize, aPath)), mAnonymize(aAnonymize) {} @@ -1559,8 +1541,48 @@ void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) { 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(std::move(aRunnable), aSyncLoopTarget, lock); + 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( @@ -1573,6 +1595,7 @@ nsresult WorkerPrivate::DispatchLockHeld( 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 " @@ -1592,7 +1615,10 @@ nsresult WorkerPrivate::DispatchLockHeld( ("WorkerPrivate::DispatchLockHeld [%p] runnable %p is queued in " "mPreStartRunnables", this, runnable.get())); - mPreStartRunnables.AppendElement(runnable); + RefPtr<WorkerThreadRunnable> workerThreadRunnable = + static_cast<WorkerThreadRunnable*>(runnable.get()); + workerThreadRunnable->mWorkerPrivateForPreStartCleaning = this; + mPreStartRunnables.AppendElement(workerThreadRunnable); return NS_OK; } @@ -1654,10 +1680,10 @@ void WorkerPrivate::DisableDebugger() { } nsresult WorkerPrivate::DispatchControlRunnable( - already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable) { + already_AddRefed<WorkerRunnable> aWorkerRunnable) { // May be called on any thread! - RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable); - MOZ_ASSERT(runnable); + RefPtr<WorkerRunnable> runnable(aWorkerRunnable); + MOZ_ASSERT_DEBUG_OR_FUZZING(runnable && runnable->IsControlRunnable()); LOG(WorkerLog(), ("WorkerPrivate::DispatchControlRunnable [%p] runnable %p", this, runnable.get())); @@ -1769,6 +1795,8 @@ bool WorkerPrivate::Notify(WorkerStatus aStatus) { mCancellationCallback = nullptr; } + mParentRef->DropWorkerPrivate(); + if (pending) { #ifdef DEBUG { @@ -1803,7 +1831,7 @@ bool WorkerPrivate::Notify(WorkerStatus aStatus) { } RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus); - return runnable->Dispatch(); + return runnable->Dispatch(this); } bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) { @@ -1841,7 +1869,7 @@ bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) { DisableDebugger(); RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this); - return runnable->Dispatch(); + return runnable->Dispatch(this); } bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) { @@ -1883,7 +1911,7 @@ bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) { EnableDebugger(); RefPtr<ThawRunnable> runnable = new ThawRunnable(this); - return runnable->Dispatch(); + return runnable->Dispatch(this); } void WorkerPrivate::ParentWindowPaused() { @@ -1945,7 +1973,33 @@ void WorkerPrivate::PropagateStorageAccessPermissionGranted() { RefPtr<PropagateStorageAccessPermissionGrantedRunnable> runnable = new PropagateStorageAccessPermissionGrantedRunnable(this); - Unused << NS_WARN_IF(!runnable->Dispatch()); + Unused << NS_WARN_IF(!runnable->Dispatch(this)); +} + +void WorkerPrivate::NotifyStorageKeyUsed() { + AssertIsOnWorkerThread(); + + // Only notify once per global. + if (hasNotifiedStorageKeyUsed) { + return; + } + hasNotifiedStorageKeyUsed = true; + + // Notify about storage access on the main thread. + RefPtr<StrongWorkerRef> strongRef = + StrongWorkerRef::Create(this, "WorkerPrivate::NotifyStorageKeyUsed"); + if (!strongRef) { + return; + } + RefPtr<ThreadSafeWorkerRef> ref = new ThreadSafeWorkerRef(strongRef); + DispatchToMainThread(NS_NewRunnableFunction( + "WorkerPrivate::NotifyStorageKeyUsed", [ref = std::move(ref)] { + nsGlobalWindowInner* window = + nsGlobalWindowInner::Cast(ref->Private()->GetAncestorWindow()); + if (window) { + window->MaybeNotifyStorageKeyUsed(); + } + })); } bool WorkerPrivate::Close() { @@ -1987,7 +2041,7 @@ void WorkerPrivate::UpdateContextOptions( RefPtr<UpdateContextOptionsRunnable> runnable = new UpdateContextOptionsRunnable(this, aContextOptions); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to update worker context options!"); } } @@ -1997,7 +2051,7 @@ void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) { RefPtr<UpdateLanguagesRunnable> runnable = new UpdateLanguagesRunnable(this, aLanguages); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to update worker languages!"); } } @@ -2016,7 +2070,7 @@ void WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey, if (changed) { RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable = new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to update memory parameter!"); } } @@ -2034,7 +2088,7 @@ void WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency) { RefPtr<UpdateGCZealRunnable> runnable = new UpdateGCZealRunnable(this, aGCZeal, aFrequency); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to update worker gczeal!"); } } @@ -2045,7 +2099,7 @@ void WorkerPrivate::SetLowMemoryState(bool aState) { RefPtr<SetLowMemoryStateRunnable> runnable = new SetLowMemoryStateRunnable(this, aState); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to set low memory state!"); } } @@ -2055,7 +2109,7 @@ void WorkerPrivate::GarbageCollect(bool aShrinking) { RefPtr<GarbageCollectRunnable> runnable = new GarbageCollectRunnable( this, aShrinking, /* aCollectChildren = */ true); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to GC worker!"); } } @@ -2065,7 +2119,7 @@ void WorkerPrivate::CycleCollect() { RefPtr<CycleCollectRunnable> runnable = new CycleCollectRunnable(this, /* aCollectChildren = */ true); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to CC worker!"); } } @@ -2075,7 +2129,7 @@ void WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline) { RefPtr<OfflineStatusChangeRunnable> runnable = new OfflineStatusChangeRunnable(this, aIsOffline); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { NS_WARNING("Failed to dispatch offline status change event!"); } } @@ -2118,7 +2172,7 @@ void WorkerPrivate::MemoryPressure() { AssertIsOnParentThread(); RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this); - Unused << NS_WARN_IF(!runnable->Dispatch()); + Unused << NS_WARN_IF(!runnable->Dispatch(this)); } RefPtr<WorkerPrivate::JSMemoryUsagePromise> WorkerPrivate::GetJSMemoryUsage() { @@ -2661,6 +2715,7 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor( // must keep ourself alive. We can now only be cleared by // ClearSelfAndParentEventTargetRef(). worker->mSelfRef = worker; + worker->mParentRef = MakeRefPtr<WorkerParentRef>(worker); worker->EnableDebugger(); @@ -2681,7 +2736,7 @@ already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor( RefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable( worker, std::move(stack), aScriptURL, aDocumentEncoding); - if (!compiler->Dispatch()) { + if (!compiler->Dispatch(worker)) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } @@ -3134,7 +3189,7 @@ void WorkerPrivate::RunLoopNeverRan() { // After mStatus is set to Dead there can be no more // WorkerControlRunnables so no need to lock here. if (!mControlQueue.IsEmpty()) { - WorkerControlRunnable* runnable = nullptr; + WorkerRunnable* runnable = nullptr; while (mControlQueue.Pop(runnable)) { runnable->Cancel(); runnable->Release(); @@ -3146,8 +3201,6 @@ void WorkerPrivate::RunLoopNeverRan() { // PerformanceStorageWorker which holds a WeakWorkerRef. // Notify WeakWorkerRefs with Dead status. NotifyWorkerRefs(Dead); - - ScheduleDeletion(WorkerPrivate::WorkerRan); } void WorkerPrivate::UnrootGlobalScopes() { @@ -3355,7 +3408,7 @@ void WorkerPrivate::DoRunLoop(JSContext* aCx) { ("WorkerPrivate::DoRunLoop [%p] dropping control runnables in " "Dead status", this)); - WorkerControlRunnable* runnable = nullptr; + WorkerRunnable* runnable = nullptr; while (mControlQueue.Pop(runnable)) { runnable->Cancel(); runnable->Release(); @@ -3530,8 +3583,11 @@ nsresult WorkerPrivate::DispatchToMainThread( } nsresult WorkerPrivate::DispatchDebuggeeToMainThread( - already_AddRefed<WorkerDebuggeeRunnable> aRunnable, uint32_t aFlags) { - return mMainThreadDebuggeeEventTarget->Dispatch(std::move(aRunnable), aFlags); + already_AddRefed<WorkerRunnable> aRunnable, uint32_t aFlags) { + RefPtr<WorkerRunnable> debuggeeRunnable = std::move(aRunnable); + MOZ_ASSERT_DEBUG_OR_FUZZING(debuggeeRunnable->IsDebuggeeRunnable()); + return mMainThreadDebuggeeEventTarget->Dispatch(debuggeeRunnable.forget(), + aFlags); } nsISerialEventTarget* WorkerPrivate::ControlEventTarget() { @@ -3915,7 +3971,7 @@ void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) { if (WorkerPrivate* parent = GetParent()) { RefPtr<WorkerFinishedRunnable> runnable = new WorkerFinishedRunnable(parent, this); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(parent)) { NS_WARNING("Failed to dispatch runnable!"); } } else { @@ -4028,7 +4084,7 @@ WorkerPrivate::ProcessAllControlRunnablesLocked() { auto result = ProcessAllControlRunnablesResult::Nothing; for (;;) { - WorkerControlRunnable* event; + WorkerRunnable* event; if (!mControlQueue.Pop(event)) { break; } @@ -4327,6 +4383,11 @@ void WorkerPrivate::RunShutdownTasks() { mWorkerHybridEventTarget->ForgetWorkerPrivate(this); } +RefPtr<WorkerParentRef> WorkerPrivate::GetWorkerParentRef() const { + RefPtr<WorkerParentRef> ref(mParentRef); + return ref; +} + void WorkerPrivate::AdjustNonblockingCCBackgroundActorCount(int32_t aCount) { AssertIsOnWorkerThread(); auto data = mWorkerThreadAccessible.Access(); @@ -4704,7 +4765,7 @@ void WorkerPrivate::DispatchCancelingRunnable() { this)); RefPtr<CancelingWithTimeoutOnParentRunnable> rr = new CancelingWithTimeoutOnParentRunnable(this); - rr->Dispatch(); + rr->Dispatch(this); } void WorkerPrivate::ReportUseCounters() { @@ -4848,8 +4909,8 @@ void WorkerPrivate::PostMessageToParent( return; } - RefPtr<MessageEventRunnable> runnable = - new MessageEventRunnable(this, WorkerRunnable::ParentThread); + RefPtr<MessageEventToParentRunnable> runnable = + new MessageEventToParentRunnable(this); JS::CloneDataPolicy clonePolicy; @@ -4866,7 +4927,7 @@ void WorkerPrivate::PostMessageToParent( return; } - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { aRv = NS_ERROR_FAILURE; } } @@ -4966,7 +5027,7 @@ void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, RefPtr<DebuggerImmediateRunnable> runnable = new DebuggerImmediateRunnable(this, aHandler); - if (!runnable->Dispatch()) { + if (!runnable->Dispatch(this)) { aRv.Throw(NS_ERROR_FAILURE); } } @@ -5711,10 +5772,11 @@ void WorkerPrivate::ResetWorkerPrivateInWorkerThread() { // Release the mutex before doomedThread. MutexAutoLock lock(mMutex); + MOZ_ASSERT(mStatus == Dead); MOZ_ASSERT(mThread); - mThread->SetWorker(WorkerThreadFriendKey{}, nullptr); + mThread->ClearEventQueueAndWorker(WorkerThreadFriendKey{}); mThread.swap(doomedThread); } @@ -6229,5 +6291,32 @@ WorkerPrivate::AutoPushEventLoopGlobal::~AutoPushEventLoopGlobal() { data->mCurrentEventLoopGlobal = std::move(mOldEventLoopGlobal); } +// ----------------------------------------------------------------------------- +// WorkerParentRef +WorkerParentRef::WorkerParentRef(RefPtr<WorkerPrivate>& aWorkerPrivate) + : mWorkerPrivate(aWorkerPrivate) { + LOGV(("WorkerParentRef::WorkerParentRef [%p] aWorkerPrivate %p", this, + aWorkerPrivate.get())); + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->AssertIsOnParentThread(); +} + +const RefPtr<WorkerPrivate>& WorkerParentRef::Private() const { + if (mWorkerPrivate) { + mWorkerPrivate->AssertIsOnParentThread(); + } + return mWorkerPrivate; +} + +void WorkerParentRef::DropWorkerPrivate() { + LOGV(("WorkerParentRef::DropWorkerPrivate [%p]", this)); + if (mWorkerPrivate) { + mWorkerPrivate->AssertIsOnParentThread(); + mWorkerPrivate = nullptr; + } +} + +WorkerParentRef::~WorkerParentRef() = default; + } // namespace dom } // namespace mozilla diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index ce754ba9f6..e9aa976bb3 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -85,6 +85,7 @@ class WorkerDebuggerGlobalScope; class WorkerErrorReport; class WorkerEventTarget; class WorkerGlobalScope; +class WorkerParentRef; class WorkerRef; class WorkerRunnable; class WorkerDebuggeeRunnable; @@ -590,7 +591,7 @@ class WorkerPrivate final uint32_t aFlags = NS_DISPATCH_NORMAL); nsresult DispatchDebuggeeToMainThread( - already_AddRefed<WorkerDebuggeeRunnable> aRunnable, + already_AddRefed<WorkerRunnable> aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL); // Get an event target that will dispatch runnables as control runnables on @@ -996,6 +997,8 @@ class WorkerPrivate final void PropagateStorageAccessPermissionGranted(); + void NotifyStorageKeyUsed(); + void EnableDebugger(); void DisableDebugger(); @@ -1048,11 +1051,13 @@ class WorkerPrivate final nsIEventTarget* aSyncLoopTarget = nullptr); nsresult DispatchControlRunnable( - already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable); + already_AddRefed<WorkerRunnable> aWorkerRunnable); nsresult DispatchDebuggerRunnable( already_AddRefed<WorkerRunnable> aDebuggerRunnable); + nsresult DispatchToParent(already_AddRefed<WorkerRunnable> aRunnable); + bool IsOnParentThread() const; #ifdef DEBUG @@ -1169,6 +1174,8 @@ class WorkerPrivate final // Worker thread only. void AdjustNonblockingCCBackgroundActorCount(int32_t aCount); + RefPtr<WorkerParentRef> GetWorkerParentRef() const; + private: WorkerPrivate( WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, @@ -1366,7 +1373,7 @@ class WorkerPrivate final WorkerDebugger* mDebugger; - workerinternals::Queue<WorkerControlRunnable*, 4> mControlQueue; + workerinternals::Queue<WorkerRunnable*, 4> mControlQueue; workerinternals::Queue<WorkerRunnable*, 4> mDebuggerQueue; // Touched on multiple threads, protected with mMutex. Only modified on the @@ -1618,6 +1625,12 @@ class WorkerPrivate final // The flag indicates if the worke is idle for events in the main event loop. bool mWorkerLoopIsIdle MOZ_GUARDED_BY(mMutex){false}; + + // This flag is used to ensure we only call NotifyStorageKeyUsed once per + // global. + bool hasNotifiedStorageKeyUsed{false}; + + RefPtr<WorkerParentRef> mParentRef; }; class AutoSyncLoopHolder { @@ -1658,6 +1671,42 @@ class AutoSyncLoopHolder { } }; +/** + * WorkerParentRef is a RefPtr<WorkerPrivate> wrapper for cross-thread access. + * WorkerPrivate needs to be accessed in multiple threads; for example, + * in WorkerParentThreadRunnable, the associated WorkerPrivate must be accessed + * in the worker thread when creating/dispatching and in the parent thread when + * executing. Unfortunately, RefPtr can not be used on this WorkerPrivate since + * it is not a thread-safe ref-counted object. + * + * Instead of using a raw pointer and a complicated mechanism to ensure the + * WorkerPrivate's accessibility. WorkerParentRef is used to resolve the + * problem. WorkerParentRef has a RefPtr<WorkerPrivate> mWorkerPrivate + * initialized on the parent thread when WorkerPrivate::Constructor(). + * WorkerParentRef is a thread-safe ref-counted object that can be copied at + * any thread by WorkerPrivate::GetWorkerParentRef() and propagated to other + * threads. In the target thread, call WorkerParentRef::Private() to get the + * reference for WorkerPrivate or get a nullptr if the Worker has shut down. + * + * Since currently usage cases, WorkerParentRef::Private() will assert to be on + * the parent thread. + */ +class WorkerParentRef final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerParentRef); + + explicit WorkerParentRef(RefPtr<WorkerPrivate>& aWorkerPrivate); + + const RefPtr<WorkerPrivate>& Private() const; + + void DropWorkerPrivate(); + + private: + ~WorkerParentRef(); + + RefPtr<WorkerPrivate> mWorkerPrivate; +}; + } // namespace dom } // namespace mozilla diff --git a/dom/workers/WorkerRef.cpp b/dom/workers/WorkerRef.cpp index 9ba9841041..7935a7c67f 100644 --- a/dom/workers/WorkerRef.cpp +++ b/dom/workers/WorkerRef.cpp @@ -20,8 +20,7 @@ class ReleaseRefControlRunnable final : public WorkerControlRunnable { public: ReleaseRefControlRunnable(WorkerPrivate* aWorkerPrivate, already_AddRefed<StrongWorkerRef> aRef) - : WorkerControlRunnable(aWorkerPrivate, "ReleaseRefControlRunnable", - WorkerThread), + : WorkerControlRunnable("ReleaseRefControlRunnable"), mRef(std::move(aRef)) { MOZ_ASSERT(mRef); } @@ -211,7 +210,7 @@ ThreadSafeWorkerRef::~ThreadSafeWorkerRef() { WorkerPrivate* workerPrivate = mRef->mWorkerPrivate; RefPtr<ReleaseRefControlRunnable> r = new ReleaseRefControlRunnable(workerPrivate, mRef.forget()); - r->Dispatch(); + r->Dispatch(workerPrivate); return; } } diff --git a/dom/workers/WorkerRef.h b/dom/workers/WorkerRef.h index e41ef07bfd..f1dc4deb1b 100644 --- a/dom/workers/WorkerRef.h +++ b/dom/workers/WorkerRef.h @@ -174,6 +174,8 @@ class WeakWorkerRef final : public WorkerRef { WorkerPrivate* GetUnsafePrivate() const; private: + friend class ThreadSafeWeakWorkerRef; + explicit WeakWorkerRef(WorkerPrivate* aWorkerPrivate); ~WeakWorkerRef(); diff --git a/dom/workers/WorkerRunnable.cpp b/dom/workers/WorkerRunnable.cpp index 14a3e5e3f9..05831c7cef 100644 --- a/dom/workers/WorkerRunnable.cpp +++ b/dom/workers/WorkerRunnable.cpp @@ -54,129 +54,17 @@ const nsIID kWorkerRunnableIID = { } // namespace #ifdef DEBUG -WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, const char* aName, - Target aTarget) - : mWorkerPrivate(aWorkerPrivate), - mTarget(aTarget), +WorkerRunnable::WorkerRunnable(const char* aName) # ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY - mName(aName), -# endif - mCallingCancelWithinRun(false) { - LOG(("WorkerRunnable::WorkerRunnable [%p]", this)); - MOZ_ASSERT(aWorkerPrivate); -} -#endif - -bool WorkerRunnable::IsDebuggerRunnable() const { return false; } - -nsIGlobalObject* WorkerRunnable::DefaultGlobalObject() const { - if (IsDebuggerRunnable()) { - return mWorkerPrivate->DebuggerGlobalScope(); - } else { - return mWorkerPrivate->GlobalScope(); - } -} - -bool WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) { -#ifdef DEBUG - MOZ_ASSERT(aWorkerPrivate); - - switch (mTarget) { - case ParentThread: - aWorkerPrivate->AssertIsOnWorkerThread(); - break; - - case WorkerThread: - aWorkerPrivate->AssertIsOnParentThread(); - break; - - default: - MOZ_ASSERT_UNREACHABLE("Unknown behavior!"); - } -#endif - return true; -} - -bool WorkerRunnable::Dispatch() { - bool ok = PreDispatch(mWorkerPrivate); - if (ok) { - ok = DispatchInternal(); - } - PostDispatch(mWorkerPrivate, ok); - return ok; + : mName(aName) { + LOG(("WorkerRunnable::WorkerRunnable [%p] (%s)", this, mName)); } - -bool WorkerRunnable::DispatchInternal() { - LOG(("WorkerRunnable::DispatchInternal [%p]", this)); - RefPtr<WorkerRunnable> runnable(this); - - if (mTarget == WorkerThread) { - if (IsDebuggerRunnable()) { - return NS_SUCCEEDED( - mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget())); - } else { - return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget())); - } - } - - MOZ_ASSERT(mTarget == ParentThread); - - if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { - return NS_SUCCEEDED(parent->Dispatch(runnable.forget())); - } - - if (IsDebuggeeRunnable()) { - RefPtr<WorkerDebuggeeRunnable> debuggeeRunnable = - runnable.forget().downcast<WorkerDebuggeeRunnable>(); - return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggeeToMainThread( - debuggeeRunnable.forget(), NS_DISPATCH_NORMAL)); - } - - return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget())); -} - -void WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, - bool aDispatchResult) { - MOZ_ASSERT(aWorkerPrivate); - -#ifdef DEBUG - switch (mTarget) { - case ParentThread: - aWorkerPrivate->AssertIsOnWorkerThread(); - break; - - case WorkerThread: - aWorkerPrivate->AssertIsOnParentThread(); - break; - - default: - MOZ_ASSERT_UNREACHABLE("Unknown behavior!"); - } -#endif +# else +{ + LOG(("WorkerRunnable::WorkerRunnable [%p]", this)); } - -bool WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate) { return true; } - -void WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, - bool aRunResult) { - MOZ_ASSERT(aCx); - MOZ_ASSERT(aWorkerPrivate); - -#ifdef DEBUG - switch (mTarget) { - case ParentThread: - aWorkerPrivate->AssertIsOnParentThread(); - break; - - case WorkerThread: - aWorkerPrivate->AssertIsOnWorkerThread(); - break; - - default: - MOZ_ASSERT_UNREACHABLE("Unknown behavior!"); - } +# endif #endif -} // static WorkerRunnable* WorkerRunnable::FromRunnable(nsIRunnable* aRunnable) { @@ -193,6 +81,20 @@ WorkerRunnable* WorkerRunnable::FromRunnable(nsIRunnable* aRunnable) { return runnable; } +bool WorkerRunnable::Dispatch(WorkerPrivate* aWorkerPrivate) { + LOG(("WorkerRunnable::Dispatch [%p] aWorkerPrivate: %p", this, + aWorkerPrivate)); + MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); + bool ok = PreDispatch(aWorkerPrivate); + if (ok) { + ok = DispatchInternal(aWorkerPrivate); + } + PostDispatch(aWorkerPrivate, ok); + return ok; +} + +NS_IMETHODIMP WorkerRunnable::Run() { return NS_OK; } + NS_IMPL_ADDREF(WorkerRunnable) NS_IMPL_RELEASE(WorkerRunnable) @@ -221,79 +123,83 @@ NS_INTERFACE_MAP_BEGIN(WorkerRunnable) } else NS_INTERFACE_MAP_END -NS_IMETHODIMP -WorkerRunnable::Run() { - LOG(("WorkerRunnable::Run [%p]", this)); - bool targetIsWorkerThread = mTarget == WorkerThread; +WorkerParentThreadRunnable::WorkerParentThreadRunnable(const char* aName) + : WorkerRunnable(aName) { + LOG(("WorkerParentThreadRunnable::WorkerParentThreadRunnable [%p]", this)); +} +WorkerParentThreadRunnable::~WorkerParentThreadRunnable() = default; + +bool WorkerParentThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) { #ifdef DEBUG - if (targetIsWorkerThread) { - mWorkerPrivate->AssertIsOnWorkerThread(); - } else { - MOZ_ASSERT(mTarget == ParentThread); - mWorkerPrivate->AssertIsOnParentThread(); - } + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); #endif + return true; +} - if (targetIsWorkerThread && !mCallingCancelWithinRun && - mWorkerPrivate->CancelBeforeWorkerScopeConstructed()) { - mCallingCancelWithinRun = true; - Cancel(); - mCallingCancelWithinRun = false; +bool WorkerParentThreadRunnable::DispatchInternal( + WorkerPrivate* aWorkerPrivate) { + LOG(("WorkerParentThreadRunnable::DispatchInternal [%p]", this)); + mWorkerParentRef = aWorkerPrivate->GetWorkerParentRef(); + RefPtr<WorkerParentThreadRunnable> runnable(this); + return NS_SUCCEEDED(aWorkerPrivate->DispatchToParent(runnable.forget())); +} + +void WorkerParentThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) { +#ifdef DEBUG + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); +#endif +} + +bool WorkerParentThreadRunnable::PreRun(WorkerPrivate* aWorkerPrivate) { + return true; +} + +void WorkerParentThreadRunnable::PostRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate, + bool aRunResult) { + MOZ_ASSERT(aCx); +#ifdef DEBUG + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnParentThread(); +#endif +} + +NS_IMETHODIMP +WorkerParentThreadRunnable::Run() { + LOG(("WorkerParentThreadRunnable::Run [%p]", this)); + RefPtr<WorkerPrivate> workerPrivate; + MOZ_ASSERT(mWorkerParentRef); + workerPrivate = mWorkerParentRef->Private(); + if (!workerPrivate) { + NS_WARNING("Worker has already shut down!!!"); return NS_OK; } +#ifdef DEBUG + workerPrivate->AssertIsOnParentThread(); +#endif - bool result = PreRun(mWorkerPrivate); - if (!result) { - MOZ_ASSERT(targetIsWorkerThread, - "The only PreRun implementation that can fail is " - "ScriptExecutorRunnable"); - mWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(!JS_IsExceptionPending(mWorkerPrivate->GetJSContext())); - // We can't enter a useful realm on the JSContext here; just pass it - // in as-is. - PostRun(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false); - return NS_ERROR_FAILURE; - } + WorkerPrivate* parent = workerPrivate->GetParent(); + bool isOnMainThread = !parent; + bool result = PreRun(workerPrivate); + MOZ_ASSERT(result); + + LOG(("WorkerParentThreadRunnable::Run [%p] WorkerPrivate: %p, parent: %p", + this, workerPrivate.get(), parent)); // Track down the appropriate global, if any, to use for the AutoEntryScript. nsCOMPtr<nsIGlobalObject> globalObject; - bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent(); - MOZ_ASSERT(isMainThread == NS_IsMainThread()); - RefPtr<WorkerPrivate> kungFuDeathGrip; - if (targetIsWorkerThread) { - globalObject = mWorkerPrivate->GetCurrentEventLoopGlobal(); - if (!globalObject) { - globalObject = DefaultGlobalObject(); - // Our worker thread may not be in a good state here if there is no - // JSContext avaliable. The way this manifests itself is that - // globalObject ends up null (though it's not clear to me how we can be - // running runnables at all when DefaultGlobalObject() is returning - // false!) and then when we try to init the AutoJSAPI either - // CycleCollectedJSContext::Get() returns null or it has a null JSContext. - // In any case, we used to have a check for - // GetCurrentWorkerThreadJSContext() being non-null here and that seems to - // avoid the problem, so let's keep doing that check even if we don't need - // the JSContext here at all. - if (NS_WARN_IF(!globalObject && !GetCurrentWorkerThreadJSContext())) { - return NS_ERROR_FAILURE; - } - } - - // We may still not have a globalObject here: in the case of - // CompileScriptRunnable, we don't actually create the global object until - // we have the script data, which happens in a syncloop under - // CompileScriptRunnable::WorkerRun, so we can't assert that it got created - // in the PreRun call above. + if (isOnMainThread) { + MOZ_ASSERT(isOnMainThread == NS_IsMainThread()); + globalObject = nsGlobalWindowInner::Cast(workerPrivate->GetWindow()); } else { - kungFuDeathGrip = mWorkerPrivate; - if (isMainThread) { - globalObject = nsGlobalWindowInner::Cast(mWorkerPrivate->GetWindow()); - } else { - globalObject = mWorkerPrivate->GetParent()->GlobalScope(); - } + MOZ_ASSERT(parent == GetCurrentThreadWorkerPrivate()); + globalObject = parent->GlobalScope(); + MOZ_DIAGNOSTIC_ASSERT(globalObject); } - // We might run script as part of WorkerRun, so we need an AutoEntryScript. // This is part of the HTML spec for workers at: // http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker @@ -303,8 +209,9 @@ WorkerRunnable::Run() { Maybe<mozilla::dom::AutoEntryScript> aes; JSContext* cx; AutoJSAPI* jsapi; + if (globalObject) { - aes.emplace(globalObject, "Worker runnable", isMainThread); + aes.emplace(globalObject, "Worker parent thread runnable", isOnMainThread); jsapi = aes.ptr(); cx = aes->cx(); } else { @@ -315,7 +222,7 @@ WorkerRunnable::Run() { } // Note that we can't assert anything about - // mWorkerPrivate->ParentEventTargetRef()->GetWrapper() + // workerPrivate->ParentEventTargetRef()->GetWrapper() // existing, since it may in fact have been GCed (and we may be one of the // runnables cleaning up the worker as a result). @@ -325,17 +232,18 @@ WorkerRunnable::Run() { // definitely have a globalObject. If it _is_ the main thread, globalObject // can be null for workers started from JSMs or other non-window contexts, // sadly. - MOZ_ASSERT_IF(!targetIsWorkerThread && !isMainThread, - mWorkerPrivate->IsDedicatedWorker() && globalObject); + MOZ_ASSERT_IF(!isOnMainThread, + workerPrivate->IsDedicatedWorker() && globalObject); // If we're on the parent thread we might be in a null realm in the // situation described above when globalObject is null. Make sure to enter // the realm of the worker's reflector if there is one. There might // not be one if we're just starting to compile the script for this worker. Maybe<JSAutoRealm> ar; - if (!targetIsWorkerThread && mWorkerPrivate->IsDedicatedWorker() && - mWorkerPrivate->ParentEventTargetRef()->GetWrapper()) { - JSObject* wrapper = mWorkerPrivate->ParentEventTargetRef()->GetWrapper(); + if (workerPrivate->IsDedicatedWorker() && + workerPrivate->ParentEventTargetRef() && + workerPrivate->ParentEventTargetRef()->GetWrapper()) { + JSObject* wrapper = workerPrivate->ParentEventTargetRef()->GetWrapper(); // If we're on the parent thread and have a reflector and a globalObject, // then the realms of cx, globalObject, and the worker's reflector @@ -359,12 +267,9 @@ WorkerRunnable::Run() { } MOZ_ASSERT(!jsapi->HasException()); - result = WorkerRun(cx, mWorkerPrivate); + result = WorkerRun(cx, workerPrivate); jsapi->ReportException(); - // We can't even assert that this didn't create our global, since in the case - // of CompileScriptRunnable it _does_. - // It would be nice to avoid passing a JSContext to PostRun, but in the case // of ScriptExecutorRunnable we need to know the current compartment on the // JSContext (the one we set up based on the global returned from PreRun) so @@ -383,71 +288,226 @@ WorkerRunnable::Run() { // this point; in the one case in which we could do that // (CompileScriptRunnable) it actually doesn't matter which compartment we're // in for PostRun. - PostRun(cx, mWorkerPrivate, result); + PostRun(cx, workerPrivate, result); MOZ_ASSERT(!jsapi->HasException()); return result ? NS_OK : NS_ERROR_FAILURE; } -nsresult WorkerRunnable::Cancel() { - LOG(("WorkerRunnable::Cancel [%p]", this)); +nsresult WorkerParentThreadRunnable::Cancel() { + LOG(("WorkerParentThreadRunnable::Cancel [%p]", this)); return NS_OK; } -void WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, - bool aDispatchResult) {} +WorkerParentControlRunnable::WorkerParentControlRunnable(const char* aName) + : WorkerParentThreadRunnable(aName) {} -WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, - nsIEventTarget* aSyncLoopTarget, - const char* aName) - : WorkerRunnable(aWorkerPrivate, aName, WorkerThread), - mSyncLoopTarget(aSyncLoopTarget) { -#ifdef DEBUG - if (mSyncLoopTarget) { - mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); +WorkerParentControlRunnable::~WorkerParentControlRunnable() = default; + +nsresult WorkerParentControlRunnable::Cancel() { + LOG(("WorkerParentControlRunnable::Cancel [%p]", this)); + if (NS_FAILED(Run())) { + NS_WARNING("WorkerParentControlRunnable::Run() failed."); + } + return NS_OK; +} + +WorkerThreadRunnable::WorkerThreadRunnable(const char* aName) + : WorkerRunnable(aName), mCallingCancelWithinRun(false) { + LOG(("WorkerThreadRunnable::WorkerThreadRunnable [%p]", this)); +} + +nsIGlobalObject* WorkerThreadRunnable::DefaultGlobalObject( + WorkerPrivate* aWorkerPrivate) const { + MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); + if (IsDebuggerRunnable()) { + return aWorkerPrivate->DebuggerGlobalScope(); } + return aWorkerPrivate->GlobalScope(); +} + +bool WorkerThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) { + MOZ_ASSERT(aWorkerPrivate); +#ifdef DEBUG + aWorkerPrivate->AssertIsOnParentThread(); #endif + return true; } -WorkerSyncRunnable::WorkerSyncRunnable( - WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, - const char* aName) - : WorkerRunnable(aWorkerPrivate, aName, WorkerThread), - mSyncLoopTarget(std::move(aSyncLoopTarget)) { +bool WorkerThreadRunnable::DispatchInternal(WorkerPrivate* aWorkerPrivate) { + LOG(("WorkerThreadRunnable::DispatchInternal [%p]", this)); + RefPtr<WorkerThreadRunnable> runnable(this); + return NS_SUCCEEDED(aWorkerPrivate->Dispatch(runnable.forget())); +} + +void WorkerThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) { + MOZ_ASSERT(aWorkerPrivate); #ifdef DEBUG - if (mSyncLoopTarget) { - mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); + aWorkerPrivate->AssertIsOnParentThread(); +#endif +} + +bool WorkerThreadRunnable::PreRun(WorkerPrivate* aWorkerPrivate) { + return true; +} + +void WorkerThreadRunnable::PostRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate, + bool aRunResult) { + MOZ_ASSERT(aCx); + MOZ_ASSERT(aWorkerPrivate); + +#ifdef DEBUG + aWorkerPrivate->AssertIsOnWorkerThread(); +#endif +} + +NS_IMETHODIMP +WorkerThreadRunnable::Run() { + LOG(("WorkerThreadRunnable::Run [%p]", this)); + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + if (!workerPrivate && mWorkerPrivateForPreStartCleaning) { + workerPrivate = mWorkerPrivateForPreStartCleaning; + mWorkerPrivateForPreStartCleaning = nullptr; } + MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate); +#ifdef DEBUG + workerPrivate->AssertIsOnWorkerThread(); #endif + + if (!mCallingCancelWithinRun && + workerPrivate->CancelBeforeWorkerScopeConstructed()) { + mCallingCancelWithinRun = true; + Cancel(); + mCallingCancelWithinRun = false; + return NS_OK; + } + + bool result = PreRun(workerPrivate); + if (!result) { + workerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(!JS_IsExceptionPending(workerPrivate->GetJSContext())); + // We can't enter a useful realm on the JSContext here; just pass it + // in as-is. + PostRun(workerPrivate->GetJSContext(), workerPrivate, false); + return NS_ERROR_FAILURE; + } + + // Track down the appropriate global, if any, to use for the AutoEntryScript. + nsCOMPtr<nsIGlobalObject> globalObject = + workerPrivate->GetCurrentEventLoopGlobal(); + if (!globalObject) { + globalObject = DefaultGlobalObject(workerPrivate); + // Our worker thread may not be in a good state here if there is no + // JSContext avaliable. The way this manifests itself is that + // globalObject ends up null (though it's not clear to me how we can be + // running runnables at all when default globalObject(DebuggerGlobalScope + // for debugger runnable, and GlobalScope for normal runnables) is returning + // false!) and then when we try to init the AutoJSAPI either + // CycleCollectedJSContext::Get() returns null or it has a null JSContext. + // In any case, we used to have a check for + // GetCurrentWorkerThreadJSContext() being non-null here and that seems to + // avoid the problem, so let's keep doing that check even if we don't need + // the JSContext here at all. + if (NS_WARN_IF(!globalObject && !GetCurrentWorkerThreadJSContext())) { + return NS_ERROR_FAILURE; + } + } + + // We might run script as part of WorkerRun, so we need an AutoEntryScript. + // This is part of the HTML spec for workers at: + // http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker + // If we don't have a globalObject we have to use an AutoJSAPI instead, but + // this is OK as we won't be running script in these circumstances. + Maybe<mozilla::dom::AutoJSAPI> maybeJSAPI; + Maybe<mozilla::dom::AutoEntryScript> aes; + JSContext* cx; + AutoJSAPI* jsapi; + if (globalObject) { + aes.emplace(globalObject, "Worker runnable", false); + jsapi = aes.ptr(); + cx = aes->cx(); + } else { + maybeJSAPI.emplace(); + maybeJSAPI->Init(); + jsapi = maybeJSAPI.ptr(); + cx = jsapi->cx(); + } + + MOZ_ASSERT(!jsapi->HasException()); + result = WorkerRun(cx, workerPrivate); + jsapi->ReportException(); + + // We can't even assert that this didn't create our global, since in the case + // of CompileScriptRunnable it _does_. + + // It would be nice to avoid passing a JSContext to PostRun, but in the case + // of ScriptExecutorRunnable we need to know the current compartment on the + // JSContext (the one we set up based on the global returned from PreRun) so + // that we can sanely do exception reporting. In particular, we want to make + // sure that we do our JS_SetPendingException while still in that compartment, + // because otherwise we might end up trying to create a cross-compartment + // wrapper when we try to move the JS exception from our runnable's + // ErrorResult to the JSContext, and that's not desirable in this case. + // + // We _could_ skip passing a JSContext here and then in + // ScriptExecutorRunnable::PostRun end up grabbing it from the WorkerPrivate + // and looking at its current compartment. But that seems like slightly weird + // action-at-a-distance... + // + // In any case, we do NOT try to change the compartment on the JSContext at + // this point; in the one case in which we could do that + // (CompileScriptRunnable) it actually doesn't matter which compartment we're + // in for PostRun. + PostRun(cx, workerPrivate, result); + MOZ_ASSERT(!jsapi->HasException()); + + return result ? NS_OK : NS_ERROR_FAILURE; } +nsresult WorkerThreadRunnable::Cancel() { + LOG(("WorkerThreadRunnable::Cancel [%p]", this)); + return NS_OK; +} + +void WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) {} + +WorkerSyncRunnable::WorkerSyncRunnable(nsIEventTarget* aSyncLoopTarget, + const char* aName) + : WorkerThreadRunnable(aName), mSyncLoopTarget(aSyncLoopTarget) {} + +WorkerSyncRunnable::WorkerSyncRunnable( + nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, const char* aName) + : WorkerThreadRunnable(aName), + mSyncLoopTarget(std::move(aSyncLoopTarget)) {} + WorkerSyncRunnable::~WorkerSyncRunnable() = default; -bool WorkerSyncRunnable::DispatchInternal() { +bool WorkerSyncRunnable::DispatchInternal(WorkerPrivate* aWorkerPrivate) { if (mSyncLoopTarget) { +#ifdef DEBUG + aWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); +#endif RefPtr<WorkerSyncRunnable> runnable(this); return NS_SUCCEEDED( mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); } - return WorkerRunnable::DispatchInternal(); + return WorkerThreadRunnable::DispatchInternal(aWorkerPrivate); } void MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) {} MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable( - WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, - nsresult aResult) - : WorkerSyncRunnable(aWorkerPrivate, std::move(aSyncLoopTarget)), - mResult(aResult) { + nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, nsresult aResult) + : WorkerSyncRunnable(std::move(aSyncLoopTarget)), mResult(aResult) { LOG(("MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable [%p]", this)); AssertIsOnMainThread(); -#ifdef DEBUG - mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); -#endif } nsresult MainThreadStopSyncLoopRunnable::Cancel() { @@ -470,9 +530,12 @@ bool MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx, return true; } -bool MainThreadStopSyncLoopRunnable::DispatchInternal() { +bool MainThreadStopSyncLoopRunnable::DispatchInternal( + WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(mSyncLoopTarget); - +#ifdef DEBUG + aWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); +#endif RefPtr<MainThreadStopSyncLoopRunnable> runnable(this); return NS_SUCCEEDED( mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL)); @@ -481,13 +544,8 @@ bool MainThreadStopSyncLoopRunnable::DispatchInternal() { void MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) {} -#ifdef DEBUG -WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, - const char* aName, Target aTarget) - : WorkerRunnable(aWorkerPrivate, aName, aTarget) { - MOZ_ASSERT(aWorkerPrivate); -} -#endif +WorkerControlRunnable::WorkerControlRunnable(const char* aName) + : WorkerThreadRunnable(aName) {} nsresult WorkerControlRunnable::Cancel() { LOG(("WorkerControlRunnable::Cancel [%p]", this)); @@ -498,21 +556,6 @@ nsresult WorkerControlRunnable::Cancel() { return NS_OK; } -bool WorkerControlRunnable::DispatchInternal() { - RefPtr<WorkerControlRunnable> runnable(this); - - if (mTarget == WorkerThread) { - return NS_SUCCEEDED( - mWorkerPrivate->DispatchControlRunnable(runnable.forget())); - } - - if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { - return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget())); - } - - return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget())); -} - WorkerMainThreadRunnable::WorkerMainThreadRunnable( WorkerPrivate* aWorkerPrivate, const nsACString& aTelemetryKey) : mozilla::Runnable("dom::WorkerMainThreadRunnable"), @@ -570,11 +613,10 @@ WorkerMainThreadRunnable::Run() { bool runResult = MainThreadRun(); RefPtr<MainThreadStopSyncLoopRunnable> response = - new MainThreadStopSyncLoopRunnable(mWorkerPrivate, - std::move(mSyncLoopTarget), + new MainThreadStopSyncLoopRunnable(std::move(mSyncLoopTarget), runResult ? NS_OK : NS_ERROR_FAILURE); - MOZ_ALWAYS_TRUE(response->Dispatch()); + MOZ_ALWAYS_TRUE(response->Dispatch(mWorkerPrivate)); return NS_OK; } @@ -633,15 +675,15 @@ void WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread() { RefPtr<WorkerProxyToMainThreadRunnable> mRunnable; public: - ReleaseRunnable(WorkerPrivate* aWorkerPrivate, - WorkerProxyToMainThreadRunnable* aRunnable) - : MainThreadWorkerControlRunnable(aWorkerPrivate), + explicit ReleaseRunnable(WorkerProxyToMainThreadRunnable* aRunnable) + : MainThreadWorkerControlRunnable("ReleaseRunnable"), mRunnable(aRunnable) { MOZ_ASSERT(aRunnable); } virtual nsresult Cancel() override { - Unused << WorkerRun(nullptr, mWorkerPrivate); + MOZ_ASSERT(GetCurrentThreadWorkerPrivate()); + Unused << WorkerRun(nullptr, GetCurrentThreadWorkerPrivate()); return NS_OK; } @@ -665,25 +707,10 @@ void WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread() { ~ReleaseRunnable() = default; }; - RefPtr<WorkerControlRunnable> runnable = - new ReleaseRunnable(mWorkerRef->Private(), this); - Unused << NS_WARN_IF(!runnable->Dispatch()); + RefPtr<WorkerControlRunnable> runnable = new ReleaseRunnable(this); + Unused << NS_WARN_IF(!runnable->Dispatch(mWorkerRef->Private())); } void WorkerProxyToMainThreadRunnable::ReleaseWorker() { mWorkerRef = nullptr; } -bool WorkerDebuggeeRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) { - if (mTarget == ParentThread) { - RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create( - aWorkerPrivate, "WorkerDebuggeeRunnable::mSender"); - if (!strongRef) { - return false; - } - - mSender = new ThreadSafeWorkerRef(strongRef); - } - - return WorkerRunnable::PreDispatch(aWorkerPrivate); -} - } // namespace mozilla::dom diff --git a/dom/workers/WorkerRunnable.h b/dom/workers/WorkerRunnable.h index d133f11ea2..d404e12d8d 100644 --- a/dom/workers/WorkerRunnable.h +++ b/dom/workers/WorkerRunnable.h @@ -12,8 +12,11 @@ #include "MainThreadUtils.h" #include "mozilla/Atomics.h" #include "mozilla/RefPtr.h" +#include "mozilla/ThreadSafeWeakPtr.h" +#include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/dom/WorkerStatus.h" +#include "mozilla/dom/quota/CheckedUnsafePtr.h" #include "nsCOMPtr.h" #include "nsIRunnable.h" #include "nsISupports.h" @@ -31,53 +34,30 @@ class ErrorResult; namespace dom { -class WorkerPrivate; +class Worker; -// Use this runnable to communicate from the worker to its parent or vice-versa. class WorkerRunnable : public nsIRunnable #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY , public nsINamed #endif { - public: - enum Target { - // Target the main thread for top-level workers, otherwise target the - // WorkerThread of the worker's parent. - ParentThread, - - // Target the thread where the worker event loop runs. - WorkerThread, - }; - protected: - // The WorkerPrivate that this runnable is associated with. - WorkerPrivate* mWorkerPrivate; - - // See above. - Target mTarget; - #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY const char* mName = nullptr; #endif - private: - // Whether or not Cancel() is currently being called from inside the Run() - // method. Avoids infinite recursion when a subclass calls Run() from inside - // Cancel(). Only checked and modified on the target thread. - bool mCallingCancelWithinRun; - public: NS_DECL_THREADSAFE_ISUPPORTS #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY NS_DECL_NSINAMED #endif - virtual nsresult Cancel(); + virtual nsresult Cancel() = 0; // The return value is true if and only if both PreDispatch and // DispatchInternal return true. - bool Dispatch(); + virtual bool Dispatch(WorkerPrivate* aWorkerPrivate); // True if this runnable is handled by running JavaScript in some global that // could possibly be a debuggee, and thus needs to be deferred when the target @@ -90,41 +70,43 @@ class WorkerRunnable : public nsIRunnable // support debugging the debugger server at the moment. virtual bool IsDebuggeeRunnable() const { return false; } + // True if this runnable needs to be dispatched to + // WorkerPrivate::mControlEventTareget. + virtual bool IsControlRunnable() const { return false; } + + // True if this runnable should be dispatched to the debugger queue, + // and false otherwise. + virtual bool IsDebuggerRunnable() const { return false; } + static WorkerRunnable* FromRunnable(nsIRunnable* aRunnable); protected: - WorkerRunnable(WorkerPrivate* aWorkerPrivate, - const char* aName = "WorkerRunnable", - Target aTarget = WorkerThread) + explicit WorkerRunnable(const char* aName = "WorkerRunnable") #ifdef DEBUG ; #else - : mWorkerPrivate(aWorkerPrivate), - mTarget(aTarget), # ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY - mName(aName), + : mName(aName) # endif - mCallingCancelWithinRun(false) { + { } #endif // This class is reference counted. virtual ~WorkerRunnable() = default; - // Returns true if this runnable should be dispatched to the debugger queue, - // and false otherwise. - virtual bool IsDebuggerRunnable() const; - - nsIGlobalObject* DefaultGlobalObject() const; + // Calling Run() directly is not supported. Just call Dispatch() and + // WorkerRun() will be called on the correct thread automatically. + NS_DECL_NSIRUNNABLE // By default asserts that Dispatch() is being called on the right thread - // (ParentThread if |mTarget| is WorkerThread). - virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate); + virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) = 0; // By default asserts that Dispatch() is being called on the right thread - // (ParentThread if |mTarget| is WorkerThread). virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, - bool aDispatchResult); + bool aDispatchResult) = 0; + + virtual bool DispatchInternal(WorkerPrivate* aWorkerPrivate) = 0; // May be implemented by subclasses if desired if they need to do some sort of // setup before we try to set up our JSContext and compartment for real. @@ -133,17 +115,19 @@ class WorkerRunnable : public nsIRunnable // // If false is returned, WorkerRun will not be called at all. PostRun will // still be called, with false passed for aRunResult. - virtual bool PreRun(WorkerPrivate* aWorkerPrivate); + virtual bool PreRun(WorkerPrivate* aWorkerPrivate) = 0; // Must be implemented by subclasses. Called on the target thread. The return // value will be passed to PostRun(). The JSContext passed in here comes from - // an AutoJSAPI (or AutoEntryScript) that we set up on the stack. If - // mTarget is ParentThread, it is in the compartment of + // an AutoJSAPI (or AutoEntryScript) that we set up on the stack. + // + // If the runnable is for parent thread, aCx is in the compartment of // mWorkerPrivate's reflector (i.e. the worker object in the parent thread), // unless that reflector is null, in which case it's in the compartment of the // parent global (which is the compartment reflector would have been in), or - // in the null compartment if there is no parent global. For other mTarget - // values, we're running on the worker thread and aCx is in whatever + // in the null compartment if there is no parent global. + // + // For runnables on the worker thread, aCx is in whatever // compartment GetCurrentWorkerThreadJSContext() was in when // nsIRunnable::Run() got called. This is actually important for cases when a // runnable spins a syncloop and wants everything that happens during the @@ -166,21 +150,138 @@ class WorkerRunnable : public nsIRunnable // exception on the JSContext and must not run script, because the incoming // JSContext may be in the null compartment. virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, - bool aRunResult); + bool aRunResult) = 0; +}; + +class WorkerParentThreadRunnable : public WorkerRunnable { + public: + NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerParentThreadRunnable, + WorkerRunnable) + + virtual nsresult Cancel() override; + + protected: + explicit WorkerParentThreadRunnable( + const char* aName = "WorkerParentThreadRunnable"); + + // This class is reference counted. + virtual ~WorkerParentThreadRunnable(); + + virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override; + + virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) override; + + virtual bool PreRun(WorkerPrivate* aWorkerPrivate) override; + + virtual bool WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) override = 0; + + virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + bool aRunResult) override; + + virtual bool DispatchInternal(WorkerPrivate* aWorkerPrivate) final; + + // Calling Run() directly is not supported. Just call Dispatch() and + // WorkerRun() will be called on the correct thread automatically. + NS_DECL_NSIRUNNABLE + + private: + RefPtr<WorkerParentRef> mWorkerParentRef; +}; + +class WorkerParentControlRunnable : public WorkerParentThreadRunnable { + friend class WorkerPrivate; + + protected: + explicit WorkerParentControlRunnable( + const char* aName = "WorkerParentControlRunnable"); + + virtual ~WorkerParentControlRunnable(); + + nsresult Cancel() override; + + public: + NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerParentControlRunnable, + WorkerParentThreadRunnable) + + private: + bool IsControlRunnable() const override { return true; } + + // Should only be called by WorkerPrivate::DoRunLoop. + using WorkerParentThreadRunnable::Cancel; +}; + +class WorkerParentDebuggeeRunnable : public WorkerParentThreadRunnable { + protected: + explicit WorkerParentDebuggeeRunnable( + const char* aName = "WorkerParentDebuggeeRunnable") + : WorkerParentThreadRunnable(aName) {} + + private: + // This override is deliberately private: it doesn't make sense to call it if + // we know statically that we are a WorkerDebuggeeRunnable. + bool IsDebuggeeRunnable() const override { return true; } +}; + +class WorkerThreadRunnable : public WorkerRunnable { + friend class WorkerPrivate; + + public: + NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadRunnable, WorkerRunnable) + + virtual nsresult Cancel() override; + + protected: + explicit WorkerThreadRunnable(const char* aName = "WorkerThreadRunnable"); + + // This class is reference counted. + virtual ~WorkerThreadRunnable() = default; + + nsIGlobalObject* DefaultGlobalObject(WorkerPrivate* aWorkerPrivate) const; + + virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override; + + virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) override; + + virtual bool PreRun(WorkerPrivate* aWorkerPrivate) override; + + virtual bool WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) override = 0; + + virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + bool aRunResult) override; - virtual bool DispatchInternal(); + virtual bool DispatchInternal(WorkerPrivate* aWorkerPrivate) override; // Calling Run() directly is not supported. Just call Dispatch() and // WorkerRun() will be called on the correct thread automatically. NS_DECL_NSIRUNNABLE + + // Whether or not Cancel() is currently being called from inside the Run() + // method. Avoids infinite recursion when a subclass calls Run() from inside + // Cancel(). Only checked and modified on the target thread. + bool mCallingCancelWithinRun; + + // If dispatching a WorkerThreadRunnable before Worker initialization complete + // in worker thread, which are in WorkerPrivate::mPreStartRunnables, when + // GetCurrentThreadWorkerPrivate() might get an invalid WorkerPrivate for + // WorkerThreadRunnable::Run() because it is in Worker's shutdown. + // + // This is specific for cleanup these pre-start runnables if the shutdown + // starts before Worker executes its event loop. + // This member is only set when the runnable is dispatched to + // WorkerPrivate::mPreStartRunnables. Any other cases to use this + // WorkerPrivate is always wrong. + CheckedUnsafePtr<WorkerPrivate> mWorkerPrivateForPreStartCleaning; }; // This runnable is used to send a message to a worker debugger. -class WorkerDebuggerRunnable : public WorkerRunnable { +class WorkerDebuggerRunnable : public WorkerThreadRunnable { protected: - explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate, - const char* aName = "WorkerDebuggerRunnable") - : WorkerRunnable(aWorkerPrivate, aName, WorkerThread) {} + explicit WorkerDebuggerRunnable(const char* aName = "WorkerDebuggerRunnable") + : WorkerThreadRunnable(aName) {} virtual ~WorkerDebuggerRunnable() = default; @@ -198,23 +299,21 @@ class WorkerDebuggerRunnable : public WorkerRunnable { }; // This runnable is used to send a message directly to a worker's sync loop. -class WorkerSyncRunnable : public WorkerRunnable { +class WorkerSyncRunnable : public WorkerThreadRunnable { protected: nsCOMPtr<nsIEventTarget> mSyncLoopTarget; // Passing null for aSyncLoopTarget is allowed and will result in the behavior - // of a normal WorkerRunnable. - WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, - nsIEventTarget* aSyncLoopTarget, - const char* aName = "WorkerSyncRunnable"); + // of a normal WorkerThreadRunnable. + explicit WorkerSyncRunnable(nsIEventTarget* aSyncLoopTarget, + const char* aName = "WorkerSyncRunnable"); - WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, - nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, - const char* aName = "WorkerSyncRunnable"); + explicit WorkerSyncRunnable(nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, + const char* aName = "WorkerSyncRunnable"); virtual ~WorkerSyncRunnable(); - virtual bool DispatchInternal() override; + virtual bool DispatchInternal(WorkerPrivate* aWorkerPrivate) override; }; // This runnable is identical to WorkerSyncRunnable except it is meant to be @@ -223,18 +322,18 @@ class WorkerSyncRunnable : public WorkerRunnable { class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable { protected: // Passing null for aSyncLoopTarget is allowed and will result in the behavior - // of a normal WorkerRunnable. - MainThreadWorkerSyncRunnable( - WorkerPrivate* aWorkerPrivate, nsIEventTarget* aSyncLoopTarget, + // of a normal WorkerThreadRunnable. + explicit MainThreadWorkerSyncRunnable( + nsIEventTarget* aSyncLoopTarget, const char* aName = "MainThreadWorkerSyncRunnable") - : WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget, aName) { + : WorkerSyncRunnable(aSyncLoopTarget, aName) { AssertIsOnMainThread(); } - MainThreadWorkerSyncRunnable( - WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, + explicit MainThreadWorkerSyncRunnable( + nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, const char* aName = "MainThreadWorkerSyncRunnable") - : WorkerSyncRunnable(aWorkerPrivate, std::move(aSyncLoopTarget), aName) { + : WorkerSyncRunnable(std::move(aSyncLoopTarget), aName) { AssertIsOnMainThread(); } @@ -254,42 +353,34 @@ class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable { // potentially running before previously queued runnables and perhaps even with // other JS code executing on the stack. These runnables must not alter the // state of the JS runtime and should only twiddle state values. -class WorkerControlRunnable : public WorkerRunnable { +class WorkerControlRunnable : public WorkerThreadRunnable { friend class WorkerPrivate; protected: - WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, - const char* aName = "WorkerControlRunnable", - Target aTarget = WorkerThread) -#ifdef DEBUG - ; -#else - : WorkerRunnable(aWorkerPrivate, aName, aTarget) { - } -#endif + explicit WorkerControlRunnable(const char* aName = "WorkerControlRunnable"); virtual ~WorkerControlRunnable() = default; nsresult Cancel() override; public: - NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerControlRunnable, WorkerRunnable) + NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerControlRunnable, + WorkerThreadRunnable) private: - virtual bool DispatchInternal() override; + bool IsControlRunnable() const override { return true; } // Should only be called by WorkerPrivate::DoRunLoop. - using WorkerRunnable::Cancel; + using WorkerThreadRunnable::Cancel; }; -// A convenience class for WorkerRunnables that are originated on the main +// A convenience class for WorkerThreadRunnables that are originated on the main // thread. -class MainThreadWorkerRunnable : public WorkerRunnable { +class MainThreadWorkerRunnable : public WorkerThreadRunnable { protected: explicit MainThreadWorkerRunnable( - WorkerPrivate* aWorkerPrivate, const char* aName = "MainThreadWorkerRunnable") - : WorkerRunnable(aWorkerPrivate, aName, WorkerThread) { + : WorkerThreadRunnable(aName) { AssertIsOnMainThread(); } @@ -311,9 +402,8 @@ class MainThreadWorkerRunnable : public WorkerRunnable { class MainThreadWorkerControlRunnable : public WorkerControlRunnable { protected: explicit MainThreadWorkerControlRunnable( - WorkerPrivate* aWorkerPrivate, const char* aName = "MainThreadWorkerControlRunnable") - : WorkerControlRunnable(aWorkerPrivate, aName, WorkerThread) {} + : WorkerControlRunnable(aName) {} virtual ~MainThreadWorkerControlRunnable() = default; @@ -328,17 +418,16 @@ class MainThreadWorkerControlRunnable : public WorkerControlRunnable { } }; -// A WorkerRunnable that should be dispatched from the worker to itself for -// async tasks. +// A WorkerThreadRunnable that should be dispatched from the worker to itself +// for async tasks. // // Async tasks will almost always want to use this since // a WorkerSameThreadRunnable keeps the Worker from being GCed. -class WorkerSameThreadRunnable : public WorkerRunnable { +class WorkerSameThreadRunnable : public WorkerThreadRunnable { protected: explicit WorkerSameThreadRunnable( - WorkerPrivate* aWorkerPrivate, const char* aName = "WorkerSameThreadRunnable") - : WorkerRunnable(aWorkerPrivate, aName, WorkerThread) {} + : WorkerThreadRunnable(aName) {} virtual ~WorkerSameThreadRunnable() = default; @@ -347,7 +436,7 @@ class WorkerSameThreadRunnable : public WorkerRunnable { virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override; - // We just delegate PostRun to WorkerRunnable, since it does exactly + // We just delegate PostRun to WorkerThreadRunnable, since it does exactly // what we want. }; @@ -424,8 +513,7 @@ class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable { public: // Passing null for aSyncLoopTarget is not allowed. - MainThreadStopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate, - nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, + MainThreadStopSyncLoopRunnable(nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget, nsresult aResult); // By default StopSyncLoopRunnables cannot be canceled since they could leave @@ -447,7 +535,7 @@ class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable { virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; - bool DispatchInternal() final; + bool DispatchInternal(WorkerPrivate* aWorkerPrivate) final; }; // Runnables handled by content JavaScript (MessageEventRunnable, JavaScript @@ -470,31 +558,15 @@ class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable { // from a top-level frozen worker to its parent window must not be delivered // either, even as the main thread event loop continues to spin. Thus, freezing // a top-level worker also pauses mMainThreadDebuggeeEventTarget. -class WorkerDebuggeeRunnable : public WorkerRunnable { +class WorkerDebuggeeRunnable : public WorkerThreadRunnable { protected: - WorkerDebuggeeRunnable(WorkerPrivate* aWorkerPrivate, - const char* aName = "WorkerDebuggeeRunnable", - Target aTarget = ParentThread) - : WorkerRunnable(aWorkerPrivate, aName, aTarget) {} - - bool PreDispatch(WorkerPrivate* aWorkerPrivate) override; + explicit WorkerDebuggeeRunnable(const char* aName = "WorkerDebuggeeRunnable") + : WorkerThreadRunnable(aName) {} private: // This override is deliberately private: it doesn't make sense to call it if // we know statically that we are a WorkerDebuggeeRunnable. bool IsDebuggeeRunnable() const override { return true; } - - // Runnables sent upwards, to the content window or parent worker, must keep - // their sender alive until they are delivered: they check back with the - // sender in case it has been terminated after having dispatched the runnable - // (in which case it should not be acted upon); and runnables sent to content - // wait until delivery to determine the target window, since - // WorkerPrivate::GetWindow may only be used on the main thread. - // - // Runnables sent downwards, from content to a worker or from a worker to a - // child, keep the sender alive because they are WorkerThread - // runnables, and so leave this null. - RefPtr<ThreadSafeWorkerRef> mSender; }; } // namespace dom diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 2121a99cb3..cbb61c7055 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -468,6 +468,7 @@ already_AddRefed<CacheStorage> WorkerGlobalScope::GetCaches(ErrorResult& aRv) { if (!mCacheStorage) { mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this, mWorkerPrivate, aRv); + mWorkerPrivate->NotifyStorageKeyUsed(); } RefPtr<CacheStorage> ref = mCacheStorage; @@ -735,25 +736,28 @@ already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB( if (!indexedDB) { StorageAccess access = mWorkerPrivate->StorageAccess(); + bool allowed = true; if (access == StorageAccess::eDeny) { NS_WARNING("IndexedDB is not allowed in this worker!"); - aErrorResult = NS_ERROR_DOM_SECURITY_ERR; - return nullptr; + allowed = false; } if (ShouldPartitionStorage(access) && !StoragePartitioningEnabled(access, mWorkerPrivate->CookieJarSettings())) { NS_WARNING("IndexedDB is not allowed in this worker!"); - aErrorResult = NS_ERROR_DOM_SECURITY_ERR; - return nullptr; + allowed = false; } - const PrincipalInfo& principalInfo = - mWorkerPrivate->GetEffectiveStoragePrincipalInfo(); + auto windowID = mWorkerPrivate->WindowID(); + + auto principalInfoPtr = + allowed ? MakeUnique<PrincipalInfo>( + mWorkerPrivate->GetEffectiveStoragePrincipalInfo()) + : nullptr; + auto res = IDBFactory::CreateForWorker(this, std::move(principalInfoPtr), + windowID); - auto res = IDBFactory::CreateForWorker(this, principalInfo, - mWorkerPrivate->WindowID()); if (NS_WARN_IF(res.isErr())) { aErrorResult = res.unwrapErr(); return nullptr; @@ -763,6 +767,8 @@ already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB( mIndexedDB = indexedDB; } + mWorkerPrivate->NotifyStorageKeyUsed(); + return indexedDB.forget(); } diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index 14d944e4d3..2b051c0440 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -117,50 +117,54 @@ SafeRefPtr<WorkerThread> WorkerThread::Create( void WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */, WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(PR_GetCurrentThread() == mThread); + MOZ_ASSERT(aWorkerPrivate); - if (aWorkerPrivate) { - { - MutexAutoLock lock(mLock); + { + MutexAutoLock lock(mLock); - MOZ_ASSERT(!mWorkerPrivate); - MOZ_ASSERT(mAcceptingNonWorkerRunnables); + MOZ_ASSERT(!mWorkerPrivate); + MOZ_ASSERT(mAcceptingNonWorkerRunnables); - mWorkerPrivate = aWorkerPrivate; + mWorkerPrivate = aWorkerPrivate; #ifdef DEBUG - mAcceptingNonWorkerRunnables = false; + mAcceptingNonWorkerRunnables = false; #endif - } + } - mObserver = new Observer(aWorkerPrivate); - MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver)); - } else { - MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver)); - mObserver = nullptr; + mObserver = new Observer(aWorkerPrivate); + MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver)); +} - { - MutexAutoLock lock(mLock); +void WorkerThread::ClearEventQueueAndWorker( + const WorkerThreadFriendKey& /* aKey */) { + MOZ_ASSERT(PR_GetCurrentThread() == mThread); - MOZ_ASSERT(mWorkerPrivate); - MOZ_ASSERT(!mAcceptingNonWorkerRunnables); - // mOtherThreadsDispatchingViaEventTarget can still be non-zero here - // because WorkerThread::Dispatch isn't atomic so a thread initiating - // dispatch can have dispatched a runnable at this thread allowing us to - // begin shutdown before that thread gets a chance to decrement - // mOtherThreadsDispatchingViaEventTarget back to 0. So we need to wait - // for that. - while (mOtherThreadsDispatchingViaEventTarget) { - mWorkerPrivateCondVar.Wait(); - } - // Need to clean up the dispatched runnables if - // mOtherThreadsDispatchingViaEventTarget was non-zero. - if (NS_HasPendingEvents(nullptr)) { - NS_ProcessPendingEvents(nullptr); - } + MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver)); + mObserver = nullptr; + + { + MutexAutoLock lock(mLock); + + MOZ_ASSERT(mWorkerPrivate); + MOZ_ASSERT(!mAcceptingNonWorkerRunnables); + // mOtherThreadsDispatchingViaEventTarget can still be non-zero here + // because WorkerThread::Dispatch isn't atomic so a thread initiating + // dispatch can have dispatched a runnable at this thread allowing us to + // begin shutdown before that thread gets a chance to decrement + // mOtherThreadsDispatchingViaEventTarget back to 0. So we need to wait + // for that. + while (mOtherThreadsDispatchingViaEventTarget) { + mWorkerPrivateCondVar.Wait(); + } + // Need to clean up the dispatched runnables if + // mOtherThreadsDispatchingViaEventTarget was non-zero. + if (NS_HasPendingEvents(nullptr)) { + NS_ProcessPendingEvents(nullptr); + } #ifdef DEBUG - mAcceptingNonWorkerRunnables = true; + mAcceptingNonWorkerRunnables = true; #endif - mWorkerPrivate = nullptr; - } + mWorkerPrivate = nullptr; } } @@ -246,8 +250,13 @@ WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, WorkerPrivate* workerPrivate = nullptr; if (onWorkerThread) { + // If the mWorkerPrivate has already disconnected by + // WorkerPrivate::ResetWorkerPrivateInWorkerThread(), there is no chance + // that to execute this runnable. Return NS_ERROR_UNEXPECTED here. + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } // No need to lock here because it is only modified on this thread. - MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate->AssertIsOnWorkerThread(); workerPrivate = mWorkerPrivate; @@ -266,13 +275,7 @@ WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, } nsresult rv; - if (runnable && onWorkerThread) { - RefPtr<WorkerRunnable> workerRunnable = - workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); - rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL); - } else { - rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); - } + rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (!onWorkerThread && workerPrivate) { // We need to wake the worker thread if we're not already on the right diff --git a/dom/workers/WorkerThread.h b/dom/workers/WorkerThread.h index 06624f60c1..8da085b51b 100644 --- a/dom/workers/WorkerThread.h +++ b/dom/workers/WorkerThread.h @@ -25,9 +25,6 @@ namespace dom { class WorkerRunnable; class WorkerPrivate; -template <class> -class WorkerPrivateParent; - namespace workerinternals { class RuntimeService; } @@ -38,7 +35,6 @@ class RuntimeService; class WorkerThreadFriendKey { friend class workerinternals::RuntimeService; friend class WorkerPrivate; - friend class WorkerPrivateParent<WorkerPrivate>; WorkerThreadFriendKey(); ~WorkerThreadFriendKey(); @@ -76,6 +72,13 @@ class WorkerThread final : public nsThread { void SetWorker(const WorkerThreadFriendKey& aKey, WorkerPrivate* aWorkerPrivate); + // This method is used to decouple the connection with the WorkerPrivate which + // is set in SetWorker(). And it also clears all pending runnables on this + // WorkerThread. + // After decoupling, WorkerThreadRunnable can not run on this WorkerThread + // anymore, since WorkerPrivate is invalid. + void ClearEventQueueAndWorker(const WorkerThreadFriendKey& aKey); + nsresult DispatchPrimaryRunnable(const WorkerThreadFriendKey& aKey, already_AddRefed<nsIRunnable> aRunnable); diff --git a/dom/workers/remoteworkers/RemoteWorkerChild.cpp b/dom/workers/remoteworkers/RemoteWorkerChild.cpp index bf63c1729c..9a4ca60287 100644 --- a/dom/workers/remoteworkers/RemoteWorkerChild.cpp +++ b/dom/workers/remoteworkers/RemoteWorkerChild.cpp @@ -106,12 +106,12 @@ NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor) NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor) // Normal runnable because AddPortIdentifier() is going to exec JS code. -class MessagePortIdentifierRunnable final : public WorkerRunnable { +class MessagePortIdentifierRunnable final : public WorkerThreadRunnable { public: MessagePortIdentifierRunnable(WorkerPrivate* aWorkerPrivate, RemoteWorkerChild* aActor, const MessagePortIdentifier& aPortIdentifier) - : WorkerRunnable(aWorkerPrivate, "MessagePortIdentifierRunnable"), + : WorkerThreadRunnable("MessagePortIdentifierRunnable"), mActor(aActor), mPortIdentifier(aPortIdentifier) {} @@ -933,7 +933,7 @@ class RemoteWorkerChild::SharedWorkerOp : public RemoteWorkerChild::Op { new MessagePortIdentifierRunnable( workerPrivate, aOwner, mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier()); - if (NS_WARN_IF(!r->Dispatch())) { + if (NS_WARN_IF(!r->Dispatch(workerPrivate))) { aOwner->ErrorPropagationDispatch(NS_ERROR_FAILURE); } } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) { diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index c53c0b2b0f..c3535fadba 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -46,6 +46,7 @@ let wasmGlobalInterfaces = [ { name: "Function", insecureContext: true, nightly: true }, { name: "Exception", insecureContext: true }, { name: "Tag", insecureContext: true }, + { name: "JSTag", insecureContext: true, earlyBetaOrEarlier: true }, { name: "compile", insecureContext: true }, { name: "compileStreaming", insecureContext: true }, { name: "instantiate", insecureContext: true }, |