Bug 1328964 - part 3 - Console API exposed to worklets draft
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 16 May 2017 12:12:22 -0400
changeset 697793 e6201b8e4e26cbf1507e525756a31a05f00ebaa4
parent 697792 7955d507ed43a2190b4660029755975ef9cec028
child 697794 9c83e858634113434f7b101aef24941d1c14dd98
push id89093
push userjbruaroey@mozilla.com
push dateTue, 14 Nov 2017 17:33:26 +0000
bugs1328964
milestone59.0a1
Bug 1328964 - part 3 - Console API exposed to worklets MozReview-Commit-ID: G5E5OXydj3a
dom/console/Console.cpp
dom/console/Console.h
dom/worklet/Worklet.cpp
dom/worklet/Worklet.h
dom/worklet/WorkletGlobalScope.cpp
dom/worklet/WorkletThread.cpp
dom/worklet/WorkletThread.h
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -308,32 +308,387 @@ public:
   {
     JS_ClearPendingException(mCx);
   }
 
 private:
   JSContext* mCx;
 };
 
-class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
-                      , public StructuredCloneHolderBase
+// This base class must be extended for Worker and for Worklet.
+class ConsoleRunnable : public StructuredCloneHolderBase
 {
 public:
-  explicit ConsoleRunnable(Console* aConsole)
-    : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
-    , mConsole(aConsole)
-  {}
-
   virtual
   ~ConsoleRunnable()
   {
     // Clear the StructuredCloneHolderBase class.
     Clear();
   }
 
+protected:
+  JSObject*
+  CustomReadHandler(JSContext* aCx,
+                    JSStructuredCloneReader* aReader,
+                    uint32_t aTag,
+                    uint32_t aIndex) override
+  {
+    AssertIsOnMainThread();
+
+    if (aTag == CONSOLE_TAG_BLOB) {
+      MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
+
+      JS::Rooted<JS::Value> val(aCx);
+      {
+        RefPtr<Blob> blob =
+          Blob::Create(mClonedData.mParent,
+                       mClonedData.mBlobs.ElementAt(aIndex));
+        if (!ToJSValue(aCx, blob, &val)) {
+          return nullptr;
+        }
+      }
+
+      return &val.toObject();
+    }
+
+    MOZ_CRASH("No other tags are supported.");
+    return nullptr;
+  }
+
+  bool
+  CustomWriteHandler(JSContext* aCx,
+                     JSStructuredCloneWriter* aWriter,
+                     JS::Handle<JSObject*> aObj) override
+  {
+    RefPtr<Blob> blob;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
+        blob->Impl()->MayBeClonedToOtherThreads()) {
+      if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
+                                         mClonedData.mBlobs.Length()))) {
+        return false;
+      }
+
+      mClonedData.mBlobs.AppendElement(blob->Impl());
+      return true;
+    }
+
+    if (!JS_ObjectNotWritten(aWriter, aObj)) {
+      return false;
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
+    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+    if (NS_WARN_IF(!jsString)) {
+      return false;
+    }
+
+    if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  // Helper methods for CallData
+  bool
+  StoreConsoleData(JSContext* aCx, ConsoleCallData* aCallData)
+  {
+    ClearException ce(aCx);
+
+    JS::Rooted<JSObject*> arguments(aCx,
+      JS_NewArrayObject(aCx, aCallData->mCopiedArguments.Length()));
+    if (NS_WARN_IF(!arguments)) {
+      return false;
+    }
+
+    JS::Rooted<JS::Value> arg(aCx);
+    for (uint32_t i = 0; i < aCallData->mCopiedArguments.Length(); ++i) {
+      arg = aCallData->mCopiedArguments[i];
+      if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
+                                       JSPROP_ENUMERATE))) {
+        return false;
+      }
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
+
+    if (NS_WARN_IF(!Write(aCx, value))) {
+      return false;
+    }
+
+    aCallData->mStatus = ConsoleCallData::eInUse;
+    return true;
+  }
+
+  void
+  ProcessCallData(JSContext* aCx, Console* aConsole, ConsoleCallData* aCallData)
+  {
+    AssertIsOnMainThread();
+
+    ClearException ce(aCx);
+
+    JS::Rooted<JS::Value> argumentsValue(aCx);
+    if (!Read(aCx, &argumentsValue)) {
+      return;
+    }
+
+    MOZ_ASSERT(argumentsValue.isObject());
+
+    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
+
+    uint32_t length;
+    if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
+      return;
+    }
+
+    Sequence<JS::Value> values;
+    SequenceRooter<JS::Value> arguments(aCx, &values);
+
+    for (uint32_t i = 0; i < length; ++i) {
+      JS::Rooted<JS::Value> value(aCx);
+
+      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
+        return;
+      }
+
+      if (!values.AppendElement(value, fallible)) {
+        return;
+      }
+    }
+
+    MOZ_ASSERT(values.Length() == length);
+
+    aConsole->ProcessCallData(aCx, aCallData, values);
+  }
+
+  void
+  ReleaseCallData(Console* aConsole, ConsoleCallData* aCallData)
+  {
+    aConsole->AssertIsOnOwningThread();
+
+    if (aCallData->mStatus == ConsoleCallData::eToBeDeleted) {
+      aConsole->ReleaseCallData(aCallData);
+    } else {
+      MOZ_ASSERT(aCallData->mStatus == ConsoleCallData::eInUse);
+      aCallData->mStatus = ConsoleCallData::eUnused;
+    }
+  }
+
+  // Helper methods for Profile calls
+  bool
+  StoreProfileData(JSContext* aCx, const Sequence<JS::Value>& aArguments)
+  {
+    ClearException ce(aCx);
+
+    JS::Rooted<JSObject*> arguments(aCx,
+      JS_NewArrayObject(aCx, aArguments.Length()));
+    if (NS_WARN_IF(!arguments)) {
+      return false;
+    }
+
+    JS::Rooted<JS::Value> arg(aCx);
+    for (uint32_t i = 0; i < aArguments.Length(); ++i) {
+      arg = aArguments[i];
+      if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
+                                       JSPROP_ENUMERATE))) {
+        return false;
+      }
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
+
+    if (NS_WARN_IF(!Write(aCx, value))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  void
+  ProcessProfileData(JSContext* aCx, Console* aConsole,
+                     const nsAString& aAction)
+  {
+    AssertIsOnMainThread();
+
+    ClearException ce(aCx);
+
+    JS::Rooted<JS::Value> argumentsValue(aCx);
+    bool ok = Read(aCx, &argumentsValue);
+    mClonedData.mParent = nullptr;
+
+    if (!ok) {
+      return;
+    }
+
+    MOZ_ASSERT(argumentsValue.isObject());
+    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
+
+    uint32_t length;
+    if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
+      return;
+    }
+
+    Sequence<JS::Value> arguments;
+
+    for (uint32_t i = 0; i < length; ++i) {
+      JS::Rooted<JS::Value> value(aCx);
+
+      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
+        return;
+      }
+
+      if (!arguments.AppendElement(value, fallible)) {
+        return;
+      }
+    }
+
+    aConsole->ProfileMethodInternal(aCx, aAction, arguments);
+  }
+
+  ConsoleStructuredCloneData mClonedData;
+};
+
+class ConsoleWorkletRunnable : public Runnable
+                             , public ConsoleRunnable
+{
+protected:
+  explicit ConsoleWorkletRunnable(Console* aConsole)
+    : mConsole(aConsole)
+    , mWorkletThread(WorkletThread::Get())
+  {
+    WorkletThread::AssertIsOnWorkletThread();
+    MOZ_ASSERT(mWorkletThread);
+  }
+
+  virtual
+  ~ConsoleWorkletRunnable() = default;
+
+  NS_IMETHOD
+  Run() override
+  {
+    // This runnable is dispatched to main-thread first, then it goes back to
+    // worklet thread.
+    if (NS_IsMainThread()) {
+      RunOnMainThread();
+      RefPtr<ConsoleWorkletRunnable> runnable(this);
+      return mWorkletThread->DispatchRunnable(runnable.forget());
+    }
+
+    WorkletThread::AssertIsOnWorkletThread();
+
+    ReleaseData();
+    mConsole = nullptr;
+    return NS_OK;
+  }
+
+protected:
+  // This method is called in the main-thread.
+  virtual void
+  RunOnMainThread() = 0;
+
+  // This method is called in the owning thread of the Console object.
+  virtual void
+  ReleaseData() = 0;
+
+  // This must be released on the worker thread.
+  RefPtr<Console> mConsole;
+
+  RefPtr<WorkletThread> mWorkletThread;
+};
+
+// This runnable appends a CallData object into the Console queue running on
+// the main-thread.
+class ConsoleCallDataWorkletRunnable final : public ConsoleWorkletRunnable
+{
+public:
+  static already_AddRefed<ConsoleCallDataWorkletRunnable>
+  Create(Console* aConsole, ConsoleCallData* aConsoleData)
+  {
+    WorkletThread::AssertIsOnWorkletThread();
+
+    RefPtr<WorkletThread> workletThread = WorkletThread::Get();
+    MOZ_ASSERT(workletThread);
+
+    aConsoleData->SetIDs(workletThread->GetWorkletLoadInfo().OuterWindowID(),
+                         workletThread->GetWorkletLoadInfo().InnerWindowID());
+
+    RefPtr<ConsoleCallDataWorkletRunnable> runnable =
+      new ConsoleCallDataWorkletRunnable(aConsole, aConsoleData);
+
+    if (!runnable->StoreConsoleData(WorkletThread::Get()->GetJSContext(),
+                                    aConsoleData)) {
+      return nullptr;
+    }
+
+    return runnable.forget();
+  }
+
+private:
+  ConsoleCallDataWorkletRunnable(Console* aConsole,
+                                 ConsoleCallData* aCallData)
+    : ConsoleWorkletRunnable(aConsole)
+    , mCallData(aCallData)
+  {
+    WorkletThread::AssertIsOnWorkletThread();
+    MOZ_ASSERT(aCallData);
+    aCallData->AssertIsOnOwningThread();
+  }
+
+  ~ConsoleCallDataWorkletRunnable()
+  {
+    MOZ_ASSERT(!mCallData);
+  }
+
+  virtual void
+  RunOnMainThread() override
+  {
+    AutoSafeJSContext cx;
+
+    JSObject* sandbox =
+      mConsole->GetOrCreateSandbox(cx,
+                                   mWorkletThread->GetWorkletLoadInfo().Principal());
+    JS::Rooted<JSObject*> global(cx, sandbox);
+    if (NS_WARN_IF(!global)) {
+      return;
+    }
+
+    // The CreateSandbox call returns a proxy to the actual sandbox object. We
+    // don't need a proxy here.
+    global = js::UncheckedUnwrap(global);
+
+    JSAutoCompartment ac(cx, global);
+
+    // We don't need to set a parent object in mCallData bacause there are not
+    // DOM objects exposed to worklet.
+
+    ProcessCallData(cx, mConsole, mCallData);
+  }
+
+  virtual void
+  ReleaseData() override
+  {
+    ReleaseCallData(mConsole, mCallData);
+    mCallData = nullptr;
+  }
+
+  RefPtr<ConsoleCallData> mCallData;
+};
+
+class ConsoleWorkerRunnable : public WorkerProxyToMainThreadRunnable
+                            , public ConsoleRunnable
+{
+public:
+  explicit ConsoleWorkerRunnable(Console* aConsole)
+    : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
+    , mConsole(aConsole)
+  {}
+
+  virtual
+  ~ConsoleWorkerRunnable() = default;
+
   bool
   Dispatch(JSContext* aCx)
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (NS_WARN_IF(!PreDispatch(aCx))) {
       RunBackOnWorkerThreadForCleanup();
       return false;
@@ -437,133 +792,47 @@ protected:
   virtual void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) = 0;
 
   // This method is called in the owning thread of the Console object.
   virtual void
   ReleaseData() = 0;
 
-  virtual JSObject* CustomReadHandler(JSContext* aCx,
-                                      JSStructuredCloneReader* aReader,
-                                      uint32_t aTag,
-                                      uint32_t aIndex) override
-  {
-    AssertIsOnMainThread();
-
-    if (aTag == CONSOLE_TAG_BLOB) {
-      MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
-
-      JS::Rooted<JS::Value> val(aCx);
-      {
-        RefPtr<Blob> blob =
-          Blob::Create(mClonedData.mParent, mClonedData.mBlobs.ElementAt(aIndex));
-        if (!ToJSValue(aCx, blob, &val)) {
-          return nullptr;
-        }
-      }
-
-      return &val.toObject();
-    }
-
-    MOZ_CRASH("No other tags are supported.");
-    return nullptr;
-  }
-
-  virtual bool CustomWriteHandler(JSContext* aCx,
-                                  JSStructuredCloneWriter* aWriter,
-                                  JS::Handle<JSObject*> aObj) override
-  {
-    RefPtr<Blob> blob;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
-        blob->Impl()->MayBeClonedToOtherThreads()) {
-      if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
-                                         mClonedData.mBlobs.Length()))) {
-        return false;
-      }
-
-      mClonedData.mBlobs.AppendElement(blob->Impl());
-      return true;
-    }
-
-    if (!JS_ObjectNotWritten(aWriter, aObj)) {
-      return false;
-    }
-
-    JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
-    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
-    if (NS_WARN_IF(!jsString)) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
-      return false;
-    }
-
-    return true;
-  }
-
   // This must be released on the worker thread.
   RefPtr<Console> mConsole;
 
   ConsoleStructuredCloneData mClonedData;
 };
 
 // This runnable appends a CallData object into the Console queue running on
 // the main-thread.
-class ConsoleCallDataRunnable final : public ConsoleRunnable
+class ConsoleCallDataWorkerRunnable final : public ConsoleWorkerRunnable
 {
 public:
-  ConsoleCallDataRunnable(Console* aConsole,
-                          ConsoleCallData* aCallData)
-    : ConsoleRunnable(aConsole)
+  ConsoleCallDataWorkerRunnable(Console* aConsole,
+                                ConsoleCallData* aCallData)
+    : ConsoleWorkerRunnable(aConsole)
     , mCallData(aCallData)
   {
     MOZ_ASSERT(aCallData);
     mWorkerPrivate->AssertIsOnWorkerThread();
     mCallData->AssertIsOnOwningThread();
   }
 
 private:
-  ~ConsoleCallDataRunnable()
+  ~ConsoleCallDataWorkerRunnable()
   {
     MOZ_ASSERT(!mCallData);
   }
 
   bool
   PreDispatch(JSContext* aCx) override
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    mCallData->AssertIsOnOwningThread();
-
-    ClearException ce(aCx);
-
-    JS::Rooted<JSObject*> arguments(aCx,
-      JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
-    if (NS_WARN_IF(!arguments)) {
-      return false;
-    }
-
-    JS::Rooted<JS::Value> arg(aCx);
-    for (uint32_t i = 0; i < mCallData->mCopiedArguments.Length(); ++i) {
-      arg = mCallData->mCopiedArguments[i];
-      if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
-                                       JSPROP_ENUMERATE))) {
-        return false;
-      }
-    }
-
-    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
-
-    if (NS_WARN_IF(!Write(aCx, value))) {
-      return false;
-    }
-
-    mCallData->mStatus = ConsoleCallData::eInUse;
-    return true;
+    return StoreConsoleData(aCx, mCallData);
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
     AssertIsOnMainThread();
 
@@ -592,168 +861,126 @@ private:
       }
 
       mCallData->SetIDs(id, innerID);
     }
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
-    ProcessCallData(aCx);
+    ProcessCallData(aCx, mConsole, mCallData);
 
     mClonedData.mParent = nullptr;
   }
 
   virtual void
   ReleaseData() override
   {
-    mConsole->AssertIsOnOwningThread();
-
-    if (mCallData->mStatus == ConsoleCallData::eToBeDeleted) {
-      mConsole->ReleaseCallData(mCallData);
-    } else {
-      MOZ_ASSERT(mCallData->mStatus == ConsoleCallData::eInUse);
-      mCallData->mStatus = ConsoleCallData::eUnused;
-    }
-
+    ReleaseCallData(mConsole, mCallData);
     mCallData = nullptr;
   }
 
-  void
-  ProcessCallData(JSContext* aCx)
-  {
-    AssertIsOnMainThread();
-
-    ClearException ce(aCx);
-
-    JS::Rooted<JS::Value> argumentsValue(aCx);
-    if (!Read(aCx, &argumentsValue)) {
-      return;
-    }
-
-    MOZ_ASSERT(argumentsValue.isObject());
-
-    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
-
-    uint32_t length;
-    if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
-      return;
-    }
-
-    Sequence<JS::Value> values;
-    SequenceRooter<JS::Value> arguments(aCx, &values);
-
-    for (uint32_t i = 0; i < length; ++i) {
-      JS::Rooted<JS::Value> value(aCx);
-
-      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
-        return;
-      }
-
-      if (!values.AppendElement(value, fallible)) {
-        return;
-      }
-    }
-
-    MOZ_ASSERT(values.Length() == length);
-
-    mConsole->ProcessCallData(aCx, mCallData, values);
-  }
-
   RefPtr<ConsoleCallData> mCallData;
 };
 
 // This runnable calls ProfileMethod() on the console on the main-thread.
-class ConsoleProfileRunnable final : public ConsoleRunnable
+class ConsoleProfileWorkletRunnable final : public ConsoleWorkletRunnable
 {
 public:
-  ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
-                         const Sequence<JS::Value>& aArguments)
-    : ConsoleRunnable(aConsole)
+  static already_AddRefed<ConsoleProfileWorkletRunnable>
+  Create(Console* aConsole, const nsAString& aAction,
+         const Sequence<JS::Value>& aArguments)
+  {
+    WorkletThread::AssertIsOnWorkletThread();
+
+    RefPtr<ConsoleProfileWorkletRunnable> runnable =
+      new ConsoleProfileWorkletRunnable(aConsole, aAction);
+
+    if (!runnable->StoreProfileData(WorkletThread::Get()->GetJSContext(),
+                                    aArguments)) {
+      return nullptr;
+    }
+
+    return runnable.forget();
+  }
+
+private:
+  ConsoleProfileWorkletRunnable(Console* aConsole, const nsAString& aAction)
+    : ConsoleWorkletRunnable(aConsole)
+    , mAction(aAction)
+  {
+    MOZ_ASSERT(aConsole);
+  }
+
+  void
+  RunOnMainThread() override
+  {
+    AssertIsOnMainThread();
+
+    AutoSafeJSContext cx;
+
+    JSObject* sandbox =
+      mConsole->GetOrCreateSandbox(cx,
+                                   mWorkletThread->GetWorkletLoadInfo().Principal());
+    JS::Rooted<JSObject*> global(cx, sandbox);
+    if (NS_WARN_IF(!global)) {
+      return;
+    }
+
+    // The CreateSandbox call returns a proxy to the actual sandbox object. We
+    // don't need a proxy here.
+    global = js::UncheckedUnwrap(global);
+
+    JSAutoCompartment ac(cx, global);
+
+    // We don't need to set a parent object in mCallData bacause there are not
+    // DOM objects exposed to worklet.
+
+    ProcessProfileData(cx, mConsole, mAction);
+  }
+
+  virtual void
+  ReleaseData() override
+  {}
+
+  nsString mAction;
+};
+
+// This runnable calls ProfileMethod() on the console on the main-thread.
+class ConsoleProfileWorkerRunnable final : public ConsoleWorkerRunnable
+{
+public:
+  ConsoleProfileWorkerRunnable(Console* aConsole, const nsAString& aAction,
+                               const Sequence<JS::Value>& aArguments)
+    : ConsoleWorkerRunnable(aConsole)
     , mAction(aAction)
     , mArguments(aArguments)
   {
     MOZ_ASSERT(aConsole);
   }
 
 private:
   bool
   PreDispatch(JSContext* aCx) override
   {
-    ClearException ce(aCx);
-
-    JS::Rooted<JSObject*> arguments(aCx,
-      JS_NewArrayObject(aCx, mArguments.Length()));
-    if (NS_WARN_IF(!arguments)) {
-      return false;
-    }
-
-    JS::Rooted<JS::Value> arg(aCx);
-    for (uint32_t i = 0; i < mArguments.Length(); ++i) {
-      arg = mArguments[i];
-      if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
-                                       JSPROP_ENUMERATE))) {
-        return false;
-      }
-    }
-
-    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
-
-    if (NS_WARN_IF(!Write(aCx, value))) {
-      return false;
-    }
-
-    return true;
+    return StoreProfileData(aCx, mArguments);
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
     AssertIsOnMainThread();
 
-    ClearException ce(aCx);
-
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
-    JS::Rooted<JS::Value> argumentsValue(aCx);
-    bool ok = Read(aCx, &argumentsValue);
+    ProcessProfileData(aCx, mConsole, mAction);
+
     mClonedData.mParent = nullptr;
-
-    if (!ok) {
-      return;
-    }
-
-    MOZ_ASSERT(argumentsValue.isObject());
-    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
-    if (NS_WARN_IF(!argumentsObj)) {
-      return;
-    }
-
-    uint32_t length;
-    if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
-      return;
-    }
-
-    Sequence<JS::Value> arguments;
-
-    for (uint32_t i = 0; i < length; ++i) {
-      JS::Rooted<JS::Value> value(aCx);
-
-      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
-        return;
-      }
-
-      if (!arguments.AppendElement(value, fallible)) {
-        return;
-      }
-    }
-
-    mConsole->ProfileMethodInternal(aCx, mAction, arguments);
   }
 
   virtual void
   ReleaseData() override
   {}
 
   nsString mAction;
 
@@ -800,46 +1027,64 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
 /* static */ already_AddRefed<Console>
 Console::Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
 {
   MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
 
-  RefPtr<Console> console = new Console(aWindow);
+  uint64_t outerWindowID = 0;
+  uint64_t innerWindowID = 0;
+
+  if (aWindow) {
+    innerWindowID = aWindow->WindowID();
+
+    // Without outerwindow any console message coming from this object will not
+    // shown in the devtools webconsole. But this should be fine because
+    // probably we are shutting down, or the window is CCed/GCed.
+    nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
+    if (outerWindow) {
+      outerWindowID = outerWindow->WindowID();
+    }
+  }
+
+  RefPtr<Console> console = new Console(aWindow, outerWindowID, innerWindowID);
   console->Initialize(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return console.forget();
 }
 
-Console::Console(nsPIDOMWindowInner* aWindow)
+/* static */ already_AddRefed<Console>
+Console::CreateForWorklet(uint64_t aOuterWindowID, uint64_t aInnerWindowID,
+                          ErrorResult& aRv)
+{
+  WorkletThread::AssertIsOnWorkletThread();
+
+  RefPtr<Console> console =
+    new Console(nullptr, aOuterWindowID, aInnerWindowID);
+  console->Initialize(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return console.forget();
+}
+
+Console::Console(nsPIDOMWindowInner* aWindow, uint64_t aOuterWindowID,
+                 uint64_t aInnerWindowID)
   : mWindow(aWindow)
-  , mOuterID(0)
-  , mInnerID(0)
+  , mOuterID(aOuterWindowID)
+  , mInnerID(aInnerWindowID)
   , mStatus(eUnknown)
 {
   MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
-
-  if (mWindow) {
-    MOZ_ASSERT(mWindow->IsInnerWindow());
-    mInnerID = mWindow->WindowID();
-
-    // Without outerwindow any console message coming from this object will not
-    // shown in the devtools webconsole. But this should be fine because
-    // probably we are shutting down, or the window is CCed/GCed.
-    nsPIDOMWindowOuter* outerWindow = mWindow->GetOuterWindow();
-    if (outerWindow) {
-      mOuterID = outerWindow->WindowID();
-    }
-  }
-
   mozilla::HoldJSObjects(this);
 }
 
 Console::~Console()
 {
   AssertIsOnOwningThread();
   Shutdown();
   mozilla::DropJSObjects(this);
@@ -1056,20 +1301,31 @@ void
 Console::ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
                                const Sequence<JS::Value>& aData)
 {
   // Make all Console API no-op if DevTools aren't enabled.
   if (!nsContentUtils::DevToolsEnabled(aCx)) {
     return;
   }
 
+  if (WorkletThread::IsOnWorkletThread()) {
+    RefPtr<ConsoleProfileWorkletRunnable> runnable =
+      ConsoleProfileWorkletRunnable::Create(this, aAction, aData);
+    if (!runnable) {
+      return;
+    }
+
+    WorkletThread::Get()->DispatchRunnable(runnable.forget());
+    return;
+  }
+
   if (!NS_IsMainThread()) {
     // Here we are in a worker thread.
-    RefPtr<ConsoleProfileRunnable> runnable =
-      new ConsoleProfileRunnable(this, aAction, aData);
+    RefPtr<ConsoleProfileWorkerRunnable> runnable =
+      new ConsoleProfileWorkerRunnable(this, aAction, aData);
 
     runnable->Dispatch(aCx);
     return;
   }
 
   ClearException ce(aCx);
 
   RootedDictionary<ConsoleProfileEvent> event(aCx);
@@ -1221,17 +1477,19 @@ Console::MethodInternal(JSContext* aCx, 
 
   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
                                        aData, this))) {
     return;
   }
 
   OriginAttributes oa;
 
-  if (mWindow) {
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(mWindow);
+
     // Save the principal's OriginAttributes in the console event data
     // so that we will be able to filter messages by origin attributes.
     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
     if (NS_WARN_IF(!sop)) {
       return;
     }
 
     nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
@@ -1251,16 +1509,18 @@ Console::MethodInternal(JSContext* aCx, 
 
         bool pb;
         if (NS_SUCCEEDED(loadContext->GetUsePrivateBrowsing(&pb))) {
           MOZ_ASSERT(pb == !!oa.mPrivateBrowsingId);
         }
       }
     }
 #endif
+  } else if (WorkletThread::IsOnWorkletThread()) {
+    oa = WorkletThread::Get()->GetWorkletLoadInfo().OriginAttributesRef();
   } else {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     oa = workerPrivate->GetOriginAttributes();
   }
 
   callData->SetOriginAttributes(oa);
 
@@ -1343,16 +1603,19 @@ Console::MethodInternal(JSContext* aCx, 
           return;
         }
 
         timelines->AddMarkerForDocShell(docShell, Move(
           MakeUnique<ConsoleTimelineMarker>(
             key, aMethodName == MethodTime ? MarkerTracingType::START
                                            : MarkerTracingType::END)));
       }
+    } else if (WorkletThread::IsOnWorkletThread()) {
+      monotonicTimer =
+        WorkletThread::Get()->TimeStampToDOMHighRes(TimeStamp::Now());
     } else {
       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(workerPrivate);
 
       monotonicTimer = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now());
     }
   }
 
@@ -1383,21 +1646,32 @@ Console::MethodInternal(JSContext* aCx, 
 
     // Just because we don't want to expose
     // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
     // cleanup the mCallDataStorage:
     UnstoreCallData(callData);
     return;
   }
 
+  if (WorkletThread::IsOnWorkletThread()) {
+    RefPtr<ConsoleCallDataWorkletRunnable> runnable =
+      ConsoleCallDataWorkletRunnable::Create(this, callData);
+    if (!runnable) {
+      return;
+    }
+
+    NS_DispatchToMainThread(runnable);
+    return;
+  }
+
   // We do this only in workers for now.
   NotifyHandler(aCx, aData, callData);
 
-  RefPtr<ConsoleCallDataRunnable> runnable =
-    new ConsoleCallDataRunnable(this, callData);
+  RefPtr<ConsoleCallDataWorkerRunnable> runnable =
+    new ConsoleCallDataWorkerRunnable(this, callData);
   Unused << NS_WARN_IF(!runnable->Dispatch(aCx));
 }
 
 // We store information to lazily compute the stack in the reserved slots of
 // LazyStackGetter.  The first slot always stores a JS object: it's either the
 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
 // reified the stack yet, or an UndefinedValue() otherwise.
@@ -2283,17 +2557,18 @@ Console::StoreCallData(ConsoleCallData* 
 
   if (mCallDataStorage.Length() > STORAGE_MAX_EVENTS) {
     RefPtr<ConsoleCallData> callData = mCallDataStorage[0];
     mCallDataStorage.RemoveElementAt(0);
 
     MOZ_ASSERT(callData->mStatus != ConsoleCallData::eToBeDeleted);
 
     // We cannot delete this object now because we have to trace its JSValues
-    // until the pending operation (ConsoleCallDataRunnable) is completed.
+    // until the pending operation (ConsoleCallDataWorkerRunnable or
+    // ConsoleCallDataWorkletRunnable) is completed.
     if (callData->mStatus == ConsoleCallData::eInUse) {
       callData->mStatus = ConsoleCallData::eToBeDeleted;
       mCallDataStoragePending.AppendElement(callData);
     }
   }
 }
 
 void
--- a/dom/console/Console.h
+++ b/dom/console/Console.h
@@ -38,16 +38,20 @@ class Console final : public nsIObserver
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver)
   NS_DECL_NSIOBSERVER
 
   static already_AddRefed<Console>
   Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv);
 
+  static already_AddRefed<Console>
+  CreateForWorklet(uint64_t aOuterWindowID, uint64_t aInnerWindowID,
+                   ErrorResult& aRv);
+
   // WebIDL methods
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
   static void
   Log(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
@@ -119,17 +123,18 @@ public:
   void
   RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
                         ErrorResult& aRv);
 
   void
   SetConsoleEventHandler(AnyCallback* aHandler);
 
 private:
-  explicit Console(nsPIDOMWindowInner* aWindow);
+  Console(nsPIDOMWindowInner* aWindow, uint64_t aOuterWindowID,
+          uint64_t aInnerWIndowID);
   ~Console();
 
   void
   Initialize(ErrorResult& aRv);
 
   void
   Shutdown();
 
@@ -402,17 +407,19 @@ private:
 
   enum {
     eUnknown,
     eInitialized,
     eShuttingDown
   } mStatus;
 
   friend class ConsoleCallData;
+  friend class ConsoleCallDataWorkletRunnable;
+  friend class ConsoleProfileWorkerRunnable;
+  friend class ConsoleProfileWorkletRunnable;
   friend class ConsoleRunnable;
-  friend class ConsoleCallDataRunnable;
-  friend class ConsoleProfileRunnable;
+  friend class ConsoleWorkerRunnable;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_Console_h */
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -14,27 +14,29 @@
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/RegisterWorkletBindings.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "nsIInputStreamPump.h"
 #include "nsIThreadRetargetableRequest.h"
+#include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "xpcprivate.h"
 
 namespace mozilla {
 namespace dom {
 
 class ExecutionRunnable final : public Runnable
 {
 public:
   ExecutionRunnable(WorkletFetchHandler* aHandler, Worklet::WorkletType aType,
-                    char16_t* aScriptBuffer, size_t aScriptLength)
+                    char16_t* aScriptBuffer, size_t aScriptLength,
+                    const WorkletLoadInfo& aWorkletLoadInfo)
     : Runnable("Worklet::ExecutionRunnable")
     , mHandler(aHandler)
     , mWorkletType(aType)
     , mBuffer(aScriptBuffer, aScriptLength,
               JS::SourceBufferHolder::GiveOwnership)
     , mResult(NS_ERROR_FAILURE)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -217,17 +219,17 @@ public:
     if (NS_WARN_IF(NS_FAILED(rv))) {
       RejectPromises(rv);
       return NS_OK;
     }
 
     // Moving the ownership of the buffer
     nsCOMPtr<nsIRunnable> runnable =
       new ExecutionRunnable(this, mWorklet->Type(), scriptTextBuf,
-                            scriptTextLength);
+                            scriptTextLength, mWorklet->LoadInfo());
 
     RefPtr<WorkletThread> thread = mWorklet->GetOrCreateThread();
     if (!thread) {
       RejectPromises(NS_ERROR_FAILURE);
       return NS_OK;
     }
 
     if (NS_FAILED(thread->DispatchRunnable(runnable.forget()))) {
@@ -422,16 +424,29 @@ ExecutionRunnable::RunOnMainThread()
     mHandler->ExecutionFailed(mResult);
     return;
   }
 
   mHandler->ExecutionSucceeded();
 }
 
 // ---------------------------------------------------------------------------
+// WorkletLoadInfo
+
+WorkletLoadInfo::WorkletLoadInfo()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+WorkletLoadInfo::~WorkletLoadInfo()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+// ---------------------------------------------------------------------------
 // Worklet
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Worklet)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Worklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   tmp->TerminateThread();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -449,26 +464,44 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worklet)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
                  WorkletType aWorkletType)
   : mWindow(aWindow)
-  , mPrincipal(aPrincipal)
   , mWorkletType(aWorkletType)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(NS_IsMainThread());
 
 #ifdef RELEASE_OR_BETA
   MOZ_CRASH("This code should not go to release/beta yet!");
 #endif
+
+  // Reset mWorkletLoadInfo and populate it.
+
+  memset(&mWorkletLoadInfo, 0, sizeof(WorkletLoadInfo));
+
+  mWorkletLoadInfo.mInnerWindowID = aWindow->WindowID();
+
+  nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
+  if (outerWindow) {
+    mWorkletLoadInfo.mOuterWindowID = outerWindow->WindowID();
+  }
+
+  mWorkletLoadInfo.mOriginAttributes =
+    BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+
+  mWorkletLoadInfo.mPrincipal = aPrincipal;
+
+  mWorkletLoadInfo.mDumpEnabled =
+    nsContentUtils::DOMWindowDumpEnabled();
 }
 
 Worklet::~Worklet()
 {
   TerminateThread();
 }
 
 JSObject*
@@ -537,28 +570,29 @@ Worklet::AddImportFetchHandler(const nsA
 
 WorkletThread*
 Worklet::GetOrCreateThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mWorkletThread) {
     // Thread creation. FIXME: this will change.
-    mWorkletThread = WorkletThread::Create();
+    mWorkletThread = WorkletThread::Create(mWorkletLoadInfo);
   }
 
   return mWorkletThread;
 }
 
 void
 Worklet::TerminateThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mWorkletThread) {
     return;
   }
 
   mWorkletThread->Terminate();
   mWorkletThread = nullptr;
+  mWorkletLoadInfo.mPrincipal = nullptr;
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/worklet/Worklet.h
+++ b/dom/worklet/Worklet.h
@@ -3,33 +3,67 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Worklet_h
 #define mozilla_dom_Worklet_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/ErrorResult.h"
 #include "nsRefPtrHashtable.h"
 #include "nsWrapperCache.h"
 #include "nsCOMPtr.h"
 
 class nsPIDOMWindowInner;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
+class Worklet;
 class WorkletFetchHandler;
 class WorkletGlobalScope;
 class WorkletThread;
 enum class CallerType : uint32_t;
 
+class WorkletLoadInfo
+{
+public:
+  WorkletLoadInfo();
+  ~WorkletLoadInfo();
+
+  uint64_t OuterWindowID() const { return mOuterWindowID; }
+  uint64_t InnerWindowID() const { return mInnerWindowID; }
+  bool DumpEnabled() const { return mDumpEnabled; }
+
+  const OriginAttributes& OriginAttributesRef() const
+  {
+    return mOriginAttributes;
+  }
+
+  nsIPrincipal* Principal() const
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mPrincipal;
+  }
+
+private:
+  uint64_t mOuterWindowID;
+  uint64_t mInnerWindowID;
+  bool mDumpEnabled;
+  OriginAttributes mOriginAttributes;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
+  friend class Worklet;
+  friend class WorkletThread;
+};
+
 class Worklet final : public nsISupports
                     , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Worklet)
 
   enum WorkletType {
@@ -60,36 +94,43 @@ public:
   }
 
   static already_AddRefed<WorkletGlobalScope>
   CreateGlobalScope(JSContext* aCx, WorkletType aWorkletType);
 
   WorkletThread*
   GetOrCreateThread();
 
+  const WorkletLoadInfo&
+  LoadInfo() const
+  {
+    return mWorkletLoadInfo;
+  }
+
 private:
   ~Worklet();
 
   WorkletFetchHandler*
   GetImportFetchHandler(const nsACString& aURI);
 
   void
   AddImportFetchHandler(const nsACString& aURI, WorkletFetchHandler* aHandler);
 
   void
   TerminateThread();
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
 
   WorkletType mWorkletType;
 
   nsRefPtrHashtable<nsCStringHashKey, WorkletFetchHandler> mImportHandlers;
 
   RefPtr<WorkletThread> mWorkletThread;
 
+  WorkletLoadInfo mWorkletLoadInfo;
+
   friend class WorkletFetchHandler;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Worklet_h
--- a/dom/worklet/WorkletGlobalScope.cpp
+++ b/dom/worklet/WorkletGlobalScope.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkletGlobalScope.h"
 #include "mozilla/dom/WorkletGlobalScopeBinding.h"
 #include "mozilla/dom/Console.h"
-#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletGlobalScope)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletGlobalScope)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -45,35 +44,40 @@ WorkletGlobalScope::WrapObject(JSContext
 {
   MOZ_CRASH("We should never get here!");
   return nullptr;
 }
 
 Console*
 WorkletGlobalScope::GetConsole(ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_FAILURE);
-/* TODO
+  RefPtr<WorkletThread> thread = WorkletThread::Get();
+  MOZ_ASSERT(thread);
+
   if (!mConsole) {
-    mConsole = Console::CreateForWorklet(aRv);
+    mConsole = Console::CreateForWorklet(thread->GetWorkletLoadInfo().OuterWindowID(),
+                                         thread->GetWorkletLoadInfo().InnerWindowID(),
+                                         aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
-  } */
+  }
 
   return mConsole;
 }
 
 void
 WorkletGlobalScope::Dump(const Optional<nsAString>& aString) const
 {
-  return;
+  WorkletThread::AssertIsOnWorkletThread();
 
-  // TODO: this is not thread-safe!!
-  if (!nsContentUtils::DOMWindowDumpEnabled()) {
+  WorkletThread* workletThread = WorkletThread::Get();
+  MOZ_ASSERT(workletThread);
+
+  if (!workletThread->GetWorkletLoadInfo().DumpEnabled()) {
     return;
   }
 
   if (!aString.WasPassed()) {
     return;
   }
 
   NS_ConvertUTF16toUTF8 str(aString.Value());
--- a/dom/worklet/WorkletThread.cpp
+++ b/dom/worklet/WorkletThread.cpp
@@ -22,17 +22,17 @@ namespace {
 // The size of the generational GC nursery for worklet, in bytes.
 #define WORKLET_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
 
 // The C stack size. We use the same stack size on all platforms for
 // consistency.
 const uint32_t kWorkletStackSize = 256 * sizeof(size_t) * 1024;
 
 // This class is allocated per thread and can be retrieved from CC.
-// It's used to get the current WorketThread object.
+// It's used to get the current WorkletThread object.
 class WorkletThreadContextPrivate : private PerThreadAtomCache
 {
 public:
   explicit
   WorkletThreadContextPrivate(WorkletThread* aWorkletThread)
     : mWorkletThread(aWorkletThread)
   {
     MOZ_ASSERT(!NS_IsMainThread());
@@ -282,35 +282,38 @@ public:
     mWorkletThread->TerminateInternal();
     return NS_OK;
   }
 
 private:
   RefPtr<WorkletThread> mWorkletThread;
 };
 
-WorkletThread::WorkletThread()
+WorkletThread::WorkletThread(const WorkletLoadInfo& aWorkletLoadInfo)
   : nsThread(MakeNotNull<ThreadEventQueue<mozilla::EventQueue>*>(
                MakeUnique<mozilla::EventQueue>()),
              nsThread::NOT_MAIN_THREAD, kWorkletStackSize)
+  , mWorkletLoadInfo(aWorkletLoadInfo)
+  , mCreationTimeStamp(TimeStamp::Now())
   , mJSContext(nullptr)
 {
 }
 
 WorkletThread::~WorkletThread()
 {
   // This should be gone during the termination step.
   MOZ_ASSERT(!mJSContext);
 }
 
 // static
 already_AddRefed<WorkletThread>
-WorkletThread::Create()
+WorkletThread::Create(const WorkletLoadInfo& aWorkletLoadInfo)
 {
-  RefPtr<WorkletThread> thread = new WorkletThread();
+  RefPtr<WorkletThread> thread =
+    new WorkletThread(aWorkletLoadInfo);
   if (NS_WARN_IF(NS_FAILED(thread->Init()))) {
     return nullptr;
   }
 
   RefPtr<PrimaryRunnable> runnable = new PrimaryRunnable(thread);
   if (NS_WARN_IF(NS_FAILED(thread->DispatchRunnable(runnable.forget())))) {
     return nullptr;
   }
@@ -415,16 +418,22 @@ WorkletThread::TerminateInternal()
 JSContext*
 WorkletThread::GetJSContext() const
 {
   AssertIsOnWorkletThread();
   MOZ_ASSERT(mJSContext);
   return mJSContext;
 }
 
+const WorkletLoadInfo&
+WorkletThread::GetWorkletLoadInfo() const
+{
+  return mWorkletLoadInfo;
+}
+
 /* static */ bool
 WorkletThread::IsOnWorkletThread()
 {
   const char* threadName = PR_GetThreadName(PR_GetCurrentThread());
   return threadName && !strcmp(threadName, "worklet");
 }
 
 /* static */ void
--- a/dom/worklet/WorkletThread.h
+++ b/dom/worklet/WorkletThread.h
@@ -4,56 +4,69 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_worklet_WorkletThread_h
 #define mozilla_dom_worklet_WorkletThread_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CondVar.h"
+#include "mozilla/dom/Worklet.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/TimeStamp.h"
 #include "nsThread.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
 class WorkletThread final : public nsThread
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<WorkletThread>
-  Create();
+  Create(const WorkletLoadInfo& aWorkletLoadInfo);
 
   static WorkletThread*
   Get();
 
   static bool
   IsOnWorkletThread();
 
   static void
   AssertIsOnWorkletThread();
 
   static JSPrincipals*
   GetWorkerPrincipal();
 
   JSContext*
   GetJSContext() const;
 
+  const WorkletLoadInfo&
+  GetWorkletLoadInfo() const;
+
   nsresult
   DispatchRunnable(already_AddRefed<nsIRunnable> aRunnable);
 
   void
   Terminate();
 
+  DOMHighResTimeStamp
+  TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const
+  {
+    MOZ_ASSERT(!aTimeStamp.IsNull());
+    TimeDuration duration = aTimeStamp - mCreationTimeStamp;
+    return duration.ToMilliseconds();
+  }
+
 private:
-  WorkletThread();
+  explicit WorkletThread(const WorkletLoadInfo& aWorkletLoadInfo);
   ~WorkletThread();
 
   void
   RunEventLoop(JSRuntime* aParentRuntime);
   class PrimaryRunnable;
 
   void
   TerminateInternal();
@@ -65,16 +78,19 @@ private:
   Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override;
 
   NS_IMETHOD
   DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
 
   NS_IMETHOD
   DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
 
+  const WorkletLoadInfo mWorkletLoadInfo;
+  TimeStamp mCreationTimeStamp;
+
   // Touched only on the worklet thread. This is a raw pointer because it's set
   // and nullified by RunEventLoop().
   JSContext* mJSContext;
 };
 
 } // namespace dom
 } // namespace mozilla