Bug 1429174 - Introducing ConsoleUtils for logging messages to console, r=bkelly
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 10 Jan 2018 22:06:37 +0100
changeset 452961 8c42fc215b1be8456a1c4bb0a1491d9c2bee0ff4
parent 452960 b7d96ef3b33c77e7a8fe367d4b6c463a07aac63a
child 452962 75a5873327a61af0923c83720a1f677d832e2b9c
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1429174
milestone59.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 1429174 - Introducing ConsoleUtils for logging messages to console, r=bkelly
dom/console/Console.cpp
dom/console/ConsoleCommon.h
dom/console/ConsoleInstance.cpp
dom/console/ConsoleUtils.cpp
dom/console/ConsoleUtils.h
dom/console/moz.build
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -2,16 +2,17 @@
 /* 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 "mozilla/dom/Console.h"
 #include "mozilla/dom/ConsoleInstance.h"
 #include "mozilla/dom/ConsoleBinding.h"
+#include "ConsoleCommon.h"
 
 #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/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
@@ -290,34 +291,16 @@ public:
 private:
   ~ConsoleCallData()
   {
     AssertIsOnOwningThread();
     MOZ_ASSERT(mStatus != eInUse);
   }
 };
 
-// This class is used to clear any exception at the end of this method.
-class ClearException
-{
-public:
-  explicit ClearException(JSContext* aCx)
-    : mCx(aCx)
-  {
-  }
-
-  ~ClearException()
-  {
-    JS_ClearPendingException(mCx);
-  }
-
-private:
-  JSContext* mCx;
-};
-
 class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
                       , public StructuredCloneHolderBase
 {
 public:
   explicit ConsoleRunnable(Console* aConsole)
     : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
     , mConsole(aConsole)
   {}
@@ -531,17 +514,17 @@ private:
   }
 
   bool
   PreDispatch(JSContext* aCx) override
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
     mCallData->AssertIsOnOwningThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::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);
@@ -618,17 +601,17 @@ private:
     mCallData = nullptr;
   }
 
   void
   ProcessCallData(JSContext* aCx)
   {
     AssertIsOnMainThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::ClearException ce(aCx);
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     if (!Read(aCx, &argumentsValue)) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
 
@@ -676,17 +659,17 @@ public:
   {
     MOZ_ASSERT(aConsole);
   }
 
 private:
   bool
   PreDispatch(JSContext* aCx) override
   {
-    ClearException ce(aCx);
+    ConsoleCommon::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);
@@ -708,17 +691,17 @@ private:
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
     AssertIsOnMainThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::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);
     mClonedData.mParent = nullptr;
 
@@ -1009,17 +992,17 @@ Console::StringMethod(const GlobalObject
                                 aMethodString);
 }
 
 void
 Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
                               MethodName aMethodName,
                               const nsAString& aMethodString)
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   JS::Rooted<JS::Value> value(aCx);
   if (!dom::ToJSValue(aCx, aLabel, &value)) {
     return;
   }
@@ -1032,17 +1015,17 @@ Console::StringMethodInternal(JSContext*
 }
 
 /* static */ void
 Console::TimeStamp(const GlobalObject& aGlobal,
                    const JS::Handle<JS::Value> aData)
 {
   JSContext* cx = aGlobal.Context();
 
-  ClearException ce(cx);
+  ConsoleCommon::ClearException ce(cx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(cx, &data);
 
   if (aData.isString() && !data.AppendElement(aData, fallible)) {
     return;
   }
 
@@ -1108,17 +1091,17 @@ Console::ProfileMethodInternal(JSContext
     // Here we are in a worker thread.
     RefPtr<ConsoleProfileRunnable> runnable =
       new ConsoleProfileRunnable(this, aMethodName, aAction, aData);
 
     runnable->Dispatch(aCx);
     return;
   }
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   RootedDictionary<ConsoleProfileEvent> event(aCx);
   event.mAction = aAction;
 
   event.mArguments.Construct();
   Sequence<JS::Value>& sequence = event.mArguments.Value();
 
   for (uint32_t i = 0; i < aData.Length(); ++i) {
@@ -1259,17 +1242,17 @@ Console::MethodInternal(JSContext* aCx, 
   if (!ShouldProceed(aMethodName)) {
     return;
   }
 
   AssertIsOnOwningThread();
 
   RefPtr<ConsoleCallData> callData(new ConsoleCallData());
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
                                        aData, this))) {
     return;
   }
 
   OriginAttributes oa;
 
@@ -1525,17 +1508,17 @@ Console::PopulateConsoleNotificationInTh
 
   JS::Rooted<JSObject*> targetScope(aCx, aTargetScope);
 
   ConsoleStackEntry frame;
   if (aData->mTopStackFrame) {
     frame = *aData->mTopStackFrame;
   }
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
   RootedDictionary<ConsoleEvent> event(aCx);
 
   event.mAddonId = aData->mAddonId;
 
   event.mID.Construct();
   event.mInnerID.Construct();
 
   if (aData->mIDType == ConsoleCallData::eString) {
@@ -2183,17 +2166,17 @@ Console::ArgumentsToValueList(const Sequ
 }
 
 uint32_t
 Console::IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aArguments,
                          nsAString& aCountLabel)
 {
   AssertIsOnOwningThread();
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   MOZ_ASSERT(!aArguments.IsEmpty());
 
   JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
   if (!jsString) {
     return 0; // We cannot continue.
   }
@@ -2219,17 +2202,17 @@ Console::IncreaseCounter(JSContext* aCx,
   }
   return entry.Data();
 }
 
 JS::Value
 Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
                             uint32_t aCountValue) const
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   if (aCountValue == MAX_PAGE_COUNTERS) {
     RootedDictionary<ConsoleCounterError> error(aCx);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!ToJSValue(aCx, error, &value)) {
       return JS::UndefinedValue();
     }
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleCommon.h
@@ -0,0 +1,38 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_ConsoleCommon_h
+#define mozilla_dom_ConsoleCommon_h
+
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+namespace ConsoleCommon {
+
+// This class is used to clear any exception at the end of this method.
+class MOZ_RAII ClearException
+{
+public:
+  explicit ClearException(JSContext* aCx)
+    : mCx(aCx)
+  {
+  }
+
+  ~ClearException()
+  {
+    JS_ClearPendingException(mCx);
+  }
+
+private:
+  JSContext* mCx;
+};
+
+} // namespace ConsoleCommon
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ConsoleCommon_h */
--- a/dom/console/ConsoleInstance.cpp
+++ b/dom/console/ConsoleInstance.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "mozilla/dom/ConsoleInstance.h"
 #include "mozilla/dom/ConsoleBinding.h"
+#include "ConsoleCommon.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance)
@@ -127,17 +128,17 @@ ConsoleInstance::TimeEnd(JSContext* aCx,
 {
   mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTimeEnd,
                                  NS_LITERAL_STRING("timeEnd"));
 }
 
 void
 ConsoleInstance::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   if (aData.isString() && !data.AppendElement(aData, fallible)) {
     return;
   }
 
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleUtils.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "ConsoleUtils.h"
+#include "ConsoleCommon.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "NullPrincipal.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<ConsoleUtils> gConsoleUtilsService;
+
+}
+
+/* static */ ConsoleUtils*
+ConsoleUtils::GetOrCreate()
+{
+  if (!gConsoleUtilsService) {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    gConsoleUtilsService = new ConsoleUtils();
+    ClearOnShutdown(&gConsoleUtilsService);
+  }
+
+  return gConsoleUtilsService;
+}
+
+ConsoleUtils::ConsoleUtils() = default;
+ConsoleUtils::~ConsoleUtils() = default;
+
+/* static */ void
+ConsoleUtils::ReportForServiceWorkerScope(const nsAString& aScope,
+                                          const nsAString& aMessage,
+                                          const nsAString& aFilename,
+                                          uint32_t aLineNumber,
+                                          uint32_t aColumnNumber)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<ConsoleUtils> service = ConsoleUtils::GetOrCreate();
+  if (NS_WARN_IF(!service)) {
+    return;
+  }
+
+  service->ReportForServiceWorkerScopeInternal(aScope, aMessage, aFilename,
+                                               aLineNumber, aColumnNumber);
+}
+
+void
+ConsoleUtils::ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                                  const nsAString& aMessage,
+                                                  const nsAString& aFilename,
+                                                  uint32_t aLineNumber,
+                                                  uint32_t aColumnNumber)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+
+  JSContext* cx = jsapi.cx();
+
+  ConsoleCommon::ClearException ce(cx);
+  JS::Rooted<JSObject*> global(cx, GetOrCreateSandbox(cx));
+  if (NS_WARN_IF(!global)) {
+    return;
+  }
+
+  // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
+  // We don't need a proxy here.
+  global = js::UncheckedUnwrap(global);
+
+  JSAutoCompartment ac(cx, global);
+
+  RootedDictionary<ConsoleEvent> event(cx);
+
+  event.mID.Construct();
+  event.mID.Value().SetAsString() = aScope;
+
+  event.mInnerID.Construct();
+  event.mInnerID.Value().SetAsString() = NS_LITERAL_STRING("ServiceWorker");
+
+  event.mLevel = NS_LITERAL_STRING("log");
+  event.mFilename = aFilename;
+  event.mLineNumber = aLineNumber;
+  event.mColumnNumber = aColumnNumber;
+  event.mTimeStamp = JS_Now() / PR_USEC_PER_MSEC;
+
+  JS::Rooted<JS::Value> messageValue(cx);
+  if (!dom::ToJSValue(cx, aMessage, &messageValue)) {
+    return;
+  }
+
+  event.mArguments.Construct();
+  if (!event.mArguments.Value().AppendElement(messageValue, fallible)) {
+    return;
+  }
+
+  nsCOMPtr<nsIConsoleAPIStorage> storage =
+    do_GetService("@mozilla.org/consoleAPI-storage;1");
+
+  if (NS_WARN_IF(!storage)) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> eventValue(cx);
+  if (!ToJSValue(cx, event, &eventValue)) {
+    return;
+  }
+
+  // This is a legacy property.
+  JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
+  if (NS_WARN_IF(!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventObj,
+                                    JSPROP_ENUMERATE))) {
+    return;
+  }
+
+  storage->RecordEvent(NS_LITERAL_STRING("ServiceWorker"), aScope, eventValue);
+}
+
+JSObject*
+ConsoleUtils::GetOrCreateSandbox(JSContext* aCx)
+{
+  AssertIsOnMainThread();
+
+  if (!mSandbox) {
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    MOZ_ASSERT(xpc, "This should never be null!");
+
+    RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::Create();
+
+    JS::Rooted<JSObject*> sandbox(aCx);
+    nsresult rv = xpc->CreateSandbox(aCx, nullPrincipal, sandbox.address());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    mSandbox = new JSObjectHolder(aCx, sandbox);
+  }
+
+  return mSandbox->GetJSObject();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleUtils.h
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_ConsoleUtils_h
+#define mozilla_dom_ConsoleUtils_h
+
+#include "mozilla/JSObjectHolder.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class ConsoleUtils final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ConsoleUtils)
+
+  // Main-thread only, reports a console message from a ServiceWorker.
+  static void
+  ReportForServiceWorkerScope(const nsAString& aScope,
+                              const nsAString& aMessage,
+                              const nsAString& aFilename,
+                              uint32_t aLineNumber,
+                              uint32_t aColumnNumber);
+
+private:
+  ConsoleUtils();
+  ~ConsoleUtils();
+
+  static ConsoleUtils*
+  GetOrCreate();
+
+  JSObject*
+  GetOrCreateSandbox(JSContext* aCx);
+
+  void
+  ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                      const nsAString& aMessage,
+                                      const nsAString& aFilename,
+                                      uint32_t aLineNumber,
+                                      uint32_t aColumnNumber);
+
+  RefPtr<JSObjectHolder> mSandbox;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ConsoleUtils_h */
--- a/dom/console/moz.build
+++ b/dom/console/moz.build
@@ -19,22 +19,24 @@ EXPORTS += [
 
 EXPORTS.mozilla += [
     'ConsoleReportCollector.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'Console.h',
     'ConsoleInstance.h',
+    'ConsoleUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'Console.cpp',
     'ConsoleInstance.cpp',
     'ConsoleReportCollector.cpp',
+    'ConsoleUtils.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.manifest',
     'ConsoleAPIStorage.js',
 ]
 
 LOCAL_INCLUDES += [