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 351372 3769e657d10451ef2da58a52b9c3858de0e56fd8
parent 351371 59c14257aba5069d2c33102cc2cad24b581ed9cf
child 351373 1946c5517376329c66b51e7ec943e841f7a5e348
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1290021
milestone52.0a1
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");