Bug 1107706: Part 6: Make SpecialPowersError a prototypal Error
authorAndreas Tolfsen <ato@mozilla.com>
Thu, 26 Feb 2015 17:40:21 +0000
changeset 266311 8b695334df946117685f9f942884c965c022c979
parent 266310 6613699885faa8358c049d902041ae9dbe792a9e
child 266312 853e7da581097725fa815a95bb92172ef6f5dcce
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1107706, 843892, 1014484
milestone39.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 1107706: Part 6: Make SpecialPowersError a prototypal Error If special powers is imported using Components.utils.import on B2G which has some special concepts around global scoping, a TypeError will be raised unless the custom error is a prototypal Error. An explanation can be found for a similar issue in bug 843892, which states that toString is attached to the instances rather than the prototype, and that this causes problems once the object goes through Object.freeze. It was patched in bug 1014484. This patch also renames SpecialPowersException to SpecialPowersError.
testing/specialpowers/content/SpecialPowersObserverAPI.js
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -1,35 +1,34 @@
 /* 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/. */
+
 "use strict";
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 if (typeof(Ci) == 'undefined') {
   var Ci = Components.interfaces;
 }
 
 if (typeof(Cc) == 'undefined') {
   var Cc = Components.classes;
 }
 
-/**
- * Special Powers Exception - used to throw exceptions nicely
- **/
-this.SpecialPowersException = function SpecialPowersException(aMsg) {
+this.SpecialPowersError = function(aMsg) {
+  Error.call(this);
+  let {stack} = new Error();
   this.message = aMsg;
-  this.name = "SpecialPowersException";
+  this.name = "SpecialPowersError";
 }
+SpecialPowersError.prototype = Object.create(Error.prototype);
 
-SpecialPowersException.prototype = {
-  toString: function SPE_toString() {
-    return this.name + ': "' + this.message + '"';
-  }
+SpecialPowersError.prototype.toString = function() {
+  return `${this.name}: ${this.message}`;
 };
 
 this.SpecialPowersObserverAPI = function SpecialPowersObserverAPI() {
   this._crashDumpDir = null;
   this._processCrashObserversRegistered = false;
   this._chromeScriptListeners = [];
 }
 
@@ -216,17 +215,17 @@ SpecialPowersObserverAPI.prototype = {
     } catch(e) {
       /* The channel is not a nsIHttpCHannel, but that's fine */
       dump("-*- _readUrlAsString: Got an error while fetching " +
            "chrome script '" + aUrl + "': (" + e.name + ") " + e.message + ". " +
            "Ignoring.\n");
     }
 
     if (status == 404) {
-      throw new SpecialPowersException(
+      throw new SpecialPowersError(
         "Error while executing chrome script '" + aUrl + "':\n" +
         "The script doesn't exists. Ensure you have registered it in " +
         "'support-files' in your mochitest.ini.");
     }
 
     return output;
   },
 
@@ -242,29 +241,29 @@ SpecialPowersObserverAPI.prototype = {
       case "SPPrefService": {
         let prefs = Services.prefs;
         let prefType = aMessage.json.prefType.toUpperCase();
         let prefName = aMessage.json.prefName;
         let prefValue = "prefValue" in aMessage.json ? aMessage.json.prefValue : null;
 
         if (aMessage.json.op == "get") {
           if (!prefName || !prefType)
-            throw new SpecialPowersException("Invalid parameters for get in SPPrefService");
+            throw new SpecialPowersError("Invalid parameters for get in SPPrefService");
 
           // return null if the pref doesn't exist
           if (prefs.getPrefType(prefName) == prefs.PREF_INVALID)
             return null;
         } else if (aMessage.json.op == "set") {
           if (!prefName || !prefType  || prefValue === null)
-            throw new SpecialPowersException("Invalid parameters for set in SPPrefService");
+            throw new SpecialPowersError("Invalid parameters for set in SPPrefService");
         } else if (aMessage.json.op == "clear") {
           if (!prefName)
-            throw new SpecialPowersException("Invalid parameters for clear in SPPrefService");
+            throw new SpecialPowersError("Invalid parameters for clear in SPPrefService");
         } else {
-          throw new SpecialPowersException("Invalid operation for SPPrefService");
+          throw new SpecialPowersError("Invalid operation for SPPrefService");
         }
 
         // Now we make the call
         switch(prefType) {
           case "BOOL":
             if (aMessage.json.op == "get")
               return(prefs.getBoolPref(prefName));
             else 
@@ -301,17 +300,17 @@ SpecialPowersObserverAPI.prototype = {
           case "unregister-observer":
             this._removeProcessCrashObservers();
             break;
           case "delete-crash-dump-files":
             return this._deleteCrashDumpFiles(aMessage.json.filenames);
           case "find-crash-dump-files":
             return this._findCrashDumpFiles(aMessage.json.crashDumpFilesToIgnore);
           default:
-            throw new SpecialPowersException("Invalid operation for SPProcessCrashService");
+            throw new SpecialPowersError("Invalid operation for SPProcessCrashService");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPPermissionManager": {
         let msg = aMessage.json;
 
         let secMan = Services.scriptSecurityManager;
@@ -333,18 +332,18 @@ SpecialPowersObserverAPI.prototype = {
           case "test":
             let testPerm = Services.perms.testPermissionFromPrincipal(principal, msg.type, msg.value);
             if (testPerm == msg.value)  {
               return true;
             }
             return false;
             break;
           default:
-            throw new SpecialPowersException("Invalid operation for " +
-                                             "SPPermissionManager");
+            throw new SpecialPowersError(
+              "Invalid operation for SPPermissionManager");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPSetTestPluginEnabledState": {
         var plugin = getTestPlugin(aMessage.data.pluginName);
         if (!plugin) {
           return undefined;
@@ -372,30 +371,30 @@ SpecialPowersObserverAPI.prototype = {
           case "debug-customizations":
             {
               let scope = {};
               Components.utils.import("resource://gre/modules/UserCustomizations.jsm", scope);
               scope.UserCustomizations._debug = aMessage.json.value;
               return;
             }
           default:
-            throw new SpecialPowersException("Invalid operation for SPWebAppsService");
+            throw new SpecialPowersError("Invalid operation for SPWebAppsService");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPObserverService": {
         switch (aMessage.json.op) {
           case "notify":
             let topic = aMessage.json.observerTopic;
             let data = aMessage.json.observerData
             Services.obs.notifyObservers(null, topic, data);
             break;
           default:
-            throw new SpecialPowersException("Invalid operation for SPObserverervice");
+            throw new SpecialPowersError("Invalid operation for SPObserverervice");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPLoadChromeScript": {
         let url = aMessage.json.url;
         let id = aMessage.json.id;
 
@@ -438,19 +437,20 @@ SpecialPowersObserverAPI.prototype = {
           },
           configurable: true
         });
 
         // Evaluate the chrome script
         try {
           Components.utils.evalInSandbox(jsScript, sb, "1.8", url, 1);
         } catch(e) {
-          throw new SpecialPowersException("Error while executing chrome " +
-                                           "script '" + url + "':\n" + e + "\n" +
-                                           e.fileName + ":" + e.lineNumber);
+          throw new SpecialPowersError(
+            "Error while executing chrome script '" + url + "':\n" +
+            e + "\n" +
+            e.fileName + ":" + e.lineNumber);
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPChromeScriptMessage": {
         let id = aMessage.json.id;
         let name = aMessage.json.name;
         let message = aMessage.json.message;
@@ -466,17 +466,17 @@ SpecialPowersObserverAPI.prototype = {
         let mm = aMessage.target
                          .QueryInterface(Ci.nsIFrameLoaderOwner)
                          .frameLoader
                          .messageManager;
         let msg = aMessage.data;
         let op = msg.op;
 
         if (op != 'clear' && op != 'getUsage') {
-          throw new SpecialPowersException('Invalid operation for SPQuotaManager');
+          throw new SpecialPowersError('Invalid operation for SPQuotaManager');
         }
 
         let uri = this._getURI(msg.uri);
 
         if (op == 'clear') {
           if (('inBrowser' in msg) && msg.inBrowser !== undefined) {
             qm.clearStoragesForURI(uri, msg.appId, msg.inBrowser);
           } else if (('appId' in msg) && msg.appId !== undefined) {
@@ -505,18 +505,17 @@ SpecialPowersObserverAPI.prototype = {
         } else {
           qm.getUsageForURI(uri, callback);
         }
 
         return undefined;	// See comment at the beginning of this function.
       }
 
       default:
-        throw new SpecialPowersException("Unrecognized Special Powers API");
+        throw new SpecialPowersError("Unrecognized Special Powers API");
     }
 
     // We throw an exception before reaching this explicit return because
     // we should never be arriving here anyway.
-    throw new SpecialPowersException("Unreached code");
+    throw new SpecialPowersError("Unreached code");
     return undefined;
   }
 };
-