Bug 1283712 - Part 7: Add nsIScriptErrorNote and nsIScriptError.notes. r=bholley
authorTooru Fujisawa <arai_a@mac.com>
Wed, 15 Feb 2017 23:53:06 +0900
changeset 343091 de4875e04b7747d22377009d016feec433e666d1
parent 343090 003d483f64e0bf69ff6e7d1e9b02a9b74dfa4cc4
child 343092 ca906f20c23da421a614df6b9f16ee7711ed84cb
push id31369
push userkwierso@gmail.com
push dateThu, 16 Feb 2017 00:18:40 +0000
treeherdermozilla-central@e9b926463f9e [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 7: Add nsIScriptErrorNote and nsIScriptError.notes. r=bholley
dom/bindings/nsIScriptError.idl
dom/bindings/nsScriptError.cpp
dom/bindings/nsScriptError.h
js/xpconnect/src/nsXPConnect.cpp
--- a/dom/bindings/nsIScriptError.idl
+++ b/dom/bindings/nsIScriptError.idl
@@ -4,23 +4,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * nsIConsoleMessage subclass for representing JavaScript errors and warnings.
  */
 
 
 #include "nsISupports.idl"
+#include "nsIArray.idl"
 #include "nsIConsoleMessage.idl"
 
 %{C++
 #include "nsStringGlue.h" // for nsDependentCString
 %}
 
-[scriptable, uuid(361be358-76f0-47aa-b37b-6ad833599e8d)]
+[scriptable, uuid(e8933fc9-c302-4e12-a55b-4f88611d9c6c)]
+interface nsIScriptErrorNote : nsISupports
+{
+    readonly attribute AString errorMessage;
+    readonly attribute AString sourceName;
+    readonly attribute uint32_t lineNumber;
+    readonly attribute uint32_t columnNumber;
+
+    AUTF8String toString();
+};
+
+[scriptable, uuid(63eb4d3e-7d99-4150-b4f3-11314f9d82a9)]
 interface nsIScriptError : nsIConsoleMessage
 {
     /** pseudo-flag for default case */
     const unsigned long errorFlag = 0x0;
 
     /** message is warning */
     const unsigned long warningFlag = 0x1;
 
@@ -69,16 +81,17 @@ interface nsIScriptError : nsIConsoleMes
     attribute jsval stack;
 
     /**
      * The name of a template string, as found in js.msg, associated with the
      * error message.
      */
     attribute AString errorMessageName;
 
+    readonly attribute nsIArray notes;
 
     void init(in AString message,
               in AString sourceName,
               in AString sourceLine,
               in uint32_t lineNumber,
               in uint32_t columnNumber,
               in uint32_t flags,
               in string category);
--- a/dom/bindings/nsScriptError.cpp
+++ b/dom/bindings/nsScriptError.cpp
@@ -12,16 +12,17 @@
 #include "jsprf.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "nsGlobalWindow.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsILoadContext.h"
 #include "nsIDocShell.h"
+#include "nsIMutableArray.h"
 #include "nsIScriptError.h"
 #include "nsISensitiveInfoHiddenURI.h"
 
 static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR &&
               nsIScriptError::warningFlag == JSREPORT_WARNING &&
               nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION &&
               nsIScriptError::strictFlag == JSREPORT_STRICT &&
               nsIScriptError::infoFlag == JSREPORT_USER_1,
@@ -42,16 +43,22 @@ nsScriptErrorBase::nsScriptErrorBase()
        mInitializedOnMainThread(false),
        mIsFromPrivateWindow(false)
 {
 }
 
 nsScriptErrorBase::~nsScriptErrorBase() {}
 
 void
+nsScriptErrorBase::AddNote(nsIScriptErrorNote* note)
+{
+    mNotes.AppendObject(note);
+}
+
+void
 nsScriptErrorBase::InitializeOnMainThread()
 {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(!mInitializedOnMainThread);
 
     if (mInnerWindowID) {
         nsGlobalWindow* window =
           nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID);
@@ -184,109 +191,110 @@ nsScriptErrorBase::Init(const nsAString&
 {
     return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
                             columnNumber, flags,
                             category ? nsDependentCString(category)
                                      : EmptyCString(),
                             0);
 }
 
+static void
+AssignSourceNameHelper(nsString& aSourceNameDest, const nsAString& aSourceNameSrc)
+{
+    if (aSourceNameSrc.IsEmpty())
+        return;
+
+    aSourceNameDest.Assign(aSourceNameSrc);
+
+    nsCOMPtr<nsIURI> uri;
+    nsAutoCString pass;
+    if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), aSourceNameSrc)) &&
+        NS_SUCCEEDED(uri->GetPassword(pass)) &&
+        !pass.IsEmpty())
+    {
+        nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri = do_QueryInterface(uri);
+
+        nsAutoCString loc;
+        if (safeUri && NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc)))
+            aSourceNameDest.Assign(NS_ConvertUTF8toUTF16(loc));
+    }
+}
+
 NS_IMETHODIMP
 nsScriptErrorBase::InitWithWindowID(const nsAString& message,
                                     const nsAString& sourceName,
                                     const nsAString& sourceLine,
                                     uint32_t lineNumber,
                                     uint32_t columnNumber,
                                     uint32_t flags,
                                     const nsACString& category,
                                     uint64_t aInnerWindowID)
 {
     mMessage.Assign(message);
-
-    if (!sourceName.IsEmpty()) {
-        mSourceName.Assign(sourceName);
-
-        nsCOMPtr<nsIURI> uri;
-        nsAutoCString pass;
-        if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), sourceName)) &&
-            NS_SUCCEEDED(uri->GetPassword(pass)) &&
-            !pass.IsEmpty()) {
-            nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri =
-                do_QueryInterface(uri);
-
-            nsAutoCString loc;
-            if (safeUri &&
-                NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) {
-                mSourceName.Assign(NS_ConvertUTF8toUTF16(loc));
-            }
-        }
-    }
-
+    AssignSourceNameHelper(mSourceName, sourceName);
     mLineNumber = lineNumber;
     mSourceLine.Assign(sourceLine);
     mColumnNumber = columnNumber;
     mFlags = flags;
     mCategory = category;
     mTimeStamp = JS_Now() / 1000;
     mInnerWindowID = aInnerWindowID;
 
-    if (aInnerWindowID && NS_IsMainThread()) {
+    if (aInnerWindowID && NS_IsMainThread())
         InitializeOnMainThread();
-    }
 
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult)
+static nsresult
+ToStringHelper(const char* aSeverity, const nsString& aMessage,
+               const nsString& aSourceName, const nsString* aSourceLine,
+               uint32_t aLineNumber, uint32_t aColumnNumber,
+               nsACString& /*UTF8*/ aResult)
 {
     static const char format0[] =
         "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]";
     static const char format1[] =
         "[%s: \"%s\" {file: \"%s\" line: %d}]";
     static const char format2[] =
         "[%s: \"%s\"]";
 
-    static const char error[]   = "JavaScript Error";
-    static const char warning[] = "JavaScript Warning";
-
-    const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning;
-
     char* temp;
     char* tempMessage = nullptr;
     char* tempSourceName = nullptr;
     char* tempSourceLine = nullptr;
 
-    if (!mMessage.IsEmpty())
-        tempMessage = ToNewUTF8String(mMessage);
-    if (!mSourceName.IsEmpty())
+    if (!aMessage.IsEmpty())
+        tempMessage = ToNewUTF8String(aMessage);
+    if (!aSourceName.IsEmpty())
         // Use at most 512 characters from mSourceName.
-        tempSourceName = ToNewUTF8String(StringHead(mSourceName, 512));
-    if (!mSourceLine.IsEmpty())
+        tempSourceName = ToNewUTF8String(StringHead(aSourceName, 512));
+    if (aSourceLine && !aSourceLine->IsEmpty())
         // Use at most 512 characters from mSourceLine.
-        tempSourceLine = ToNewUTF8String(StringHead(mSourceLine, 512));
+        tempSourceLine = ToNewUTF8String(StringHead(*aSourceLine, 512));
 
-    if (nullptr != tempSourceName && nullptr != tempSourceLine)
+    if (nullptr != tempSourceName && nullptr != tempSourceLine) {
         temp = JS_smprintf(format0,
-                           severity,
+                           aSeverity,
                            tempMessage,
                            tempSourceName,
-                           mLineNumber,
-                           mColumnNumber,
+                           aLineNumber,
+                           aColumnNumber,
                            tempSourceLine);
-    else if (!mSourceName.IsEmpty())
+    } else if (!aSourceName.IsEmpty()) {
         temp = JS_smprintf(format1,
-                           severity,
+                           aSeverity,
                            tempMessage,
                            tempSourceName,
-                           mLineNumber);
-    else
+                           aLineNumber);
+    } else {
         temp = JS_smprintf(format2,
-                           severity,
+                           aSeverity,
                            tempMessage);
+    }
 
     if (nullptr != tempMessage)
         free(tempMessage);
     if (nullptr != tempSourceName)
         free(tempSourceName);
     if (nullptr != tempSourceLine)
         free(tempSourceLine);
 
@@ -294,16 +302,28 @@ nsScriptErrorBase::ToString(nsACString& 
         return NS_ERROR_OUT_OF_MEMORY;
 
     aResult.Assign(temp);
     JS_smprintf_free(temp);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult)
+{
+    static const char error[] = "JavaScript Error";
+    static const char warning[] = "JavaScript Warning";
+
+    const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning;
+
+    return ToStringHelper(severity, mMessage, mSourceName, &mSourceLine,
+                          mLineNumber, mColumnNumber, aResult);
+}
+
+NS_IMETHODIMP
 nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID)
 {
     NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
                          "This can't be safely determined off the main thread, "
                          "returning an inaccurate value!");
 
     if (!mInitializedOnMainThread && NS_IsMainThread()) {
         InitializeOnMainThread();
@@ -337,9 +357,81 @@ nsScriptErrorBase::GetIsFromPrivateWindo
     if (!mInitializedOnMainThread && NS_IsMainThread()) {
         InitializeOnMainThread();
     }
 
     *aIsFromPrivateWindow = mIsFromPrivateWindow;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsScriptErrorBase::GetNotes(nsIArray** aNotes)
+{
+    nsresult rv = NS_OK;
+    nsCOMPtr<nsIMutableArray> array =
+        do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t len = mNotes.Length();
+    for (uint32_t i = 0; i < len; i++)
+        array->AppendElement(mNotes[i], false);
+    array.forget(aNotes);
+
+    return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError)
+
+nsScriptErrorNote::nsScriptErrorNote()
+    :  mMessage(),
+       mSourceName(),
+       mLineNumber(0),
+       mColumnNumber(0)
+{
+}
+
+nsScriptErrorNote::~nsScriptErrorNote() {}
+
+void
+nsScriptErrorNote::Init(const nsAString& message,
+                        const nsAString& sourceName,
+                        uint32_t lineNumber,
+                        uint32_t columnNumber)
+{
+    mMessage.Assign(message);
+    AssignSourceNameHelper(mSourceName, sourceName);
+    mLineNumber = lineNumber;
+    mColumnNumber = columnNumber;
+}
+
+// nsIScriptErrorNote methods
+NS_IMETHODIMP
+nsScriptErrorNote::GetErrorMessage(nsAString& aResult) {
+    aResult.Assign(mMessage);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptErrorNote::GetSourceName(nsAString& aResult) {
+    aResult.Assign(mSourceName);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptErrorNote::GetLineNumber(uint32_t* result) {
+    *result = mLineNumber;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptErrorNote::GetColumnNumber(uint32_t* result) {
+    *result = mColumnNumber;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptErrorNote::ToString(nsACString& /*UTF8*/ aResult)
+{
+    return ToStringHelper("JavaScript Note", mMessage, mSourceName, nullptr,
+                          mLineNumber, mColumnNumber, aResult);
+}
+
+NS_IMPL_ISUPPORTS(nsScriptErrorNote, nsIScriptErrorNote)
--- a/dom/bindings/nsScriptError.h
+++ b/dom/bindings/nsScriptError.h
@@ -12,30 +12,53 @@
 #include <stdint.h>
 
 #include "jsapi.h"
 #include "js/RootingAPI.h"
 
 #include "nsIScriptError.h"
 #include "nsString.h"
 
+class nsScriptErrorNote final : public nsIScriptErrorNote {
+ public:
+  nsScriptErrorNote();
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISCRIPTERRORNOTE
+
+  void Init(const nsAString& message, const nsAString& sourceName,
+            uint32_t lineNumber, uint32_t columnNumber);
+
+ private:
+  virtual ~nsScriptErrorNote();
+
+  nsString mMessage;
+  nsString mSourceName;
+  nsString mSourceLine;
+  uint32_t mLineNumber;
+  uint32_t mColumnNumber;
+};
+
 // Definition of nsScriptError..
 class nsScriptErrorBase : public nsIScriptError {
 public:
   nsScriptErrorBase();
 
   NS_DECL_NSICONSOLEMESSAGE
   NS_DECL_NSISCRIPTERROR
 
+  void AddNote(nsIScriptErrorNote* note);
+
 protected:
   virtual ~nsScriptErrorBase();
 
   void
   InitializeOnMainThread();
 
+  nsCOMArray<nsIScriptErrorNote> mNotes;
   nsString mMessage;
   nsString mMessageName;
   nsString mSourceName;
   uint32_t mLineNumber;
   nsString mSourceLine;
   uint32_t mColumnNumber;
   uint32_t mFlags;
   nsCString mCategory;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -315,34 +315,44 @@ xpc::ErrorReport::LogToConsoleWithStack(
             ("file %s, line %u\n%s", NS_LossyConvertUTF16toASCII(mFileName).get(),
              mLineNumber, NS_LossyConvertUTF16toASCII(mErrorMsg).get()));
 
     // Log to the console. We do this last so that we can simply return if
     // there's no console service without affecting the other reporting
     // mechanisms.
     nsCOMPtr<nsIConsoleService> consoleService =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    NS_ENSURE_TRUE_VOID(consoleService);
 
-    nsCOMPtr<nsIScriptError> errorObject;
+    RefPtr<nsScriptErrorBase> errorObject;
     if (mWindowID && aStack) {
       // Only set stack on messages related to a document
       // As we cache messages in the console service,
       // we have to ensure not leaking them after the related
       // context is destroyed and we only track document lifecycle for now.
       errorObject = new nsScriptErrorWithStack(aStack);
     } else {
       errorObject = new nsScriptError();
     }
     errorObject->SetErrorMessageName(mErrorMsgName);
-    NS_ENSURE_TRUE_VOID(consoleService);
 
     nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,
                                                 mLineNumber, mColumn, mFlags,
                                                 mCategory, mWindowID);
     NS_ENSURE_SUCCESS_VOID(rv);
+
+    for (size_t i = 0, len = mNotes.Length(); i < len; i++) {
+        ErrorNote& note = mNotes[i];
+
+        nsScriptErrorNote* noteObject = new nsScriptErrorNote();
+        noteObject->Init(note.mErrorMsg, note.mFileName,
+                         note.mLineNumber, note.mColumn);
+        errorObject->AddNote(noteObject);
+    }
+
     consoleService->LogMessage(errorObject);
 
 }
 
 /* static */
 void
 xpc::ErrorNote::ErrorNoteToMessageString(JSErrorNotes::Note* aNote,
                                          nsAString& aString)