Bug 1441333: Part 5 - Use proper async caller location in normalizeError. r=zombie draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 01 Mar 2018 16:39:08 -0800
changeset 762302 de4d9bfe7332f917734511a4ae9c26bdb1dde0ad
parent 762301 41d7c03ef11a5c95e5f661d231d1648abbf54fd2
child 762303 e520381bc2d02d421d4929568f3388f6753f1502
push id101126
push usermaglione.k@gmail.com
push dateFri, 02 Mar 2018 00:45:19 +0000
reviewerszombie
bugs1441333
milestone60.0a1
Bug 1441333: Part 5 - Use proper async caller location in normalizeError. r=zombie Currently, when we create an error object at the end of an aysnc operation, we only get a useful caller location if async stacks are enabled. This patch changes our behavior to use the saved caller location we've already stored when creating an Error object based on a plain string message. MozReview-Commit-ID: DDO0lAUHYRO
js/xpconnect/src/XPCJSRuntime.cpp
toolkit/components/extensions/ExtensionChild.jsm
toolkit/components/extensions/ExtensionCommon.jsm
toolkit/components/extensions/MessageChannel.jsm
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2895,16 +2895,17 @@ XPCJSRuntime::Initialize(JSContext* cx)
     mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
     mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
             DoCycleCollectionCallback);
     JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
     JS_AddWeakPointerZonesCallback(cx, WeakPointerZonesCallback, this);
     JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
+    JS_InitReadPrincipalsCallback(cx, nsJSPrincipals::ReadPrincipals);
     JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
     JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
     js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
     js::SetXrayJitInfo(&gXrayJitInfo);
     JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
 
     // The JS engine needs to keep the source code around in order to implement
     // Function.prototype.toSource(). It'd be nice to not have to do this for
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -376,17 +376,17 @@ class Messenger {
   sendMessage(messageManager, msg, recipient, responseCallback) {
     let holder = new StructuredCloneHolder(msg);
 
     let promise = this._sendMessage(messageManager, "Extension:Message", holder, recipient)
       .catch(error => {
         if (error.result == MessageChannel.RESULT_NO_HANDLER) {
           return Promise.reject({message: "Could not establish connection. Receiving end does not exist."});
         } else if (error.result != MessageChannel.RESULT_NO_RESPONSE) {
-          return Promise.reject({message: error.message});
+          return Promise.reject(error);
         }
       });
     holder = null;
 
     return this.context.wrapPromise(promise, responseCallback);
   }
 
   sendNativeMessage(messageManager, msg, recipient, responseCallback) {
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -384,33 +384,46 @@ class BaseContext {
    * the target is an error object which belongs to that scope, it is
    * returned as-is. If it is an ordinary object with a `message`
    * property, it is converted into an error belonging to the target
    * scope. If it is an Error object which does *not* belong to the
    * clone scope, it is reported, and converted to an unexpected
    * exception error.
    *
    * @param {Error|object} error
+   * @param {SavedFrame?} [caller]
    * @returns {Error}
    */
-  normalizeError(error) {
+  normalizeError(error, caller) {
     if (error instanceof this.cloneScope.Error) {
       return error;
     }
     let message, fileName;
-    if (error && typeof error === "object" &&
-        (ChromeUtils.getClassName(error) === "Object" ||
-         error instanceof ExtensionError ||
-         this.principal.subsumes(Cu.getObjectPrincipal(error)))) {
-      message = error.message;
-      fileName = error.fileName;
-    } else {
+    if (error && typeof error === "object") {
+      const isPlain = ChromeUtils.getClassName(error) === "Object";
+      if (isPlain && error.mozWebExtLocation) {
+        caller = error.mozWebExtLocation;
+      }
+      if (isPlain && caller) {
+        caller = Cu.cloneInto(caller, this.cloneScope);
+        return ChromeUtils.createError(error.message, caller);
+      }
+
+      if (isPlain ||
+          error instanceof ExtensionError ||
+          this.principal.subsumes(Cu.getObjectPrincipal(error))) {
+        message = error.message;
+        fileName = error.fileName;
+      }
+    }
+
+    if (!message) {
       Cu.reportError(error);
+      message = "An unexpected error occurred";
     }
-    message = message || "An unexpected error occurred";
     return new this.cloneScope.Error(message, fileName);
   }
 
   /**
    * Sets the value of `.lastError` to `error`, calls the given
    * callback, and reports an error if the value has not been checked
    * when the callback returns.
    *
@@ -529,17 +542,17 @@ class BaseContext {
           value => {
             if (this.unloaded) {
               Cu.reportError(`Promise rejected after context unloaded: ${value && value.message}\n`,
                              caller);
             } else if (!this.active) {
               Cu.reportError(`Promise rejected while context is inactive: ${value && value.message}\n`,
                              caller);
             } else {
-              this.applySafeWithoutClone(reject, [this.normalizeError(value)],
+              this.applySafeWithoutClone(reject, [this.normalizeError(value, caller)],
                                          caller);
             }
           });
       });
     }
   }
 
   unload() {
--- a/toolkit/components/extensions/MessageChannel.jsm
+++ b/toolkit/components/extensions/MessageChannel.jsm
@@ -968,17 +968,18 @@ this.MessageChannel = {
 
         if (error && typeof(error) == "object") {
           if (error.result) {
             response.result = error.result;
           }
           // Error objects are not structured-clonable, so just copy
           // over the important properties.
           for (let key of ["fileName", "filename", "lineNumber",
-                           "columnNumber", "message", "stack", "result"]) {
+                           "columnNumber", "message", "stack", "result",
+                           "mozWebExtLocation"]) {
             if (key in error) {
               response.error[key] = error[key];
             }
           }
         }
 
         target.sendAsyncMessage(MESSAGE_RESPONSE, response);
       })