Bug 1290021 - Implement a prototype version of Houdini "Worklets Level 1" spec - part 3 - Console API in worklet, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Sun, 06 Nov 2016 09:54:52 +0100
changeset 321313 3769e657d10451ef2da58a52b9c3858de0e56fd8
parent 321312 59c14257aba5069d2c33102cc2cad24b581ed9cf
child 321314 1946c5517376329c66b51e7ec943e841f7a5e348
push id83550
push useramarchesini@mozilla.com
push dateSun, 06 Nov 2016 08:56:23 +0000
treeherdermozilla-inbound@f99b99cc076d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1290021
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1290021 - Implement a prototype version of Houdini "Worklets Level 1" spec - part 3 - Console API in worklet, r=smaug
dom/base/nsGlobalWindow.cpp
dom/console/Console.cpp
dom/console/Console.h
dom/webidl/Console.webidl
dom/worklet/Worklet.cpp
dom/worklet/Worklet.h
dom/worklet/WorkletGlobalScope.cpp
dom/worklet/WorkletGlobalScope.h
dom/worklet/tests/file_console.html
dom/worklet/tests/mochitest.ini
dom/worklet/tests/test_console.html
dom/worklet/tests/worklet_console.js
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14847,20 +14847,22 @@ nsGlobalWindow::TemporarilyDisableDialog
   if (mTopWindow) {
     mTopWindow->mAreDialogsEnabled = mSavedDialogsEnabled;
   }
 }
 
 already_AddRefed<Worklet>
 nsGlobalWindow::CreateWorklet(ErrorResult& aRv)
 {
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
   if (!mDoc) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  RefPtr<Worklet> worklet = new Worklet(this, mDoc->NodePrincipal());
+  RefPtr<Worklet> worklet = new Worklet(AsInner(), mDoc->NodePrincipal());
   return worklet.forget();
 }
 
 template class nsPIDOMWindow<mozIDOMWindowProxy>;
 template class nsPIDOMWindow<mozIDOMWindow>;
 template class nsPIDOMWindow<nsISupports>;
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -9,16 +9,17 @@
 
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/WorkletGlobalScope.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDocument.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "ScriptSettings.h"
@@ -2358,73 +2359,82 @@ Console::IsShuttingDown() const
 {
   MOZ_ASSERT(mStatus != eUnknown);
   return mStatus == eShuttingDown;
 }
 
 /* static */ already_AddRefed<Console>
 Console::GetConsole(const GlobalObject& aGlobal)
 {
-  RefPtr<Console> console;
-
+  ErrorResult rv;
+  RefPtr<Console> console = GetConsoleInternal(aGlobal, rv);
+  if (NS_WARN_IF(rv.Failed()) || !console) {
+    rv.SuppressException();
+    return nullptr;
+  }
+
+  console->AssertIsOnOwningThread();
+
+  if (console->IsShuttingDown()) {
+    return nullptr;
+  }
+
+  return console.forget();
+}
+
+/* static */ Console*
+Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
+{
+  // Worklet
+  if (NS_IsMainThread()) {
+    nsCOMPtr<WorkletGlobalScope> workletScope =
+      do_QueryInterface(aGlobal.GetAsSupports());
+    if (workletScope) {
+      return workletScope->GetConsole(aRv);
+    }
+  }
+
+  // Window
   if (NS_IsMainThread()) {
     nsCOMPtr<nsPIDOMWindowInner> innerWindow =
       do_QueryInterface(aGlobal.GetAsSupports());
     if (NS_WARN_IF(!innerWindow)) {
       return nullptr;
     }
 
     nsGlobalWindow* window = nsGlobalWindow::Cast(innerWindow);
-
-    ErrorResult rv;
-    console = window->GetConsole(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      rv.SuppressException();
-      return nullptr;
-    }
-  } else {
-    JSContext* cx = aGlobal.Context();
-    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
-    MOZ_ASSERT(workerPrivate);
-
-    nsCOMPtr<nsIGlobalObject> global =
-      do_QueryInterface(aGlobal.GetAsSupports());
-    if (NS_WARN_IF(!global)) {
-      return nullptr;
-    }
-
-    WorkerGlobalScope* scope = workerPrivate->GlobalScope();
-    MOZ_ASSERT(scope);
-
-    // Normal worker scope.
-    ErrorResult rv;
-    if (scope == global) {
-      console = scope->GetConsole(rv);
-    }
-
-    // Debugger worker scope
-    else {
-      WorkerDebuggerGlobalScope* debuggerScope =
-        workerPrivate->DebuggerGlobalScope();
-      MOZ_ASSERT(debuggerScope);
-      MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
-
-      console = debuggerScope->GetConsole(rv);
-    }
-
-    if (NS_WARN_IF(rv.Failed())) {
-      rv.SuppressException();
-      return nullptr;
-    }
+    return window->GetConsole(aRv);
   }
 
-  MOZ_ASSERT(console);
-  console->AssertIsOnOwningThread();
-
-  if (console->IsShuttingDown()) {
+  // Workers
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  JSContext* cx = aGlobal.Context();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+  MOZ_ASSERT(workerPrivate);
+
+  nsCOMPtr<nsIGlobalObject> global =
+    do_QueryInterface(aGlobal.GetAsSupports());
+  if (NS_WARN_IF(!global)) {
     return nullptr;
   }
 
-  return console.forget();
+  WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+  MOZ_ASSERT(scope);
+
+  // Normal worker scope.
+  if (scope == global) {
+    return scope->GetConsole(aRv);
+  }
+
+  // Debugger worker scope
+  else {
+    WorkerDebuggerGlobalScope* debuggerScope =
+      workerPrivate->DebuggerGlobalScope();
+    MOZ_ASSERT(debuggerScope);
+    MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
+
+    return debuggerScope->GetConsole(aRv);
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/console/Console.h
+++ b/dom/console/Console.h
@@ -156,16 +156,19 @@ private:
     MethodAssert,
     MethodCount,
     MethodClear
   };
 
   static already_AddRefed<Console>
   GetConsole(const GlobalObject& aGlobal);
 
+  static Console*
+  GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult &aRv);
+
   static void
   ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
                 const Sequence<JS::Value>& aData);
 
   void
   ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
                         const Sequence<JS::Value>& aData);
 
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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/. */
 
-[Exposed=(Window,Worker,WorkerDebugger),
+[Exposed=(Window,Worker,WorkerDebugger,Worklet),
  ClassString="Console",
  ProtoObjectHack]
 namespace console {
   void log(any... data);
   void info(any... data);
   void warn(any... data);
   void error(any... data);
   void _exception(any... data);
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -27,28 +27,31 @@ class WorkletFetchHandler : public Promi
                           , public nsIStreamLoaderObserver
 {
 public:
   NS_DECL_ISUPPORTS
 
   static already_AddRefed<Promise>
   Fetch(Worklet* aWorklet, const nsAString& aModuleURL, ErrorResult& aRv)
   {
-    RefPtr<Promise> promise = Promise::Create(aWorklet->GetParentObject(), aRv);
+    nsCOMPtr<nsIGlobalObject> global =
+      do_QueryInterface(aWorklet->GetParentObject());
+    MOZ_ASSERT(global);
+
+    RefPtr<Promise> promise = Promise::Create(global, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
     RequestOrUSVString request;
     request.SetAsUSVString().Rebind(aModuleURL.Data(), aModuleURL.Length());
 
     RequestInit init;
 
-    RefPtr<Promise> fetchPromise =
-      FetchRequest(aWorklet->GetParentObject(), request, init, aRv);
+    RefPtr<Promise> fetchPromise = FetchRequest(global, request, init, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       promise->MaybeReject(aRv);
       return promise.forget();
     }
 
     RefPtr<WorkletFetchHandler> handler =
       new WorkletFetchHandler(aWorklet, aModuleURL, promise);
     fetchPromise->AppendNativeHandler(handler);
@@ -200,29 +203,32 @@ private:
 
   nsString mURL;
 };
 
 NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
 
 } // anonymous namespace
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mGlobal, mScope)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mWindow, mScope)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
 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(nsIGlobalObject* aGlobal, nsIPrincipal* aPrincipal)
-  : mGlobal(aGlobal)
+Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
+  : mWindow(aWindow)
   , mPrincipal(aPrincipal)
-{}
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aPrincipal);
+}
 
 Worklet::~Worklet()
 {}
 
 JSObject*
 Worklet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return WorkletBinding::Wrap(aCx, this, aGivenProto);
@@ -233,17 +239,17 @@ Worklet::Import(const nsAString& aModule
 {
   return WorkletFetchHandler::Fetch(this, aModuleURL, aRv);
 }
 
 WorkletGlobalScope*
 Worklet::GetOrCreateGlobalScope(JSContext* aCx)
 {
   if (!mScope) {
-    mScope = new WorkletGlobalScope();
+    mScope = new WorkletGlobalScope(mWindow);
 
     JS::Rooted<JSObject*> global(aCx);
     NS_ENSURE_TRUE(mScope->WrapGlobalObject(aCx, mPrincipal, &global), nullptr);
 
     JSAutoCompartment ac(aCx, global);
 
     // Init Web IDL bindings
     if (!RegisterWorkletBindings(aCx, global)) {
--- a/dom/worklet/Worklet.h
+++ b/dom/worklet/Worklet.h
@@ -7,52 +7,52 @@
 #ifndef mozilla_dom_Worklet_h
 #define mozilla_dom_Worklet_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsWrapperCache.h"
 #include "nsCOMPtr.h"
 
-class nsIGlobalObject;
+class nsPIDOMWindowInner;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class WorkletGlobalScope;
 
 class Worklet final : public nsISupports
                     , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Worklet)
 
-  Worklet(nsIGlobalObject* aGlobal, nsIPrincipal* aPrincipal);
+  Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
 
-  nsIGlobalObject* GetParentObject() const
+  nsPIDOMWindowInner* GetParentObject() const
   {
-    return mGlobal;
+    return mWindow;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise>
   Import(const nsAString& aModuleURL, ErrorResult& aRv);
 
   WorkletGlobalScope*
   GetOrCreateGlobalScope(JSContext* aCx);
 
 private:
   ~Worklet();
 
-  nsCOMPtr<nsIGlobalObject> mGlobal;
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   RefPtr<WorkletGlobalScope> mScope;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/worklet/WorkletGlobalScope.cpp
+++ b/dom/worklet/WorkletGlobalScope.cpp
@@ -1,42 +1,58 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletGlobalScope)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletGlobalScope)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletGlobalScope)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WorkletGlobalScope)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletGlobalScope)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletGlobalScope)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletGlobalScope)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+  NS_INTERFACE_MAP_ENTRY(WorkletGlobalScope)
 NS_INTERFACE_MAP_END
 
+WorkletGlobalScope::WorkletGlobalScope(nsPIDOMWindowInner* aWindow)
+  : mWindow(aWindow)
+{
+  MOZ_ASSERT(aWindow);
+}
+
+WorkletGlobalScope::~WorkletGlobalScope()
+{
+}
+
 JSObject*
 WorkletGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   MOZ_CRASH("We should never get here!");
   return nullptr;
 }
 
 bool
@@ -45,10 +61,24 @@ WorkletGlobalScope::WrapGlobalObject(JSC
                                      JS::MutableHandle<JSObject*> aReflector)
 {
   JS::CompartmentOptions options;
   return WorkletGlobalScopeBinding::Wrap(aCx, this, this,
                                          options,
                                          nsJSPrincipals::get(aPrincipal),
                                          true, aReflector);
 }
+
+Console*
+WorkletGlobalScope::GetConsole(ErrorResult& aRv)
+{
+  if (!mConsole) {
+    mConsole = Console::Create(mWindow, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  return mConsole;
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/worklet/WorkletGlobalScope.h
+++ b/dom/worklet/WorkletGlobalScope.h
@@ -7,30 +7,38 @@
 #ifndef mozilla_dom_WorkletGlobalScope_h
 #define mozilla_dom_WorkletGlobalScope_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIGlobalObject.h"
 #include "nsWrapperCache.h"
 
+#define WORKLET_IID \
+  { 0x1b3f62e7, 0xe357, 0x44be, \
+    { 0xbf, 0xe0, 0xdf, 0x85, 0xe6, 0x56, 0x85, 0xac } }
+
 class nsIPrincipal;
+class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
+class Console;
+
 class WorkletGlobalScope final : public nsIGlobalObject
                                , public nsWrapperCache
 {
 public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(WORKLET_IID)
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkletGlobalScope)
 
-  WorkletGlobalScope()
-  {}
+  explicit WorkletGlobalScope(nsPIDOMWindowInner* aWindow);
 
   nsIGlobalObject* GetParentObject() const
   {
     return nullptr;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -40,17 +48,24 @@ public:
                    JS::MutableHandle<JSObject*> aReflector);
 
   virtual JSObject*
   GetGlobalJSObject() override
   {
     return GetWrapper();
   }
 
+  Console*
+  GetConsole(ErrorResult& aRv);
+
 private:
-  ~WorkletGlobalScope()
-  {}
+  ~WorkletGlobalScope();
+
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  RefPtr<Console> mConsole;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(WorkletGlobalScope, WORKLET_IID)
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WorkletGlobalScope_h
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/file_console.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet</title>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+setupTest();
+
+var worklet = window.createWorklet();
+ok(!!worklet, "We have a Worklet");
+worklet.import("worklet_console.js");
+
+</script>
+</body>
+</html>
--- a/dom/worklet/tests/mochitest.ini
+++ b/dom/worklet/tests/mochitest.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 support-files =
   common.js
-  file_basic.html
 
 [test_basic.html]
+support-files=file_basic.html
+[test_console.html]
+support-files=file_console.html worklet_console.js
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/test_console.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Worklet - Console</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+
+<script type="application/javascript">
+
+function consoleListener() {
+  SpecialPowers.addObserver(this, "console-api-log-event", false);
+}
+
+consoleListener.prototype  = {
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == "console-api-log-event") {
+      var obj = aSubject.wrappedJSObject;
+      if (obj.arguments[0] == "Hello world from a worklet") {
+        ok(true, "Message received \\o/");
+
+        SpecialPowers.removeObserver(this, "console-api-log-event");
+        SimpleTest.finish();
+        return;
+      }
+    }
+  }
+}
+
+var cl = new consoleListener();
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.worklet.testing.enabled", true],
+           ["dom.worklet.enabled", true]]},
+  function() { loadTest("file_console.html"); });
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/worklet/tests/worklet_console.js
@@ -0,0 +1,1 @@
+console.log("Hello world from a worklet");