Bug 1328964 - part 4 - WorkletThread - second part
MozReview-Commit-ID: HumXGy2mmH6
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6194,21 +6194,17 @@ nsContentUtils::GetCurrentJSContext()
JSContext *
nsContentUtils::GetCurrentJSContextForThread()
{
MOZ_ASSERT(IsInitialized());
if (MOZ_LIKELY(NS_IsMainThread())) {
return GetCurrentJSContext();
}
- if (WorkletThread::IsOnWorkletThread()) {
- return WorkletThread::Get()->GetJSContext();
- }
-
- return workers::GetCurrentThreadJSContext();
+ return CycleCollectedJSContext::Get()->GetWorkerWorkletSafeContext();
}
template<typename StringType, typename CharType>
void
_ASCIIToLowerInSitu(StringType& aStr)
{
CharType* iter = aStr.BeginWriting();
CharType* end = aStr.EndWriting();
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1139,17 +1139,17 @@ public:
{
RefPtr<nsIRunnable> runnable(aRunnable);
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(runnable);
std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
- JSContext* cx = GetCurrentThreadJSContext();
+ JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
NS_ASSERTION(cx, "This should never be null!");
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
NS_ASSERTION(global, "This should never be null!");
// On worker threads, if the current global is the worker global, we use the
// main promise micro task queue. Otherwise, the current global must be
// either the debugger global or a debugger sandbox, and we use the debugger
@@ -1160,16 +1160,25 @@ public:
MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
}
microTaskQueue->push(runnable.forget());
}
+ JSContext*
+ GetWorkerWorkletSafeContext() const override
+ {
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(Context());
+ MOZ_ASSERT(workerPrivate);
+
+ return workerPrivate->GetJSContext();
+ }
+
private:
WorkerPrivate* mWorkerPrivate;
};
class WorkerThreadPrimaryRunnable final : public Runnable
{
WorkerPrivate* mWorkerPrivate;
RefPtr<WorkerThread> mThread;
@@ -1422,26 +1431,16 @@ GetCurrentThreadWorkerPrivate()
}
bool
IsCurrentThreadRunningChromeWorker()
{
return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal();
}
-JSContext*
-GetCurrentThreadJSContext()
-{
- WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
- if (!wp) {
- return nullptr;
- }
- return wp->GetJSContext();
-}
-
JSObject*
GetCurrentThreadWorkerGlobal()
{
WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
if (!wp) {
return nullptr;
}
WorkerGlobalScope* scope = wp->GlobalScope();
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1612,19 +1612,16 @@ WorkerPrivate*
GetWorkerPrivateFromContext(JSContext* aCx);
WorkerPrivate*
GetCurrentThreadWorkerPrivate();
bool
IsCurrentThreadRunningChromeWorker();
-JSContext*
-GetCurrentThreadJSContext();
-
JSObject*
GetCurrentThreadWorkerGlobal();
class AutoSyncLoopHolder
{
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsIEventTarget> mTarget;
uint32_t mIndex;
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -278,17 +278,17 @@ WorkerRunnable::Run()
}
// 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) {
- JSContext* cx = GetCurrentThreadJSContext();
+ JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
if (NS_WARN_IF(!cx)) {
return NS_ERROR_FAILURE;
}
JSObject* global = JS::CurrentGlobalOrNull(cx);
if (global) {
globalObject = xpc::NativeGlobal(global);
} else {
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -139,23 +139,24 @@ protected:
// 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
// mBehavior is ParentThreadUnchangedBusyCount, it 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 mBehavior
// values, we're running on the worker thread and aCx is in whatever
- // compartment GetCurrentThreadJSContext() 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 syncloop to happen in
- // the compartment that runnable set up (which may, for example, be a debugger
- // sandbox compartment!). If aCx wasn't in a compartment to start with, aCx
- // will be in either the debugger global's compartment or the worker's
- // global's compartment depending on whether IsDebuggerRunnable() is true.
+ // compartment nsContentUtils::GetCurrentJSContextForThread() 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
+ // syncloop to happen in the compartment that runnable set up (which may, for
+ // example, be a debugger sandbox compartment!). If aCx wasn't in a
+ // compartment to start with, aCx will be in either the debugger global's
+ // compartment or the worker's global's compartment depending on whether
+ // IsDebuggerRunnable() is true.
//
// Immediately after WorkerRun returns, the caller will assert that either it
// returns false or there is no exception pending on aCx. Then it will report
// any pending exceptions on aCx.
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
// By default asserts that Run() (and WorkerRun()) were called on the correct
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -390,19 +390,16 @@ ExecutionRunnable::RunOnWorkletThread()
JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
JS::CompileOptions compileOptions(cx);
compileOptions.setIntroductionType("Worklet");
compileOptions.setFileAndLine(NS_ConvertUTF16toUTF8(mHandler->URL()).get(), 0);
compileOptions.setVersion(JSVERSION_DEFAULT);
compileOptions.setIsRunOnce(true);
-
- // We only need the setNoScriptRval bit when compiling off-thread here,
- // since otherwise nsJSUtils::EvaluateString will set it up for us.
compileOptions.setNoScriptRval(true);
JSAutoCompartment comp(cx, globalObj);
JS::Rooted<JS::Value> unused(cx);
if (!JS::Evaluate(cx, compileOptions, mBuffer, &unused)) {
ErrorResult error;
error.MightThrowJSException();
--- a/dom/worklet/WorkletPrincipal.cpp
+++ b/dom/worklet/WorkletPrincipal.cpp
@@ -9,17 +9,18 @@
#include "mozilla/Assertions.h"
namespace mozilla {
namespace dom {
namespace WorkletPrincipal {
struct WorkletPrincipal final : public JSPrincipals
{
- bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override {
+ bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override
+ {
MOZ_CRASH("WorkletPrincipal::write not implemented");
return false;
}
};
JSPrincipals*
GetWorkletPrincipal()
{
--- a/dom/worklet/WorkletThread.cpp
+++ b/dom/worklet/WorkletThread.cpp
@@ -208,31 +208,45 @@ public:
CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
}
void
DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable) override
{
RefPtr<nsIRunnable> runnable(aRunnable);
+#ifdef DEBUG
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(runnable);
WorkletThread* workletThread = WorkletThread::Get();
MOZ_ASSERT(workletThread);
JSContext* cx = workletThread->GetJSContext();
MOZ_ASSERT(cx);
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
MOZ_ASSERT(global);
+#endif
mPromiseMicroTaskQueue.push(runnable.forget());
}
+ JSContext*
+ GetWorkerWorkletSafeContext() const override
+ {
+ void* cxPrivate = JS_GetContextPrivate(Context());
+ MOZ_ASSERT(cxPrivate);
+
+ WorkletThread* workletThread =
+ static_cast<WorkletThreadContextPrivate*>(cxPrivate)->GetWorkletThread();
+
+ return workletThread->GetJSContext();
+ }
+
private:
RefPtr<WorkletThread> mWorkletThread;
};
// This is the first runnable to be dispatched. It calls the RunEventLoop() so
// basically everything happens into this runnable. The reason behind this
// approach is that, when the Worklet is terminated, it must not have any JS in
// stack, but, because we have CC, nsIThread creates an AutoNoJSAPI object by
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -143,16 +143,22 @@ public:
std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
JSContext* Context() const
{
MOZ_ASSERT(mJSContext);
return mJSContext;
}
+ virtual JSContext* GetWorkerWorkletSafeContext() const
+ {
+ MOZ_CRASH("This method should be override in Worklet and Worker code.");
+ return nullptr;
+ }
+
JS::RootingContext* RootingCx() const
{
MOZ_ASSERT(mJSContext);
return JS::RootingContext::get(mJSContext);
}
bool MicroTaskCheckpointDisabled() const
{