Bug 1283712 - Part 8: Add WorkerErrorBase, WorkerErrorNote, and WorkerErrorReport. r=bholley
authorTooru Fujisawa <arai_a@mac.com>
Wed, 15 Feb 2017 23:53:07 +0900
changeset 389734 ca906f20c23da421a614df6b9f16ee7711ed84cb
parent 389733 de4875e04b7747d22377009d016feec433e666d1
child 389735 1135a29fbc37a90cea364599c973e0918206a3e5
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1283712
milestone54.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 1283712 - Part 8: Add WorkerErrorBase, WorkerErrorNote, and WorkerErrorReport. r=bholley
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/moz.build
dom/workers/test/test_sharedWorker.html
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -83,16 +83,17 @@
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsSandboxFlags.h"
+#include "nsScriptError.h"
 #include "nsUTF8Utils.h"
 #include "prthread.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
@@ -276,68 +277,75 @@ struct WindowAction
   bool
   operator==(const WindowAction& aOther) const
   {
     return mWindow == aOther.mWindow;
   }
 };
 
 void
-LogErrorToConsole(const nsAString& aMessage,
-                  const nsAString& aFilename,
-                  const nsAString& aLine,
-                  uint32_t aLineNumber,
-                  uint32_t aColumnNumber,
-                  uint32_t aFlags,
-                  uint64_t aInnerWindowId)
+LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId)
 {
   AssertIsOnMainThread();
 
-  nsCOMPtr<nsIScriptError> scriptError =
-    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+  RefPtr<nsScriptErrorBase> scriptError = new nsScriptError();
   NS_WARNING_ASSERTION(scriptError, "Failed to create script error!");
 
   if (scriptError) {
-    if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine,
-                                                aLineNumber, aColumnNumber,
-                                                aFlags, "Web Worker",
+    nsAutoCString category("Web Worker");
+    if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage,
+                                                aReport.mFilename,
+                                                aReport.mLine,
+                                                aReport.mLineNumber,
+                                                aReport.mColumnNumber,
+                                                aReport.mFlags,
+                                                category,
                                                 aInnerWindowId))) {
       NS_WARNING("Failed to init script error!");
       scriptError = nullptr;
     }
+
+    for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) {
+      const WorkerErrorNote& note = aReport.mNotes.ElementAt(i);
+
+      nsScriptErrorNote* noteObject = new nsScriptErrorNote();
+      noteObject->Init(note.mMessage, note.mFilename,
+                       note.mLineNumber, note.mColumnNumber);
+      scriptError->AddNote(noteObject);
+    }
   }
 
   nsCOMPtr<nsIConsoleService> consoleService =
     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   NS_WARNING_ASSERTION(consoleService, "Failed to get console service!");
 
   if (consoleService) {
     if (scriptError) {
       if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
         return;
       }
       NS_WARNING("LogMessage failed!");
     } else if (NS_SUCCEEDED(consoleService->LogStringMessage(
-                                                    aMessage.BeginReading()))) {
+                              aReport.mMessage.BeginReading()))) {
       return;
     }
     NS_WARNING("LogStringMessage failed!");
   }
 
-  NS_ConvertUTF16toUTF8 msg(aMessage);
-  NS_ConvertUTF16toUTF8 filename(aFilename);
+  NS_ConvertUTF16toUTF8 msg(aReport.mMessage);
+  NS_ConvertUTF16toUTF8 filename(aReport.mFilename);
 
   static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
 
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
-                      filename.get(), aLineNumber);
+                      filename.get(), aReport.mLineNumber);
 #endif
 
-  fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
+  fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber);
   fflush(stderr);
 }
 
 class MainThreadReleaseRunnable final : public Runnable
 {
   nsTArray<nsCOMPtr<nsISupports>> mDoomed;
   nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
 
@@ -515,20 +523,17 @@ private:
     if (aWorkerPrivate->IsFrozen() ||
         aWorkerPrivate->IsParentWindowPaused()) {
       MOZ_ASSERT(!IsDebuggerRunnable());
       aWorkerPrivate->QueueRunnable(this);
       return true;
     }
 
     if (aWorkerPrivate->IsSharedWorker()) {
-      aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, EmptyString(),
-                                                    EmptyString(),
-                                                    EmptyString(), 0, 0,
-                                                    JSREPORT_ERROR,
+      aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, nullptr,
                                                     /* isErrorEvent */ false);
       return true;
     }
 
     if (aWorkerPrivate->IsServiceWorker()) {
       RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
       if (swm) {
         swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
@@ -986,58 +991,46 @@ private:
     MOZ_ASSERT_IF(!parent, NS_IsMainThread());
     Report(parent, mMessage);
     return true;
   }
 };
 
 class ReportErrorRunnable final : public WorkerRunnable
 {
-  nsString mMessage;
-  nsString mFilename;
-  nsString mLine;
-  uint32_t mLineNumber;
-  uint32_t mColumnNumber;
-  uint32_t mFlags;
-  uint32_t mErrorNumber;
-  JSExnType mExnType;
-  bool mMutedError;
+  WorkerErrorReport mReport;
 
 public:
   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
   // aTarget is the worker object that we are going to fire an error at
   // (if any).
   static void
   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
               bool aFireAtScope, WorkerPrivate* aTarget,
-              const nsString& aMessage, const nsString& aFilename,
-              const nsString& aLine, uint32_t aLineNumber,
-              uint32_t aColumnNumber, uint32_t aFlags,
-              uint32_t aErrorNumber, JSExnType aExnType,
-              bool aMutedError, uint64_t aInnerWindowId,
+              const WorkerErrorReport& aReport, uint64_t aInnerWindowId,
               JS::Handle<JS::Value> aException = JS::NullHandleValue)
   {
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     } else {
       AssertIsOnMainThread();
     }
 
     // We should not fire error events for warnings but instead make sure that
     // they show up in the error console.
-    if (!JSREPORT_IS_WARNING(aFlags)) {
+    if (!JSREPORT_IS_WARNING(aReport.mFlags)) {
       // First fire an ErrorEvent at the worker.
       RootedDictionary<ErrorEventInit> init(aCx);
 
-      if (aMutedError) {
+      if (aReport.mMutedError) {
         init.mMessage.AssignLiteral("Script error.");
       } else {
-        init.mMessage = aMessage;
-        init.mFilename = aFilename;
-        init.mLineno = aLineNumber;
+        init.mMessage = aReport.mMessage;
+        init.mFilename = aReport.mFilename;
+        init.mLineno = aReport.mLineNumber;
         init.mError = aException;
       }
 
       init.mCancelable = true;
       init.mBubbles = false;
 
       if (aTarget) {
         RefPtr<ErrorEvent> event =
@@ -1054,35 +1047,36 @@ public:
 
       // Now fire an event at the global object, but don't do that if the error
       // code is too much recursion and this is the same script threw the error.
       // XXXbz the interaction of this with worker errors seems kinda broken.
       // An overrecursion in the debugger or debugger sandbox will get turned
       // into an error event on our parent worker!
       // https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this
       // better.
-      if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
+      if (aFireAtScope &&
+          (aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) {
         JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
         NS_ASSERTION(global, "This should never be null!");
 
         nsEventStatus status = nsEventStatus_eIgnore;
         nsIScriptGlobalObject* sgo;
 
         if (aWorkerPrivate) {
           WorkerGlobalScope* globalScope = nullptr;
           UNWRAP_OBJECT(WorkerGlobalScope, global, globalScope);
 
           if (!globalScope) {
             WorkerDebuggerGlobalScope* globalScope = nullptr;
             UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalScope);
 
             MOZ_ASSERT_IF(globalScope, globalScope->GetWrapperPreserveColor() == global);
             if (globalScope || IsDebuggerSandbox(global)) {
-              aWorkerPrivate->ReportErrorToDebugger(aFilename, aLineNumber,
-                                                    aMessage);
+              aWorkerPrivate->ReportErrorToDebugger(aReport.mFilename, aReport.mLineNumber,
+                                                    aReport.mMessage);
               return;
             }
 
             MOZ_ASSERT(SimpleGlobalObject::SimpleGlobalType(global) ==
                          SimpleGlobalObject::GlobalType::BindingDetail);
             // XXXbz We should really log this to console, but unwinding out of
             // this stuff without ending up firing any events is ... hard.  Just
             // return for now.
@@ -1119,38 +1113,30 @@ public:
           return;
         }
       }
     }
 
     // Now fire a runnable to do the same on the parent's thread if we can.
     if (aWorkerPrivate) {
       RefPtr<ReportErrorRunnable> runnable =
-        new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
-                                aLineNumber, aColumnNumber, aFlags,
-                                aErrorNumber, aExnType, aMutedError);
+        new ReportErrorRunnable(aWorkerPrivate, aReport);
       runnable->Dispatch();
       return;
     }
 
     // Otherwise log an error to the error console.
-    LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
-                      aFlags, aInnerWindowId);
+    LogErrorToConsole(aReport, aInnerWindowId);
   }
 
 private:
-  ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
-                      const nsString& aFilename, const nsString& aLine,
-                      uint32_t aLineNumber, uint32_t aColumnNumber,
-                      uint32_t aFlags, uint32_t aErrorNumber,
-                      JSExnType aExnType, bool aMutedError)
+  ReportErrorRunnable(WorkerPrivate* aWorkerPrivate,
+                      const WorkerErrorReport& aReport)
   : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
-    mMessage(aMessage), mFilename(aFilename), mLine(aLine),
-    mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
-    mErrorNumber(aErrorNumber), mExnType(aExnType), mMutedError(aMutedError)
+    mReport(aReport)
   { }
 
   virtual void
   PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // Dispatch may fail if the worker was canceled, no need to report that as
@@ -1177,35 +1163,34 @@ private:
       if (aWorkerPrivate->IsFrozen() ||
           aWorkerPrivate->IsParentWindowPaused()) {
         MOZ_ASSERT(!IsDebuggerRunnable());
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
       if (aWorkerPrivate->IsSharedWorker()) {
-        aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
-                                                      mLine, mLineNumber,
-                                                      mColumnNumber, mFlags,
+        aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, &mReport,
                                                       /* isErrorEvent */ true);
         return true;
       }
 
       // Service workers do not have a main thread parent global, so normal
       // worker error reporting will crash.  Instead, pass the error to
       // the ServiceWorkerManager to report on any controlled documents.
       if (aWorkerPrivate->IsServiceWorker()) {
         RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
         if (swm) {
           swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
                            aWorkerPrivate->WorkerName(),
                            aWorkerPrivate->ScriptURL(),
-                           mMessage,
-                           mFilename, mLine, mLineNumber,
-                           mColumnNumber, mFlags, mExnType);
+                           mReport.mMessage,
+                           mReport.mFilename, mReport.mLine, mReport.mLineNumber,
+                           mReport.mColumnNumber, mReport.mFlags,
+                           mReport.mExnType);
         }
         return true;
       }
 
       // The innerWindowId is only required if we are going to ReportError
       // below, which is gated on this condition. The inner window correctness
       // check is only going to succeed when the worker is accepting events.
       if (workerIsAcceptingEvents) {
@@ -1215,19 +1200,18 @@ private:
     }
 
     // Don't fire this event if the JS object has been disconnected from the
     // private object.
     if (!workerIsAcceptingEvents) {
       return true;
     }
 
-    ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage,
-                mFilename, mLine, mLineNumber, mColumnNumber, mFlags,
-                mErrorNumber, mExnType, mMutedError, innerWindowId);
+    ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport,
+                innerWindowId);
     return true;
   }
 };
 
 class TimerRunnable final : public WorkerRunnable,
                             public nsITimerCallback
 {
 public:
@@ -3565,31 +3549,26 @@ WorkerPrivateParent<Derived>::RegisterSh
 
   return true;
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
                                                     JSContext* aCx,
-                                                    const nsAString& aMessage,
-                                                    const nsAString& aFilename,
-                                                    const nsAString& aLine,
-                                                    uint32_t aLineNumber,
-                                                    uint32_t aColumnNumber,
-                                                    uint32_t aFlags,
+                                                    const WorkerErrorReport* aReport,
                                                     bool aIsErrorEvent)
 {
   AssertIsOnMainThread();
 
-  if (JSREPORT_IS_WARNING(aFlags)) {
+  if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
     // Don't fire any events anywhere.  Just log to console.
     // XXXbz should we log to all the consoles of all the relevant windows?
-    LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
-                      aFlags, 0);
+    MOZ_ASSERT(aReport);
+    LogErrorToConsole(*aReport, 0);
     return;
   }
 
   AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
   GetAllSharedWorkers(sharedWorkers);
 
   if (sharedWorkers.IsEmpty()) {
     return;
@@ -3608,20 +3587,20 @@ WorkerPrivateParent<Derived>::BroadcastE
     nsPIDOMWindowInner* window = sharedWorker->GetOwner();
 
     RefPtr<Event> event;
 
     if (aIsErrorEvent) {
       RootedDictionary<ErrorEventInit> errorInit(aCx);
       errorInit.mBubbles = false;
       errorInit.mCancelable = true;
-      errorInit.mMessage = aMessage;
-      errorInit.mFilename = aFilename;
-      errorInit.mLineno = aLineNumber;
-      errorInit.mColno = aColumnNumber;
+      errorInit.mMessage = aReport->mMessage;
+      errorInit.mFilename = aReport->mFilename;
+      errorInit.mLineno = aReport->mLineNumber;
+      errorInit.mColno = aReport->mColumnNumber;
 
       event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
                                       errorInit);
     } else {
       event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
                                  EventInit());
     }
 
@@ -3677,19 +3656,19 @@ WorkerPrivateParent<Derived>::BroadcastE
     }
 
     nsCOMPtr<nsIScriptGlobalObject> sgo =
       do_QueryInterface(windowAction.mWindow);
     MOZ_ASSERT(sgo);
 
     MOZ_ASSERT(NS_IsMainThread());
     RootedDictionary<ErrorEventInit> init(aCx);
-    init.mLineno = aLineNumber;
-    init.mFilename = aFilename;
-    init.mMessage = aMessage;
+    init.mLineno = aReport->mLineNumber;
+    init.mFilename = aReport->mFilename;
+    init.mMessage = aReport->mMessage;
     init.mCancelable = true;
     init.mBubbles = true;
 
     nsEventStatus status = nsEventStatus_eIgnore;
     rv = sgo->HandleScriptError(init, &status);
     if (NS_FAILED(rv)) {
       ThrowAndReport(windowAction.mWindow, rv);
       continue;
@@ -3697,18 +3676,18 @@ WorkerPrivateParent<Derived>::BroadcastE
 
     if (status == nsEventStatus_eConsumeNoDefault) {
       shouldLogErrorToConsole = false;
     }
   }
 
   // Finally log a warning in the console if no window tried to prevent it.
   if (shouldLogErrorToConsole) {
-    LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
-                      aFlags, 0);
+    MOZ_ASSERT(aReport);
+    LogErrorToConsole(*aReport, 0);
   }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::GetAllSharedWorkers(
                                nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
 {
@@ -4364,17 +4343,20 @@ WorkerDebugger::ReportErrorToDebuggerOnM
 {
   AssertIsOnMainThread();
 
   nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnError(aFilename, aLineno, aMessage);
   }
 
-  LogErrorToConsole(aMessage, aFilename, nsString(), aLineno, 0, 0, 0);
+  WorkerErrorReport report;
+  report.mMessage = aMessage;
+  report.mFilename = aFilename;
+  LogErrorToConsole(report, 0);
 }
 
 WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsACString& aWorkerName,
                              WorkerLoadInfo& aLoadInfo)
   : WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
@@ -6163,16 +6145,57 @@ WorkerPrivate::NotifyInternal(JSContext*
              aStatus == Canceling ||
              aStatus == Killing);
 
   // Always abort the script.
   return false;
 }
 
 void
+WorkerErrorBase::AssignErrorBase(JSErrorBase* aReport)
+{
+  mFilename = NS_ConvertUTF8toUTF16(aReport->filename);
+  mLineNumber = aReport->lineno;
+  mColumnNumber = aReport->column;
+  mErrorNumber = aReport->errorNumber;
+}
+
+void
+WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote)
+{
+  WorkerErrorBase::AssignErrorBase(aNote);
+  xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage);
+}
+
+void
+WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport)
+{
+  WorkerErrorBase::AssignErrorBase(aReport);
+  xpc::ErrorReport::ErrorReportToMessageString(aReport, mMessage);
+
+  mLine.Assign(aReport->linebuf(), aReport->linebufLength());
+  mFlags = aReport->flags;
+  MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
+  mExnType = JSExnType(aReport->exnType);
+  mMutedError = aReport->isMuted;
+
+  if (aReport->notes) {
+    if (!mNotes.SetLength(aReport->notes->length(), fallible)) {
+      return;
+    }
+
+    size_t i = 0;
+    for (auto&& note : *aReport->notes) {
+      mNotes.ElementAt(i).AssignErrorNote(note.get());
+      i++;
+    }
+  }
+}
+
+void
 WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
                            JSErrorReport* aReport)
 {
   AssertIsOnWorkerThread();
 
   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
     return;
   }
@@ -6184,67 +6207,50 @@ WorkerPrivate::ReportError(JSContext* aC
   JS::Rooted<JS::Value> exn(aCx);
   if (!JS_GetPendingException(aCx, &exn)) {
     // Probably shouldn't actually happen?  But let's go ahead and just use null
     // for lack of anything better.
     exn.setNull();
   }
   JS_ClearPendingException(aCx);
 
-  nsString message, filename, line;
-  uint32_t lineNumber, columnNumber, flags, errorNumber;
-  JSExnType exnType = JSEXN_ERR;
-  bool mutedError = aReport && aReport->isMuted;
-
+  WorkerErrorReport report;
   if (aReport) {
-    // We want the same behavior here as xpc::ErrorReport::init here.
-    xpc::ErrorReport::ErrorReportToMessageString(aReport, message);
-
-    filename = NS_ConvertUTF8toUTF16(aReport->filename);
-    line.Assign(aReport->linebuf(), aReport->linebufLength());
-    lineNumber = aReport->lineno;
-    columnNumber = aReport->tokenOffset();
-    flags = aReport->flags;
-    errorNumber = aReport->errorNumber;
-    MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
-    exnType = JSExnType(aReport->exnType);
+    report.AssignErrorReport(aReport);
   }
   else {
-    lineNumber = columnNumber = errorNumber = 0;
-    flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
-  }
-
-  if (message.IsEmpty() && aToStringResult) {
+    report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
+  }
+
+  if (report.mMessage.IsEmpty() && aToStringResult) {
     nsDependentCString toStringResult(aToStringResult.c_str());
-    if (!AppendUTF8toUTF16(toStringResult, message, mozilla::fallible)) {
+    if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) {
       // Try again, with only a 1 KB string. Do this infallibly this time.
       // If the user doesn't have 1 KB to spare we're done anyways.
       uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
 
       // Drop the last code point that may be cropped.
       index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
 
       nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
                                                  index);
-      AppendUTF8toUTF16(truncatedToStringResult, message);
+      AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
     }
   }
 
   mErrorHandlerRecursionCount++;
 
   // Don't want to run the scope's error handler if this is a recursive error or
   // if we ran out of memory.
   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
-                     errorNumber != JSMSG_OUT_OF_MEMORY &&
+                     report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
                      JS::CurrentGlobalOrNull(aCx);
 
-  ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
-                                   filename, line, lineNumber,
-                                   columnNumber, flags, errorNumber, exnType,
-                                   mutedError, 0, exn);
+  ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
+                                   exn);
 
   mErrorHandlerRecursionCount--;
 }
 
 // static
 void
 WorkerPrivate::ReportErrorToConsole(const char* aMessage)
 {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -136,16 +136,55 @@ public:
 
   void
   AssertCurrentThreadOwns() const
   {
     mMutex->AssertCurrentThreadOwns();
   }
 };
 
+class WorkerErrorBase {
+public:
+  nsString mMessage;
+  nsString mFilename;
+  uint32_t mLineNumber;
+  uint32_t mColumnNumber;
+  uint32_t mErrorNumber;
+
+  WorkerErrorBase()
+  : mLineNumber(0),
+    mColumnNumber(0),
+    mErrorNumber(0)
+  { }
+
+  void AssignErrorBase(JSErrorBase* aReport);
+};
+
+class WorkerErrorNote : public WorkerErrorBase {
+public:
+  void AssignErrorNote(JSErrorNotes::Note* aNote);
+};
+
+class WorkerErrorReport : public WorkerErrorBase {
+public:
+  nsString mLine;
+  uint32_t mFlags;
+  JSExnType mExnType;
+  bool mMutedError;
+  nsTArray<WorkerErrorNote> mNotes;
+
+  WorkerErrorReport()
+  : mFlags(0),
+    mExnType(JSEXN_ERR),
+    mMutedError(false)
+  { }
+
+  void AssignErrorReport(JSErrorReport* aReport);
+};
+
 template <class Derived>
 class WorkerPrivateParent : public DOMEventTargetHelper
 {
 protected:
   class EventTarget;
   friend class EventTarget;
 
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
@@ -390,22 +429,17 @@ public:
   void
   MemoryPressure(bool aDummy);
 
   bool
   RegisterSharedWorker(SharedWorker* aSharedWorker, MessagePort* aPort);
 
   void
   BroadcastErrorToSharedWorkers(JSContext* aCx,
-                                const nsAString& aMessage,
-                                const nsAString& aFilename,
-                                const nsAString& aLine,
-                                uint32_t aLineNumber,
-                                uint32_t aColumnNumber,
-                                uint32_t aFlags,
+                                const WorkerErrorReport* aReport,
                                 bool aIsErrorEvent);
 
   void
   WorkerScriptLoaded();
 
   void
   QueueRunnable(nsIRunnable* aRunnable)
   {
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -89,16 +89,17 @@ IPDL_SOURCES += [
     'PServiceWorkerManager.ipdl',
     'ServiceWorkerRegistrarTypes.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/dom/base',
+    '/dom/bindings',
     '/xpcom/build',
     '/xpcom/threads',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/dom/workers/test/test_sharedWorker.html
+++ b/dom/workers/test/test_sharedWorker.html
@@ -18,17 +18,17 @@
         "use strict";
 
         const href = window.location.href;
         const filename = "sharedWorker_sharedWorker.js";
         const sentMessage = "ping";
         const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) +
                               filename;
         const errorLine = 90;
-        const errorColumn = 0;
+        const errorColumn = 11;
 
         var worker = new SharedWorker(filename);
 
         ok(worker instanceof SharedWorker, "Got SharedWorker instance");
         ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'");
         ok(worker.port instanceof MessagePort,
           "Shared worker has MessagePort");