Bug 1328964 - part 4 - WorkletThread - second part draft
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 16 May 2017 12:21:40 -0400
changeset 697795 7a1e7727c7b6138859eaed51406e650a1cd1a0b8
parent 697794 9c83e858634113434f7b101aef24941d1c14dd98
child 697798 714d61181ccf44692f84a416325ab7f4af701434
push id89093
push userjbruaroey@mozilla.com
push dateTue, 14 Nov 2017 17:33:26 +0000
bugs1328964
milestone59.0a1
Bug 1328964 - part 4 - WorkletThread - second part MozReview-Commit-ID: HumXGy2mmH6
dom/base/nsContentUtils.cpp
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerRunnable.cpp
dom/workers/WorkerRunnable.h
dom/worklet/Worklet.cpp
dom/worklet/WorkletPrincipal.cpp
dom/worklet/WorkletThread.cpp
xpcom/base/CycleCollectedJSContext.h
--- 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
   {