Port bug 1246594 - Enable ESLint rule no-throw-literal by default; rs=bustage-fix DONTBUILD
authorGeoff Lankow <geoff@darktrojan.net>
Thu, 21 Mar 2019 20:45:20 +1300
changeset 34029 470f4c1ed97e2d69806d1be58b5e342fa5d825d4
parent 34028 a6833698352ab18f7596ed2571241f91202b95bb
child 34030 bbc858a8e20db63d82352a7c52a49e89e720d0a1
push id2407
push userclokep@gmail.com
push dateMon, 20 May 2019 17:11:26 +0000
treeherdercomm-beta@193428fd6fd4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage-fix
bugs1246594
Port bug 1246594 - Enable ESLint rule no-throw-literal by default; rs=bustage-fix DONTBUILD
calendar/base/content/calendar-ui-utils.js
calendar/base/modules/calHashedArray.jsm
calendar/base/src/calCalendarManager.js
calendar/base/src/calItemBase.js
calendar/providers/caldav/calDavCalendar.js
calendar/providers/storage/calStorageCalendar.js
calendar/providers/storage/calStorageUpgrade.jsm
calendar/test/unit/test_search_service.js
chat/components/src/imCommands.js
chat/components/src/imContacts.js
chat/components/src/imConversations.js
chat/components/src/logger.js
chat/content/imAccountOptionsHelper.js
chat/modules/NormalizedMap.jsm
chat/modules/imContentSink.jsm
chat/modules/imSmileys.jsm
chat/modules/imThemes.jsm
chat/modules/jsProtoHelper.jsm
chat/modules/socket.jsm
chat/protocols/gtalk/gtalk.js
chat/protocols/irc/irc.js
chat/protocols/skype/skype.js
chat/protocols/twitter/twitter.js
chat/protocols/xmpp/xmpp-authmechs.jsm
chat/protocols/xmpp/xmpp-xml.jsm
chat/protocols/xmpp/xmpp.jsm
common/bindings/menubutton.xml
common/bindings/notificationbox.xml
common/src/Overlays.jsm
mail/base/content/nsContextMenu.js
mail/base/content/nsDragAndDrop.js
mail/base/content/sanitize.js
mail/components/accountcreation/content/util.js
mail/components/accountcreation/content/verifyConfig.js
mail/components/im/content/addbuddy.js
mail/components/im/content/chat-messenger.js
mail/components/im/content/imAccountWizard.js
mail/components/im/content/imAccounts.js
mail/components/im/content/imContextMenu.js
mail/components/im/content/imconversation.xml
mail/components/im/content/imgroup.xml
mail/components/im/content/joinchat.js
mail/components/im/modules/index_im.jsm
mail/components/newmailaccount/content/accountProvisioner.js
mail/components/preferences/preferencesTab.js
mail/components/search/content/SpotlightIntegration.js
mail/test/mozmill/folder-tree-modes/test-smart-folders.js
mail/test/mozmill/shared-modules/test-mouse-event-helpers.js
mail/test/resources/jsbridge/jsbridge/extension/chrome/content/modules/server.js
mail/test/resources/mozmill/mozmill/extension/content/stdlib/EventUtils.jsm
mail/test/resources/mozmill/mozmill/extension/content/stdlib/httpd.jsm
mailnews/addrbook/test/unit/test_collection.js
mailnews/test/fakeserver/auth.js
mailnews/test/fakeserver/imapd.js
mailnews/test/fakeserver/maild.js
mailnews/test/resources/filterTestUtils.js
--- a/calendar/base/content/calendar-ui-utils.js
+++ b/calendar/base/content/calendar-ui-utils.js
@@ -489,17 +489,17 @@ function updateMenuLabelsPlural(aLengthF
  * @param menuListId    The ID of the menulist to check.
  * @param value         The value to set.
  * @throws              String error if value not found.
  */
 function menuListSelectItem(menuListId, value) {
     let menuList = document.getElementById(menuListId);
     let index = menuListIndexOf(menuList, value);
     if (index == -1) {
-        throw "menuListSelectItem: No such Element: " + value;
+        throw new Error("menuListSelectItem: No such Element: " + value);
     } else {
         menuList.selectedIndex = index;
     }
 }
 
 /**
  * Find index of menuitem with the given value, or return -1 if not found.
  *
--- a/calendar/base/modules/calHashedArray.jsm
+++ b/calendar/base/modules/calHashedArray.jsm
@@ -59,44 +59,44 @@ cal.HashedArray.prototype = {
     /**
      * Returns the item, given its hashId
      *
      * @param id            The hashId of the item to retrieve.
      * @return              The retrieved item.
      */
     itemById: function(id) {
         if (this.mBatch > 0) {
-            throw "Accessing Array by ID not supported in batch mode ";
+            throw new Error("Accessing Array by ID not supported in batch mode");
         }
         return (id in this.mHash ? this.mArray[this.mHash[id]] : null);
     },
 
     /**
      * Returns the index of the given item. This function is cheap performance
      * wise, since it uses the hash
      *
      * @param item          The item to search for.
      * @return              The index of the item.
      */
     indexOf: function(item) {
         if (this.mBatch > 0) {
-            throw "Accessing Array Indexes not supported in batch mode";
+            throw new Error("Accessing Array Indexes not supported in batch mode");
         }
         let hashId = this.hashAccessor(item);
         return (hashId in this.mHash ? this.mHash[hashId] : -1);
     },
 
     /**
      * Remove the item with the given hashId.
      *
      * @param id            The id of the item to be removed
      */
     removeById: function(id) {
         if (this.mBatch > 0) {
-            throw "Remvoing by ID in batch mode is not supported"; /* TODO */
+            throw new Error("Remvoing by ID in batch mode is not supported"); /* TODO */
         }
         let index = this.mHash[id];
         delete this.mHash[id];
         this.mArray.splice(index, 1);
         this.reindex(index);
     },
 
     /**
@@ -217,17 +217,17 @@ cal.HashedArray.prototype = {
  * Performance Considerations:
  *  - Accessing items is fast
  *  - Adding and deleting items is O(n)
  *  - Modifying items is fast.
  */
 cal.SortedHashedArray = function(comparator) {
     cal.HashedArray.apply(this, arguments);
     if (!comparator) {
-        throw "Sorted Hashed Array needs a comparator";
+        throw new Error("Sorted Hashed Array needs a comparator");
     }
     this.mCompFunc = comparator;
 };
 
 cal.SortedHashedArray.prototype = {
     __proto__: cal.HashedArray.prototype,
 
     mCompFunc: null,
--- a/calendar/base/src/calCalendarManager.js
+++ b/calendar/base/src/calCalendarManager.js
@@ -384,17 +384,17 @@ calCalendarManager.prototype = {
             if (stmt) {
                 stmt.reset();
             }
             cal.ERROR("++++++++++++ calMgrGetSchemaVersion() error: " + db.lastErrorString);
             Cu.reportError("Error getting calendar schema version! DB Error: " + db.lastErrorString);
             throw e;
         }
 
-        throw table + " SELECT returned no results";
+        throw new Error(table + " SELECT returned no results");
     },
 
     //
     // / DB migration code ends here
     //
 
     alertAndQuit: function() {
         // We want to include the extension name in the error message rather
--- a/calendar/base/src/calItemBase.js
+++ b/calendar/base/src/calItemBase.js
@@ -431,32 +431,32 @@ calItemBase.prototype = {
     setPropertyParameter: function(aPropName, aParamName, aParamValue) {
         let propName = aPropName.toUpperCase();
         let paramName = aParamName.toUpperCase();
         this.modify();
         if (!(propName in this.mPropertyParams)) {
             if (this.hasProperty(propName)) {
                 this.mPropertyParams[propName] = {};
             } else {
-                throw "Property " + aPropName + " not set";
+                throw new Error("Property " + aPropName + " not set");
             }
         }
         if (aParamValue || !isNaN(parseInt(aParamValue, 10))) {
             this.mPropertyParams[propName][paramName] = aParamValue;
         } else {
             delete this.mPropertyParams[propName][paramName];
         }
         return aParamValue;
     },
 
     // nsISimpleEnumerator getParameterEnumerator(in AString aPropertyName);
     getParameterEnumerator: function(aPropName) {
         let propName = aPropName.toUpperCase();
         if (!(propName in this.mPropertyParams)) {
-            throw "Property " + aPropName + " not set";
+            throw new Error("Property " + aPropName + " not set");
         }
         let parameters = this.mPropertyParams[propName];
         return { // nsISimpleEnumerator
             QueryInterface: ChromeUtils.generateQI([Ci.nsISimpleEnumerator]),
 
             mParamNames: Object.keys(parameters),
             hasMoreElements: function() {
                 return (this.mParamNames.length > 0);
--- a/calendar/providers/caldav/calDavCalendar.js
+++ b/calendar/providers/caldav/calDavCalendar.js
@@ -1348,17 +1348,17 @@ calDavCalendar.prototype = {
             // we'll force the renegotiation in a sync query, using OPTIONS to keep
             // it quick
             let headchannel = cal.provider.prepHttpChannel(this.makeUri(), null, null, this);
             headchannel.requestMethod = "OPTIONS";
             headchannel.open();
             headchannel.QueryInterface(Ci.nsIHttpChannel);
             try {
                 if (headchannel.responseStatus != 200) {
-                    throw "OPTIONS returned unexpected status code: " + headchannel.responseStatus;
+                    throw new Error("OPTIONS returned unexpected status code: " + headchannel.responseStatus);
                 }
             } catch (e) {
                 cal.WARN("CalDAV: Exception: " + e);
                 notifyListener(Cr.NS_ERROR_FAILURE);
             }
         }
 
         // Call getUpdatedItems right away if its the first refresh
--- a/calendar/providers/storage/calStorageCalendar.js
+++ b/calendar/providers/storage/calStorageCalendar.js
@@ -1895,17 +1895,17 @@ calStorageCalendar.prototype = {
             case "EXDATE":
                 ritem = Cc["@mozilla.org/calendar/recurrence-date;1"].createInstance(Ci.calIRecurrenceDate);
                 break;
             case "RRULE":
             case "EXRULE":
                 ritem = cal.createRecurrenceRule();
                 break;
             default:
-                throw "Unknown recurrence item: " + prop.propertyName;
+                throw new Error("Unknown recurrence item: " + prop.propertyName);
         }
 
         ritem.icalProperty = prop;
         return ritem;
     },
 
     //
     // get item from db or from cache with given iid
--- a/calendar/providers/storage/calStorageUpgrade.jsm
+++ b/calendar/providers/storage/calStorageUpgrade.jsm
@@ -173,17 +173,17 @@ function getVersion(db) {
     } catch (e) {
         throw reportErrorAndRollback(db, e);
     } finally {
         if (selectSchemaVersion) {
             selectSchemaVersion.finalize();
         }
     }
 
-    throw "cal_calendar_schema_version SELECT returned no results";
+    throw new Error("cal_calendar_schema_version SELECT returned no results");
 }
 
 /**
  * Backup the database and notify the user via error console of the process
  */
 function backupDB(db, currentVersion) {
     cal.LOG("Storage: Backing up current database...");
     try {
--- a/calendar/test/unit/test_search_service.js
+++ b/calendar/test/unit/test_search_service.js
@@ -63,17 +63,17 @@ function test_found() {
     ok(provider2.called);
 }
 
 function test_failure() {
     search.getProviders({}).forEach(search.removeProvider, search);
 
     let provider = {
         searchForCalendars: function(aStr, aHint, aMax, aListener) {
-            throw "error";
+            throw new Error("error");
         }
     };
 
     let listener = {
         called: false,
         onResult: function(request, result) {
             ok(!this.called);
             this.called = true;
--- a/chat/components/src/imCommands.js
+++ b/chat/components/src/imCommands.js
@@ -162,17 +162,17 @@ CommandsService.prototype = {
     if (aConversation)
       result = result.filter(this._usageContextFilter(aConversation));
     commandCount.value = result.length;
     return result;
   },
   // List only the commands for a protocol (excluding the global commands).
   listCommandsForProtocol(aPrplId, commandCount) {
     if (!aPrplId)
-      throw "You must provide a prpl ID.";
+      throw new Error("You must provide a prpl ID.");
 
     let result = [];
     for (let name in this._commands) {
       let commands = this._commands[name];
       if (commands.hasOwnProperty(aPrplId))
         result.push(commands[aPrplId]);
     }
     commandCount.value = result.length;
--- a/chat/components/src/imContacts.js
+++ b/chat/components/src/imContacts.js
@@ -487,19 +487,19 @@ Contact.prototype = {
     statement.params.contactId = this.id;
     statement.params.tagId = aTag.id;
     executeAsyncThenFinalize(statement);
   },
   hasTag(aTag) { return this._tags.some((t => t.id == aTag.id)); },
   _massMove: false,
   removeTag(aTag) {
     if (!this.hasTag(aTag))
-      throw "Attempting to remove a tag that the contact doesn't have";
+      throw new Error("Attempting to remove a tag that the contact doesn't have");
     if (this._tags.length == 1)
-      throw "Attempting to remove the last tag of a contact";
+      throw new Error("Attempting to remove the last tag of a contact");
 
     this._massMove = true;
     let hasTag = this.hasTag.bind(this);
     let newTag = this._tags[this._tags[0].id != aTag.id ? 0 : 1];
     let moved = false;
     this._buddies.forEach(function(aBuddy) {
       aBuddy._accounts.forEach(function(aAccountBuddy) {
         if (aAccountBuddy.tag.id == aTag.id) {
@@ -613,17 +613,17 @@ Contact.prototype = {
     for (let buddy of contact.getBuddies())
       buddy.contact = this;
     this._updatePreferredBuddy();
   },
   moveBuddyBefore(aBuddy, aBeforeBuddy) {
     let buddy = BuddiesById[aBuddy.id]; // remove XPConnect wrapper
     let oldPosition = this._buddies.indexOf(buddy);
     if (oldPosition == -1)
-      throw "aBuddy isn't attached to this contact";
+      throw new Error("aBuddy isn't attached to this contact");
 
     let newPosition = -1;
     if (aBeforeBuddy)
       newPosition = this._buddies.indexOf(BuddiesById[aBeforeBuddy.id]);
     if (newPosition == -1)
       newPosition = this._buddies.length - 1;
 
     if (oldPosition == newPosition)
@@ -665,17 +665,17 @@ Contact.prototype = {
 
       delete this._tags;
       delete this._buddies;
       delete this._observers;
     }
     else {
       let index = this._buddies.indexOf(aBuddy);
       if (index == -1)
-        throw "Removing an unknown buddy from contact " + this._id;
+        throw new Error("Removing an unknown buddy from contact " + this._id);
 
       this._buddies = this._buddies.filter(b => b !== aBuddy);
 
       // If we are actually removing the whole contact, don't bother updating
       // the positions or the preferred buddy.
       if (this._massRemove)
         return;
 
@@ -686,17 +686,17 @@ Contact.prototype = {
       if (this._preferredBuddy.id == aBuddy.id)
         this._updatePreferredBuddy();
     }
   },
   _updatePositions(aIndexBegin, aIndexEnd) {
     if (aIndexEnd === undefined)
       aIndexEnd = this._buddies.length - 1;
     if (aIndexBegin > aIndexEnd)
-      throw "_updatePositions: Invalid indexes";
+      throw new Error("_updatePositions: Invalid indexes");
 
     let statement =
       DBConn.createStatement("UPDATE buddies SET position = :position " +
                              "WHERE id = :buddyId");
     for (let i = aIndexBegin; i <= aIndexEnd; ++i) {
       statement.params.position = i;
       statement.params.buddyId = this._buddies[i].id;
       statement.executeAsync();
--- a/chat/components/src/imConversations.js
+++ b/chat/components/src/imConversations.js
@@ -166,17 +166,17 @@ UIConversation.prototype = {
     }
   },
   // Returns a boolean indicating if the ui-conversation was closed.
   // If the conversation was closed, aContactId.value is set to the contact id
   // or 0 if no contact was associated with the conversation.
   removeTarget(aPrplConversation, aContactId) {
     let id = aPrplConversation.id;
     if (!(id in this._prplConv))
-      throw "unknown prpl conversation";
+      throw new Error("unknown prpl conversation");
 
     delete this._prplConv[id];
     if (this._currentTargetId != id)
       return false;
 
     for (let newId in this._prplConv) {
       this.changeTargetTo(this._prplConv[newId]);
       return false;
@@ -648,17 +648,17 @@ ConversationsService.prototype = {
     if (aConvCount)
       aConvCount.value = rv.length;
     return rv;
   },
   getUIConversation(aPrplConversation) {
     let id = aPrplConversation.id;
     if (this._uiConv && id in this._uiConv)
       return this._uiConv[id];
-    throw "Unknown conversation";
+    throw new Error("Unknown conversation");
   },
   getUIConversationByContactId(aId) {
     return (aId in this._uiConvByContactId) ? this._uiConvByContactId[aId] : null;
   },
 
   getConversations() { return new nsSimpleEnumerator(this._prplConversations); },
   getConversationById(aId) {
     for (let conv of this._prplConversations)
--- a/chat/components/src/logger.js
+++ b/chat/components/src/logger.js
@@ -937,17 +937,17 @@ Logger.prototype = {
       let statusText = aSubject.statusText;
       if (statusText)
         status += " (\"" + statusText + "\")";
 
       let nameText = aSubject.displayName + " (" + aSubject.userName + ")";
       getSystemLogWriter(aSubject.account).logEvent(nameText + " is now " + status);
       break;
     default:
-      throw "Unexpected notification " + aTopic;
+      throw new Error("Unexpected notification " + aTopic);
     }
   },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.imILogger]),
   classDescription: "Logger",
   classID: Components.ID("{fb0dc220-2c7a-4216-9f19-6b8f3480eae9}"),
   contractID: "@mozilla.org/chat/logger;1",
 };
--- a/chat/content/imAccountOptionsHelper.js
+++ b/chat/content/imAccountOptionsHelper.js
@@ -83,17 +83,17 @@ var accountOptionsHelper = {
           rows.appendChild(this.createTextbox(null, opt.getString(), text, name,
                                               containerType));
           break;
         case Ci.prplIPref.typeList:
           rows.appendChild(this.createMenulist(opt.getList(), text, name));
           document.getElementById(name).value = opt.getListDefault();
           break;
         default:
-          throw "unknown preference type " + opt.type;
+          throw new Error("unknown preference type " + opt.type);
       }
       if (aAttributes && aAttributes[opt.type]) {
         let element = document.getElementById(name);
         for (let attr of aAttributes[opt.type])
           element.setAttribute(attr.name, attr.value);
       }
       haveOptions = true;
     }
--- a/chat/modules/NormalizedMap.jsm
+++ b/chat/modules/NormalizedMap.jsm
@@ -13,17 +13,17 @@ this.EXPORTED_SYMBOLS = ["NormalizedMap"
  *  aIterable:    A iterable to prefill the map with, keys will be normalized.
  *
  * Returns a Map object that will automatically run aNormalize on any operations
  * involving keys.
  */
 class NormalizedMap extends Map {
   constructor(aNormalize, aIterable = []) {
     if (typeof(aNormalize) != "function")
-      throw "NormalizedMap must have a normalize function!";
+      throw new Error("NormalizedMap must have a normalize function!");
     // Create the wrapped Map; use the provided iterable after normalizing the
     // keys.
     let entries = [...aIterable].map(([key, val]) => [aNormalize(key), val]);
     super(entries);
     // Note: In derived classes, super() must be called before using 'this'.
     this._normalize = aNormalize;
   }
 
--- a/chat/modules/imContentSink.jsm
+++ b/chat/modules/imContentSink.jsm
@@ -171,20 +171,20 @@ function initGlobalRuleset()
   gGlobalRuleset = newRuleset();
 
   Services.prefs.addObserver(kModePref, styleObserver);
 }
 
 var styleObserver = {
   observe(aObject, aTopic, aMsg) {
     if (aTopic != "nsPref:changed" || aMsg != kModePref)
-      throw "bad notification";
+      throw new Error("bad notification");
 
     if (!gGlobalRuleset)
-      throw "gGlobalRuleset not initialized";
+      throw new Error("gGlobalRuleset not initialized");
 
     setBaseRuleset(getModePref(), gGlobalRuleset);
   },
 };
 
 function getModePref()
 {
   let baseNum = Services.prefs.getIntPref(kModePref);
--- a/chat/modules/imSmileys.jsm
+++ b/chat/modules/imSmileys.jsm
@@ -36,29 +36,29 @@ Object.defineProperty(this, "gTheme", {
 
 var gPrefObserver = {
   init() {
     Services.prefs.addObserver(kEmoticonsThemePref, gPrefObserver);
   },
 
   observe(aObject, aTopic, aMsg) {
     if (aTopic != "nsPref:changed" || aMsg != kEmoticonsThemePref)
-      throw "bad notification";
+      throw new Error("bad notification");
 
     gTheme = getTheme();
   },
 };
 
 function getSmileRealURI(aSmile)
 {
   aSmile = Services.textToSubURI.unEscapeURIForUI("UTF-8", aSmile);
   if (aSmile in gTheme.iconsHash)
     return gTheme.baseUri + gTheme.iconsHash[aSmile].filename;
 
-  throw "Invalid smile!";
+  throw new Error("Invalid smile!");
 }
 
 function getSmileyList(aThemeName)
 {
   let theme = aThemeName == gTheme.name ? gTheme : getTheme(aThemeName);
   if (!theme.json)
     return null;
 
@@ -214,17 +214,17 @@ function smileNode(aNode)
       smileTextNode(node);
     }
   }
 }
 
 function smileImMarkup(aDocument, aText)
 {
   if (!aDocument)
-    throw "providing an HTML document is required";
+    throw new Error("providing an HTML document is required");
 
   // return early if smileys are disabled
   if (!gTheme.iconsHash)
     return aText;
 
   let div = aDocument.createElement("div");
   // eslint-disable-next-line no-unsanitized/property
   div.innerHTML = aText;
--- a/chat/modules/imThemes.jsm
+++ b/chat/modules/imThemes.jsm
@@ -93,26 +93,26 @@ function HTMLTheme(aBaseURI)
 
   for (let id in files) {
     let html = getChromeFile(aBaseURI + files[id]);
     if (html)
       Object.defineProperty(this, id, {value: html});
   }
 
   if (!("incomingContent" in files))
-    throw "Invalid theme: Incoming/Content.html is missing!";
+    throw new Error("Invalid theme: Incoming/Content.html is missing!");
 }
 
 HTMLTheme.prototype = {
   get footer() { return ""; },
   get header() { return ""; },
   get status() { return this.incomingContent; },
   get statusNext() { return this.status; },
   get incomingContent() {
-    throw "Incoming/Content.html is a required file";
+    throw new Error("Incoming/Content.html is a required file");
   },
   get incomingNextContent() { return this.incomingContent; },
   get outgoingContent() { return this.incomingContent; },
   get outgoingNextContent() { return this.incomingNextContent; },
   get incomingContext() { return this.incomingContent; },
   get incomingNextContext() { return this.incomingNextContent; },
   get outgoingContext() { return this.hasOwnProperty("outgoingContent") ? this.outgoingContent : this.incomingContext; },
   get outgoingNextContext() { return this.hasOwnProperty("outgoingNextContent") ? this.outgoingNextContent : this.incomingNextContext; },
@@ -152,38 +152,38 @@ function plistToJSON(aElt)
       nodes = aElt.childNodes;
       for (let i = 0; i < nodes.length; ++i) {
         if (Element.isInstance(nodes[i]))
           array.push(plistToJSON(nodes[i]));
       }
       return array;
 
     default:
-      throw "Unknown tag in plist file";
+      throw new Error("Unknown tag in plist file");
   }
 }
 
 function getInfoPlistContent(aBaseURI)
 {
   try {
     let channel = Services.io.newChannel(aBaseURI + "Info.plist", null, null, null,
                                          Services.scriptSecurityManager.getSystemPrincipal(),
                                          null,
                                          Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                          Ci.nsIContentPolicy.TYPE_OTHER);
     let stream = channel.open();
     let parser = new DOMParser();
     let doc = parser.parseFromStream(stream, null, stream.available(), "text/xml");
     if (doc.documentElement.localName != "plist")
-      throw "Invalid Info.plist file";
+      throw new Error("Invalid Info.plist file");
     let node = doc.documentElement.firstChild;
     while (node && !Element.isInstance(node))
       node = node.nextSibling;
     if (!node || node.localName != "dict")
-      throw "Empty or invalid Info.plist file";
+      throw new Error("Empty or invalid Info.plist file");
     return plistToJSON(node);
   } catch (e) {
     Cu.reportError(e);
     return null;
   }
 }
 
 function getChromeBaseURI(aThemeName)
@@ -193,17 +193,17 @@ function getChromeBaseURI(aThemeName)
   return "chrome://" + aThemeName + "/skin/";
 }
 
 function getThemeByName(aName)
 {
   let baseURI = getChromeBaseURI(aName);
   let metadata = getInfoPlistContent(baseURI);
   if (!metadata)
-    throw "Cannot load theme " + aName;
+    throw new Error("Cannot load theme " + aName);
 
   return {
     name: aName,
     variant: "default",
     baseURI,
     metadata,
     html: new HTMLTheme(baseURI),
     showHeader: gPrefBranch.getBoolPref(kShowHeaderPref),
--- a/chat/modules/jsProtoHelper.jsm
+++ b/chat/modules/jsProtoHelper.jsm
@@ -250,17 +250,17 @@ var GenericAccountBuddyPrototype = {
   __proto__: ClassInfo("prplIAccountBuddy", "generic account buddy object"),
   get DEBUG() { return this._account.DEBUG; },
   get LOG() { return this._account.LOG; },
   get WARN() { return this._account.WARN; },
   get ERROR() { return this._account.ERROR; },
 
   _init(aAccount, aBuddy, aTag, aUserName) {
     if (!aBuddy && !aUserName)
-      throw "aUserName is required when aBuddy is null";
+      throw new Error("aUserName is required when aBuddy is null");
 
     this._tag = aTag;
     this._account = aAccount;
     this._buddy = aBuddy;
     if (aBuddy) {
       let displayName = aBuddy.displayName;
       if (displayName != aUserName)
         this._serverAlias = displayName;
@@ -752,23 +752,23 @@ TooltipInfo.prototype = ClassInfo("prplI
  *      This could typically be used for password field.
  *      Warning: The UI currently doesn't support this.
  */
 function purplePref(aName, aOption) {
   this.name = aName; // Preference name
   this.label = aOption.label; // Text to display
 
   if (aOption.default === undefined || aOption.default === null)
-    throw "A default value for the option is required to determine its type.";
+    throw new Error("A default value for the option is required to determine its type.");
   this._defaultValue = aOption.default;
 
   const kTypes = {boolean: "Bool", string: "String", number: "Int"};
   let type = kTypes[typeof aOption.default];
   if (!type)
-    throw "Invalid option type";
+    throw new Error("Invalid option type");
 
   if (type == "String" && ("listValues" in aOption)) {
     type = "List";
     this._listValues = aOption.listValues;
   }
   this.type = Ci.prplIPref["type" + type];
 
   if ("masked" in aOption && aOption.masked)
@@ -849,30 +849,30 @@ ChatRoomFieldValues.prototype = {
 
 // the name getter and the getAccount method need to be implemented by
 // protocol plugins.
 var GenericProtocolPrototype = {
   __proto__: ClassInfo("prplIProtocol", "Generic protocol object"),
 
   init(aId) {
     if (aId != this.id)
-      throw "Creating an instance of " + aId + " but this object implements " + this.id;
+      throw new Error("Creating an instance of " + aId + " but this object implements " + this.id);
   },
   get id() { return "prpl-" + this.normalizedName; },
   // This is more aggressive than the account normalization of just
   // toLowerCase() since prpl names must be only letters/numbers.
   get normalizedName() { return this.name.replace(/[^a-z0-9]/gi, "").toLowerCase(); },
   get iconBaseURI() { return "chrome://chat/skin/prpl-generic/"; },
 
   getAccount(aImAccount) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
 
   _getOptionDefault(aName) {
     if (this.options && this.options.hasOwnProperty(aName))
       return this.options[aName].default;
-    throw aName + " has no default value in " + this.id + ".";
+    throw new Error(aName + " has no default value in " + this.id + ".");
   },
   getOptions() {
     if (!this.options)
       return EmptyEnumerator;
 
     let purplePrefs = [];
     for (let [name, option] of Object.entries(this.options))
       purplePrefs.push(new purplePref(name, option));
@@ -887,17 +887,17 @@ var GenericProtocolPrototype = {
   },
 
   registerCommands() {
     if (!this.commands)
       return;
 
     this.commands.forEach(function(command) {
       if (!command.hasOwnProperty("name") || !command.hasOwnProperty("run"))
-        throw "Every command must have a name and a run function.";
+        throw new Error("Every command must have a name and a run function.");
       if (!("QueryInterface" in command))
         command.QueryInterface = ChromeUtils.generateQI([Ci.imICommand]);
       if (!command.hasOwnProperty("usageContext"))
         command.usageContext = Ci.imICommand.CMD_CONTEXT_ALL;
       if (!command.hasOwnProperty("priority"))
         command.priority = Ci.imICommand.CMD_PRIORITY_PRPL;
       Services.cmd.registerCommand(command, this.id);
     }, this);
--- a/chat/modules/socket.jsm
+++ b/chat/modules/socket.jsm
@@ -570,23 +570,23 @@ var Socket = {
     }
 
     this.transport.setEventSink(this, Services.tm.currentThread);
 
     // No limit on the output stream buffer
     this._outputStream =
       this.transport.openOutputStream(0, this.outputSegmentSize, -1);
     if (!this._outputStream)
-      throw "Error getting output stream.";
+      throw new Error("Error getting output stream.");
 
     this._inputStream = this.transport.openInputStream(0, // flags
                                                        0, // Use default segment size
                                                        0); // Use default segment count
     if (!this._inputStream)
-      throw "Error getting input stream.";
+      throw new Error("Error getting input stream.");
 
     if (this.binaryMode) {
       // Handle binary mode
       this._binaryInputStream = new BinaryInputStream(this._inputStream);
       this._binaryOutputStream = new BinaryOutputStream(this._outputStream);
     } else {
       // Handle character mode
       this._scriptableInputStream =
--- a/chat/protocols/gtalk/gtalk.js
+++ b/chat/protocols/gtalk/gtalk.js
@@ -58,17 +58,17 @@ function* PlainFullBindAuth(aUsername, a
     "ga:client-uses-full-bind-result": "true",
   };
   let stanza = yield {
     send: Stanza.node("auth", Stanza.NS.sasl, attrs, key),
     log: "<auth.../> (PlainFullBindAuth base64 encoded username and password not logged)",
   };
 
   if (stanza.localName != "success")
-    throw "Didn't receive the expected auth success stanza.";
+    throw new Error("Didn't receive the expected auth success stanza.");
 }
 
 function GTalkAccount(aProtoInstance, aImAccount) {
   this._init(aProtoInstance, aImAccount);
 }
 GTalkAccount.prototype = {
   __proto__: XMPPAccountPrototype,
   connect() {
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -87,17 +87,17 @@ function ircMessage(aData, aOrigin) {
   // See http://joshualuckers.nl/2010/01/10/regular-expression-to-match-raw-irc-messages/
   // Note that this expression is slightly more aggressive in matching than RFC
   // 2812 would allow. It allows for empty parameters (besides the last
   // parameter, which can always be empty), by allowing multiple spaces.
   // (This is for compatibility with Unreal's 432 response, which returns an
   // empty first parameter.) It also allows a trailing space after the
   // <parameter>s when no <last parameter> is present (also occurs with Unreal).
   if (!(temp = aData.match(/^(?:@([^ ]+) )?(?::([^ ]+) )?([^ ]+)((?: +[^: ][^ ]*)*)? *(?::([\s\S]*))?$/)))
-    throw "Couldn't parse message: \"" + aData + "\"";
+    throw new Error("Couldn't parse message: \"" + aData + "\"");
 
   message.command = temp[3];
   // Space separated parameters. Since we expect a space as the first thing
   // here, we want to ignore the first value (which is empty).
   message.params = temp[4] ? temp[4].split(" ").slice(1) : [];
   // Last parameter can contain spaces or be an empty string.
   if (temp[5] !== undefined)
     message.params.push(temp[5]);
--- a/chat/protocols/skype/skype.js
+++ b/chat/protocols/skype/skype.js
@@ -280,18 +280,18 @@ function getTimezone() {
   /*
    * Zero-pad aNum to the length of aLen.
    */
   function zeroPad(aNum, aLen) {
     let nStr = aNum.toString();
     let nLen = nStr.length;
 
     if (nLen > aLen) {
-      throw "Can't zero-pad when longer than expected length: " + nStr +
-        ".length > " + aLen;
+      throw new Error("Can't zero-pad when longer than expected length: " + nStr +
+        ".length > " + aLen);
     }
 
     return "0".repeat(aLen - nLen) + nStr;
   }
 
   // Invert the sign of the timezone from JavaScript's date object.
   let sign = "+";
   let timezone = new Date().getTimezoneOffset() * -1;
--- a/chat/protocols/twitter/twitter.js
+++ b/chat/protocols/twitter/twitter.js
@@ -48,17 +48,17 @@ ChatBuddy.prototype = {
   get buddyIconFilename() {
     let userInfo = this._account._userInfo.get(this.name);
     if (userInfo)
       return userInfo.profile_image_url;
     return undefined;
   },
   set buddyIconFilename(aName) {
     // Prevent accidental removal of the getter.
-    throw "Don't set chatBuddy.buddyIconFilename directly for Twitter.";
+    throw new Error("Don't set chatBuddy.buddyIconFilename directly for Twitter.");
   },
   createConversation() {
     return this._account.createConversation(this._name);
   },
 };
 
 function Tweet(aTweet, aWho, aMessage, aObject)
 {
--- a/chat/protocols/xmpp/xmpp-authmechs.jsm
+++ b/chat/protocols/xmpp/xmpp-authmechs.jsm
@@ -27,17 +27,17 @@ function* PlainAuth(aUsername, aPassword
 
   let stanza = yield {
     send: Stanza.node("auth", Stanza.NS.sasl, {mechanism: "PLAIN"},
                       base64Data),
     log: '<auth mechanism:="PLAIN"/> (base64 encoded username and password not logged)',
   };
 
   if (stanza.localName != "success")
-    throw "Didn't receive the expected auth success stanza.";
+    throw new Error("Didn't receive the expected auth success stanza.");
 }
 
 // Handle SCRAM-SHA-1 authorization mechanism.
 const RFC3454 = {
   A1: "\u0221|[\u0234-\u024f]|[\u02ae-\u02af]|[\u02ef-\u02ff]|\
 [\u0350-\u035f]|[\u0370-\u0373]|[\u0376-\u0379]|[\u037b-\u037d]|\
 [\u037f-\u0383]|\u038b|\u038d|\u03a2|\u03cf|[\u03f7-\u03ff]|\u0487|\
 \u04cf|[\u04f6-\u04f7]|[\u04fa-\u04ff]|[\u0510-\u0530]|\
@@ -267,41 +267,41 @@ function saslPrep(aString) {
 
   // RFC 4013 2.3: Prohibited Output and 2.5: Unassigned Code Points.
   let matchStr =
     RFC3454.C12 + "|" + RFC3454.C21 + "|" + RFC3454.C22 + "|" + RFC3454.C3 +
     "|" + RFC3454.C4 + "|" + RFC3454.C5 + "|" + RFC3454.C6 + "|" + RFC3454.C7 +
     "|" + RFC3454.C8 + "|" + RFC3454.C9 + "|" + RFC3454.A1;
   let match = new RegExp(matchStr, "u").test(retVal);
   if (match)
-    throw "String contains prohibited characters";
+    throw new Error("String contains prohibited characters");
 
   // RFC 4013 2.4: Bidirectional Characters.
   let r = new RegExp(RFC3454.D1, "u").test(retVal);
   let l = new RegExp(RFC3454.D2, "u").test(retVal);
   if (l && r)
-    throw "String must not contain LCat and RandALCat characters together";
+    throw new Error("String must not contain LCat and RandALCat characters together");
   else if (r) {
     let matchFirst = new RegExp("^(" + RFC3454.D1 + ")", "u").test(retVal);
     let matchLast = new RegExp("(" + RFC3454.D1 + ")$", "u").test(retVal);
     if (!matchFirst || !matchLast)
-      throw "A RandALCat character must be the first and the last character";
+      throw new Error("A RandALCat character must be the first and the last character");
   }
 
   return retVal;
 }
 
 // Converts aName to saslname.
 function saslName(aName) {
   // RFC 5802 (5.1): the client SHOULD prepare the username using the "SASLprep".
   // The characters ’,’ or ’=’ in usernames are sent as ’=2C’ and
   // ’=3D’ respectively.
   let saslName = saslPrep(aName).replace(/=/g, "=3D").replace(/,/g, "=2C");
   if (!saslName)
-    throw "Name is not valid";
+    throw new Error("Name is not valid");
 
   return saslName;
 }
 
 // Converts aMessage to array of bytes then apply SHA-1 hashing.
 function bytesAndSHA1(aMessage) {
   let hasher =
     Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
@@ -345,34 +345,34 @@ function* scramSHA1Auth(aUsername, aPass
   let clientFirstMessage = gs2Header + clientFirstMessageBare;
 
   let receivedStanza = yield {
     send: Stanza.node("auth", Stanza.NS.sasl, {mechanism: "SCRAM-SHA-1"},
                       btoa(clientFirstMessage)),
   };
 
   if (receivedStanza.localName != "challenge")
-    throw "Not authorized";
+    throw new Error("Not authorized");
 
   // RFC 5802 (3): SCRAM Algorithm Overview.
   let decodedChallenge = atob(receivedStanza.innerText);
 
   // Expected to contain the user’s iteration count (i) and the user’s
   // salt (s), and the server appends its own nonce to the client-specified
   // one (r).
   let attributes = parseChallenge(decodedChallenge);
   if (attributes.hasOwnProperty("e"))
-    throw "Authentication failed: " + attributes.e;
+    throw new Error("Authentication failed: " + attributes.e);
   else if (!attributes.hasOwnProperty("i") ||
            !attributes.hasOwnProperty("s") ||
            !attributes.hasOwnProperty("r")) {
-    throw "Unexpected response: " + decodedChallenge;
+    throw new Error("Unexpected response: " + decodedChallenge);
   }
   if (!attributes.r.startsWith(cNonce))
-    throw "Nonce is not correct";
+    throw new Error("Nonce is not correct");
 
   let clientFinalMessageWithoutProof =
     "c=" + btoa(gs2Header) + ",r=" + attributes.r;
 
   // Calculate ClientProof.
 
   // SaltedPassword := Hi(Normalize(password), salt, i)
   // Normalize using saslPrep.
@@ -424,25 +424,25 @@ function* scramSHA1Auth(aUsername, aPass
     return {
       send: Stanza.node("response", Stanza.NS.sasl, null, btoa(clientFinalMessage)),
       log: "<response/> (base64 encoded SCRAM response containing password not logged)",
     };
   });
 
   // Only check server signature if we succeed to authenticate.
   if (receivedStanza.localName != "success")
-    throw "Didn't receive the expected auth success stanza.";
+    throw new Error("Didn't receive the expected auth success stanza.");
 
   let decodedResponse = atob(receivedStanza.innerText);
 
   // Expected to contain a base64-encoded ServerSignature (v).
   attributes = parseChallenge(decodedResponse);
   if (!attributes.hasOwnProperty("v"))
-    throw "Unexpected response: " + decodedResponse;
+    throw new Error("Unexpected response: " + decodedResponse);
 
   // Compare ServerSignature with our ServerSignature which we calculated in
   // _generateResponse.
   let serverSignatureResponse = atob(attributes.v);
   if (serverSignature != serverSignatureResponse)
-    throw "Server signature does not match";
+    throw new Error("Server signature does not match");
 }
 
 var XMPPAuthMechanisms = {"PLAIN": PlainAuth, "SCRAM-SHA-1": scramSHA1Auth};
--- a/chat/protocols/xmpp/xmpp-xml.jsm
+++ b/chat/protocols/xmpp/xmpp-xml.jsm
@@ -183,17 +183,17 @@ TextNode.prototype = {
 /* aUri is the namespace. */
 /* aLocalName must have value, otherwise throws. */
 /* aAttr can be instance of nsISAXAttributes or object */
 /* Example: <f:a xmlns:f='g' d='1'> is parsed to
    uri/namespace='g', localName='a', qName='f:a', attributes={d='1'} */
 function XMLNode(aParentNode, aUri, aLocalName, aQName = aLocalName,
                  aAttr = {}) {
   if (!aLocalName)
-    throw "aLocalName must have value";
+    throw new Error("aLocalName must have value");
 
   this._parentNode = aParentNode; // Used only for parsing
   this.uri = aUri;
   this.localName = aLocalName;
   this.qName = aQName;
   this.attributes = {};
   this.children = [];
 
--- a/chat/protocols/xmpp/xmpp.jsm
+++ b/chat/protocols/xmpp/xmpp.jsm
@@ -1367,21 +1367,21 @@ var XMPPAccountPrototype = {
 
   /* Disconnect from the server */
   disconnect() {
     this._disconnect();
   },
 
   addBuddy(aTag, aName) {
     if (!this._connection)
-      throw "The account isn't connected";
+      throw new Error("The account isn't connected");
 
     let jid = this.normalize(aName);
     if (!jid || !jid.includes("@"))
-      throw "Invalid username";
+      throw new Error("Invalid username");
 
     if (this._buddies.has(jid)) {
       let subscription = this._buddies.get(jid).subscription;
       if (subscription && (subscription == "both" || subscription == "to")) {
         this.DEBUG("not re-adding an existing buddy");
         return;
       }
     }
@@ -2292,17 +2292,17 @@ var XMPPAccountPrototype = {
     return this._setJID(result.domain, result.node, result.resource);
   },
 
   // Constructs jid as an object from domain, node and resource parts.
   // The object has properties (node, domain, resource and jid).
   // aDomain is required, but aNode and aResource are optional.
   _setJID(aDomain, aNode = null, aResource = null) {
     if (!aDomain)
-      throw "aDomain must have a value";
+      throw new Error("aDomain must have a value");
 
     let result = {
       node: aNode,
       domain: aDomain.toLowerCase(),
       resource: aResource,
     };
     let jid = result.domain;
     if (result.node) {
@@ -2451,17 +2451,17 @@ var XMPPAccountPrototype = {
     let isMucParticipant = this._mucs.has(convName);
     if (isMucParticipant)
       convName = this.normalizeFullJid(aName);
 
     // Checking that the aName can be parsed and is not broken.
     let jid = this._parseJID(convName);
     if (!jid || !jid.domain || (isMucParticipant && (!jid.node || !jid.resource))) {
       this.ERROR("Could not create conversation as jid is broken: " + convName);
-      throw "Invalid JID";
+      throw new Error("Invalid JID");
     }
 
     if (!this._conv.has(convName)) {
       this._conv.set(convName,
                      new this._conversationConstructor(this, convName,
                                                        isMucParticipant));
     }
 
--- a/common/bindings/menubutton.xml
+++ b/common/bindings/menubutton.xml
@@ -17,17 +17,17 @@
         this.init();
       </constructor>
 
       <method name="init">
         <body>
         <![CDATA[
           var btn = document.getAnonymousElementByAttribute(this, "anonid", "button");
           if (!btn)
-            throw "XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\"";
+            throw new Error("XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\"");
 
           var menubuttonParent = this;
           btn.addEventListener("mouseover", function() {
             if (!this.disabled)
               menubuttonParent.buttonover = true;
           }, true);
           btn.addEventListener("mouseout", function() {
             menubuttonParent.buttonover = false;
--- a/common/bindings/notificationbox.xml
+++ b/common/bindings/notificationbox.xml
@@ -95,17 +95,17 @@
         <parameter name="aImage"/>
         <parameter name="aPriority"/>
         <parameter name="aButtons"/>
         <parameter name="aEventCallback"/>
         <body>
           <![CDATA[
             if (aPriority < this.PRIORITY_INFO_LOW ||
                 aPriority > this.PRIORITY_CRITICAL_BLOCK)
-              throw "Invalid notification priority " + aPriority;
+              throw new Error("Invalid notification priority " + aPriority);
 
             // check for where the notification should be inserted according to
             // priority. If two are equal, the existing one appears on top.
             var notifications = this.allNotifications;
             var insertPos = null;
             for (var n = notifications.length - 1; n >= 0; n--) {
               if (notifications[n].priority < aPriority)
                 break;
--- a/common/src/Overlays.jsm
+++ b/common/src/Overlays.jsm
@@ -414,31 +414,31 @@ class Overlays {
    * Fetches the overlay from the given chrome:// or resource:// URL. This happen synchronously so
    * we have a chance to complete before the load event.
    *
    * @param {String} srcUrl                         The URL to load
    * @return {XMLHttpRequest}                       The completed XHR.
    */
   fetchOverlay(srcUrl) {
     if (!srcUrl.startsWith("chrome://") && !srcUrl.startsWith("resource://")) {
-      throw "May only load overlays from chrome:// or resource:// uris";
+      throw new Error("May only load overlays from chrome:// or resource:// uris");
     }
 
     let xhr = new XMLHttpRequest();
     xhr.overrideMimeType("application/xml");
     xhr.open("GET", srcUrl, false);
 
     // Elevate the request, so DTDs will work. Should not be a security issue since we
     // only load chrome, resource and file URLs, and that is our privileged chrome package.
     try {
       xhr.channel.owner = Services.scriptSecurityManager.getSystemPrincipal();
     } catch (ex) {
       oconsole.error("Failed to set system principal while fetching overlay " + srcUrl);
       xhr.close();
-      throw "Failed to set system principal";
+      throw new Error("Failed to set system principal");
     }
 
     xhr.send(null);
     return xhr;
   }
 
   /**
    * Loads scripts described by the given script node. The node can either have a src attribute, or
--- a/mail/base/content/nsContextMenu.js
+++ b/mail/base/content/nsContextMenu.js
@@ -795,17 +795,17 @@ nsContextMenu.prototype = {
   getLinkURL() {
     if (this.link.href) {
       return this.link.href;
     }
     var href = this.link.getAttributeNS("http://www.w3.org/1999/xlink", "href");
     if (!href || (href.trim() == "")) {
        // Without this we try to save as the current doc,
        // for example, HTML case also throws if empty.
-      throw "Empty href";
+      throw new Error("Empty href");
     }
     href = this.makeURLAbsolute(this.link.baseURI, href);
     return href;
   },
 
   /**
    * Generate a URI object from the linkURL spec
    * @return an nsIURI if possible, or null if not
--- a/mail/base/content/nsDragAndDrop.js
+++ b/mail/base/content/nsDragAndDrop.js
@@ -69,17 +69,17 @@ var nsTransferable = {
    *        for each item from the specified source (clipboard/drag&drop etc)
    * @param Boolean aAnyFlag
    *        a flag specifying whether or not a specific flavour is requested. If false,
    *        data of the type of the first flavour in the flavourlist parameter is returned,
    *        otherwise the best flavour supported will be returned.
    **/
   get(aFlavourSet, aRetrievalFunc, aAnyFlag) {
     if (!aRetrievalFunc)
-      throw "No data retrieval handler provided!";
+      throw new Error("No data retrieval handler provided!");
 
     var array = aRetrievalFunc(aFlavourSet);
     var dataArray = [];
 
     // Iterate over the number of items returned from aRetrievalFunc. For
     // clipboard operations, this is 1, for drag and drop (where multiple
     // items may have been dragged) this could be >1.
     for (let i = 0; i < array.length; i++) {
@@ -542,12 +542,12 @@ var nsDragAndDrop = {
 
     try {
       secMan.checkLoadURIStrWithPrincipal(principal, aDraggedText,
                                           Ci.nsIScriptSecurityManager.STANDARD);
     } catch (e) {
       // Stop event propagation right here.
       aEvent.stopPropagation();
 
-      throw "Drop of " + aDraggedText + " denied.";
+      throw new Error("Drop of " + aDraggedText + " denied.");
     }
   },
 };
--- a/mail/base/content/sanitize.js
+++ b/mail/base/content/sanitize.js
@@ -210,17 +210,17 @@ Sanitizer.getClearRange = function(ts) {
     case Sanitizer.TIMESPAN_TODAY :
       var d = new Date();  // Start with today
       d.setHours(0);      // zero us back to midnight...
       d.setMinutes(0);
       d.setSeconds(0);
       startDate = d.valueOf() * 1000; // convert to epoch usec
       break;
     default:
-      throw "Invalid time span for clear private data: " + ts;
+      throw new Error("Invalid time span for clear private data: " + ts);
   }
   return [startDate, endDate];
 };
 
 Sanitizer._prefs = null;
 Sanitizer.__defineGetter__("prefs", function() {
   return Sanitizer._prefs ? Sanitizer._prefs
     : Sanitizer._prefs = Services.prefs
--- a/mail/components/accountcreation/content/util.js
+++ b/mail/components/accountcreation/content/util.js
@@ -553,17 +553,17 @@ function deepCopy(org) {
     return org;
   if (typeof(org) == "number")
     return org;
   if (typeof(org) == "boolean")
     return org;
   if (typeof(org) == "function")
     return org;
   if (typeof(org) != "object")
-    throw "can't copy objects of type " + typeof(org) + " yet";
+    throw new Error("can't copy objects of type " + typeof(org) + " yet");
 
   // TODO still instanceof org != instanceof copy
   // var result = new org.constructor();
   var result = {};
   if (typeof(org.length) != "undefined")
     result = [];
   for (var prop in org)
     result[prop] = deepCopy(org[prop]);
--- a/mail/components/accountcreation/content/verifyConfig.js
+++ b/mail/components/accountcreation/content/verifyConfig.js
@@ -82,17 +82,17 @@ function verifyConfig(config, alter, msg
       if (!config.oauthSettings.issuer || !config.oauthSettings.scope) {
         // lookup issuer or scope from hostname
         let hostname = (config.incoming.auth == Ci.nsMsgAuthMethod.OAuth2) ?
                        config.incoming.hostname : config.outgoing.hostname;
         let hostDetails = OAuth2Providers.getHostnameDetails(hostname);
         if (hostDetails)
           [config.oauthSettings.issuer, config.oauthSettings.scope] = hostDetails;
         if (!config.oauthSettings.issuer || !config.oauthSettings.scope)
-          throw "Could not get issuer for oauth2 authentication";
+          throw new Error("Could not get issuer for oauth2 authentication");
       }
       gEmailWizardLogger.info("Saving oauth parameters for issuer " +
                                config.oauthSettings.issuer);
       inServer.setCharValue("oauth2.scope", config.oauthSettings.scope);
       inServer.setCharValue("oauth2.issuer", config.oauthSettings.issuer);
       gEmailWizardLogger.info("OAuth2 issuer, scope is " +
                               config.oauthSettings.issuer + ", " + config.oauthSettings.scope);
     }
--- a/mail/components/im/content/addbuddy.js
+++ b/mail/components/im/content/addbuddy.js
@@ -13,17 +13,17 @@ var addBuddy = {
         continue;
       let proto = acc.protocol;
       let item = accountList.appendItem(acc.name, acc.id, proto.name);
       item.setAttribute("image", proto.iconBaseURI + "icon.png");
       item.setAttribute("class", "menuitem-iconic");
     }
     if (!accountList.itemCount) {
       document.getElementById("addBuddyDialog").cancelDialog();
-      throw "No connected account!";
+      throw new Error("No connected account!");
     }
     accountList.selectedIndex = 0;
   },
 
   oninput() {
     document.documentElement.getButton("accept").disabled =
       !addBuddy.getValue("name");
   },
--- a/mail/components/im/content/chat-messenger.js
+++ b/mail/components/im/content/chat-messenger.js
@@ -495,17 +495,17 @@ var chatHandler = {
              treeView._rowMap[index].log.time != logTime)
         ++index;
       if (treeView._rowMap[index].log.time == logTime) {
         logTree.view.selection.select(index);
         logTree.ensureRowIsVisible(index);
       }
       return true;
     }
-    throw "Couldn't find the log to select among the set of logs passed.";
+    throw new Error("Couldn't find the log to select among the set of logs passed.");
   },
 
   onLogSelect() {
     let selection = this._treeView.selection;
     let currentIndex = selection.currentIndex;
     // The current (focused) row may not be actually selected...
     if (!selection.isSelected(currentIndex))
       return;
--- a/mail/components/im/content/imAccountWizard.js
+++ b/mail/components/im/content/imAccountWizard.js
@@ -270,17 +270,17 @@ var accountWizard = {
         if (val != opt.getString())
           this.prefs.push({opt, name, value: val});
         break;
       case opt.typeList:
         if (val != opt.getListDefault())
           this.prefs.push({opt, name, value: val});
         break;
       default:
-        throw "unknown preference type " + opt.type;
+        throw new Error("unknown preference type " + opt.type);
       }
     }
 
     for (let i = 0; i < this.prefs.length; ++i) {
       let opt = this.prefs[i];
       let label = bundle.getFormattedString("accountColon", [opt.opt.label]);
       rows.appendChild(this.createSummaryRow(label, opt.value));
     }
@@ -303,17 +303,17 @@ var accountWizard = {
       case opt.typeInt:
         acc.setInt(option.name, option.value);
         break;
       case opt.typeString:
       case opt.typeList:
         acc.setString(option.name, option.value);
         break;
       default:
-        throw "unknown type";
+        throw new Error("unknown type");
       }
     }
     var autologin = this.getValue("connectNow");
     acc.autoLogin = autologin;
 
     acc.save();
 
     try {
--- a/mail/components/im/content/imAccounts.js
+++ b/mail/components/im/content/imAccounts.js
@@ -575,19 +575,19 @@ var gAMDragAndDrop = {
     this.SCROLL_SPEED = Services.prefs.getIntPref("toolkit.scrollbox.scrollIncrement", 20);
     return this.SCROLL_SPEED;
   },
 
   onDragStart(aEvent, aTransferData, aAction) {
     let accountElement = aEvent.explicitOriginalTarget;
     // This stops the dragging session.
     if (!(accountElement instanceof Ci.nsIDOMXULSelectControlItemElement))
-      throw "Element is not draggable!";
+      throw new Error("Element is not draggable!");
     if (gAccountManager.accountList.itemCount == 1)
-      throw "Can't drag while there is only one account!";
+      throw new Error("Can't drag while there is only one account!");
 
     // Transferdata is never used, but we need to transfer something.
     aTransferData.data = new TransferData();
     aTransferData.data.addDataForFlavour(this.ACCOUNT_MIME_TYPE, accountElement);
   },
 
   onDragOver(aEvent, aFlavour, aSession) {
     let accountElement = aEvent.explicitOriginalTarget;
--- a/mail/components/im/content/imContextMenu.js
+++ b/mail/components/im/content/imContextMenu.js
@@ -228,17 +228,17 @@ imContextMenu.prototype = {
       return href;
 
     href = this.link.getAttributeNS("http://www.w3.org/1999/xlink",
                                     "href");
 
     if (!href || (href.trim() == "")) {
       // Without this we try to save as the current doc,
       // for example, HTML case also throws if empty
-      throw "Empty href";
+      throw new Error("Empty href");
     }
 
     return makeURLAbsolute(this.link.baseURI, href);
   },
 
   getLinkURI() {
     try {
       return Services.io.newURI(this.linkURL);
--- a/mail/components/im/content/imconversation.xml
+++ b/mail/components/im/content/imconversation.xml
@@ -153,17 +153,17 @@
        </body>
      </method>
 
      <method name="addMsg">
       <parameter name="aMsg"/>
       <body>
       <![CDATA[
         if (!this.loaded)
-          throw "Calling addMsg before the browser is ready?";
+          throw new Error("Calling addMsg before the browser is ready?");
 
         var conv = aMsg.conversation;
         if (!conv) {
           // The conversation has already been destroyed,
           // probably because the window was closed.
           // Return without doing anything.
           return;
         }
@@ -1105,19 +1105,19 @@
 
      <!-- Create a buddy item to add in the visible list of participants -->
      <method name="createBuddy">
        <parameter name="aBuddy"/>
        <body>
        <![CDATA[
          var name = aBuddy.name;
          if (!name)
-           throw "The empty string isn't a valid nick.";
+           throw new Error("The empty string isn't a valid nick.");
          if (this.buddies.has(name))
-           throw "Adding chat buddy " + name + " twice?!";
+           throw new Error("Adding chat buddy " + name + " twice?!");
 
          this.trackNick(name);
 
          let image = document.createElement("image");
          let label = document.createElement("label");
          label.setAttribute("value", name);
          label.setAttribute("flex", "1");
          label.setAttribute("crop", "end");
@@ -1179,17 +1179,17 @@
      <!-- Update a buddy in the visible list of participants -->
      <method name="updateBuddy">
        <parameter name="aBuddy"/>
        <parameter name="aOldName"/>
        <body>
        <![CDATA[
          var name = aBuddy.name;
          if (!name)
-           throw "The empty string isn't a valid nick.";
+           throw new Error("The empty string isn't a valid nick.");
 
          if (!aOldName) {
            // If aOldName is null, we are changing the flags of the buddy
            let item = this.buddies.get(name);
            item.chatBuddy = aBuddy;
            this.setBuddyAttributes(item);
            return;
          }
@@ -1201,20 +1201,20 @@
 
          this.trackNick(name);
 
          if (!this._isConversationSelected)
            return;
 
          // Is aOldName is not null, then we are renaming the buddy
          if (!this.buddies.has(aOldName))
-           throw "Updating a chat buddy that does not exist: " + aOldName;
+           throw new Error("Updating a chat buddy that does not exist: " + aOldName);
 
          if (this.buddies.has(name))
-           throw "Updating a chat buddy to an already existing one: " + name;
+           throw new Error("Updating a chat buddy to an already existing one: " + name);
 
          let item = this.buddies.get(aOldName);
          item.chatBuddy = aBuddy;
          this.buddies.delete(aOldName);
          this.buddies.set(name, item);
          item.querySelector("label").value = name;
 
          // Move this item to the right position if its name changed
@@ -1224,17 +1224,17 @@
        </body>
      </method>
 
      <method name="removeBuddy">
        <parameter name="aName"/>
        <body>
        <![CDATA[
          if (!this.buddies.has(aName))
-           throw "Cannot remove a buddy that was not in the room";
+           throw new Error("Cannot remove a buddy that was not in the room");
          this.buddies.get(aName).remove();
          this.buddies.delete(aName);
          if (this._isBuddyActive(aName))
            delete this._activeBuddies[aName];
        ]]>
        </body>
      </method>
 
--- a/mail/components/im/content/imgroup.xml
+++ b/mail/components/im/content/imgroup.xml
@@ -112,17 +112,17 @@
      </method>
 
      <method name="removeContact">
       <parameter name="aContact"/>
       <body>
       <![CDATA[
         let contact = this.contactsById[aContact.id];
         if (!contact)
-          throw "Removing a contact that isn't here?";
+          throw new Error("Removing a contact that isn't here?");
 
         // create a new array to remove without breaking for each loops.
         this.contacts = this.contacts.filter(c => c !== contact);
         delete this.contactsById[contact.contact.id];
 
         contact.destroy();
 
         // Check if some contacts remain in the group, if empty hide it.
--- a/mail/components/im/content/joinchat.js
+++ b/mail/components/im/content/joinchat.js
@@ -16,17 +16,17 @@ var joinChat = {
       var proto = acc.protocol;
       var item = accountList.appendItem(acc.name, acc.id, proto.name);
       item.setAttribute("image", proto.iconBaseURI + "icon.png");
       item.setAttribute("class", "menuitem-iconic");
       item.account = acc;
     }
     if (!accountList.itemCount) {
       document.getElementById("joinChatDialog").cancelDialog();
-      throw "No connected MUC enabled account!";
+      throw new Error("No connected MUC enabled account!");
     }
     accountList.selectedIndex = 0;
   },
 
   onAccountSelect() {
     let ab = document.getElementById("separatorRow1");
     while (ab.nextSibling && ab.nextSibling.id != "separatorRow2")
       ab.nextSibling.remove();
@@ -89,17 +89,17 @@ var joinChat = {
 
   join() {
     let values = joinChat._values;
     for (let field of joinChat._fields) {
       let val = field.textbox.value.trim();
       if (!val && field.field.required) {
         field.textbox.focus();
         // FIXME: why isn't the return false enough?
-        throw "Some required fields are empty!";
+        throw new Error("Some required fields are empty!");
         // return false;
       }
       if (val)
         values.setValue(field.field.identifier, val);
     }
     let account = joinChat._account;
     account.joinChat(values);
 
--- a/mail/components/im/modules/index_im.jsm
+++ b/mail/components/im/modules/index_im.jsm
@@ -598,17 +598,17 @@ var GlodaIMIndexer = {
       let id = await this._getIdFromPath(relativePath);
       if (id)
         glodaConv.id = id;
       if (aGlodaConv)
         aGlodaConv.value = glodaConv;
     }
 
     if (!aCache)
-      throw "indexIMConversation called without aCache parameter.";
+      throw new Error("indexIMConversation called without aCache parameter.");
     let isNew = !Object.prototype.hasOwnProperty.call(aCache, fileName) && !glodaConv.id;
     let rv = aCallbackHandle.pushAndGo(
       Gloda.grokNounItem(glodaConv, {}, true, isNew, aCallbackHandle));
 
     if (!aLastModifiedTime)
       Cu.reportError("indexIMConversation called without lastModifiedTime parameter.");
     aCache[fileName] = aLastModifiedTime || 1;
     this._scheduleCacheSave();
--- a/mail/components/newmailaccount/content/accountProvisioner.js
+++ b/mail/components/newmailaccount/content/accountProvisioner.js
@@ -339,17 +339,17 @@ var EmailAccountProvisioner = {
       let resultsGroup = event.target;
       while (resultsGroup) {
         if (resultsGroup.classList.contains("resultsGroup")) {
           break;
         }
         resultsGroup = resultsGroup.parentElement;
       }
       if (!resultsGroup)
-        throw "Unexpected error finding resultsGroup.";
+        throw new Error("Unexpected error finding resultsGroup.");
 
       // Return if we're already expanded
       if (resultsGroup.classList.contains("expanded"))
         return;
 
       for (let child of resultsGroup.parentElement.children) {
         if (child != resultsGroup) {
           child.classList.remove("expanded");
--- a/mail/components/preferences/preferencesTab.js
+++ b/mail/components/preferences/preferencesTab.js
@@ -51,17 +51,17 @@ var preferencesTabType = {
   },
 
   closeTab(aTab) {
     gPrefTab = null;
   },
 
   openTab(aTab, aArgs) {
     if (!("contentPage" in aArgs)) {
-      throw ("contentPage must be specified");
+      throw new Error("contentPage must be specified");
     }
 
     // First clone the page and set up the basics.
     let clone = document.getElementById("preferencesTab").firstChild
                         .cloneNode(true);
 
     clone.setAttribute("id", "preferencesTab" + this.lastBrowserId);
     clone.setAttribute("collapsed", false);
--- a/mail/components/search/content/SpotlightIntegration.js
+++ b/mail/components/search/content/SpotlightIntegration.js
@@ -102,17 +102,17 @@ SearchIntegration = { // eslint-disable-
 
     // Encodes reserved XML characters
     _xmlEscapeString(s) {
       return s.replace(/[<>&]/g, function(s) {
         switch (s) {
           case "<": return "&lt;";
           case ">": return "&gt;";
           case "&": return "&amp;";
-          default: throw "Unexpected match";
+          default: throw new Error("Unexpected match");
           }
         }
       );
     },
 
     onStartRequest(request) {
       try {
         let outputFileStream = Cc["@mozilla.org/network/file-output-stream;1"]
--- a/mail/test/mozmill/folder-tree-modes/test-smart-folders.js
+++ b/mail/test/mozmill/folder-tree-modes/test-smart-folders.js
@@ -190,17 +190,17 @@ function test_folder_flag_changes() {
   let localArchiveFolder = rootFolder.getChildNamed("Archives");
   let allDescendants = localArchiveFolder.descendants;
   let desiredScope = "|" + localArchiveFolder.URI + "|";
   for (let folder of fixIterator(allDescendants, Ci.nsIMsgFolder)) {
     desiredScope += folder.URI + "|";
   }
 
   if (archiveScope != desiredScope)
-    throw "archive scope wrong after removing folder";
+    throw new Error("archive scope wrong after removing folder");
   assert_folder_and_children_not_in_scope(archiveFolder, archiveScope);
 }
 
 function assert_folder_and_children_in_scope(folder, searchScope)
 {
   let folderURI = "|" + folder.URI + "|";
   assert_uri_found(folderURI, searchScope);
   let allDescendants = folder.descendants;
--- a/mail/test/mozmill/shared-modules/test-mouse-event-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-mouse-event-helpers.js
@@ -75,17 +75,17 @@ function drag_n_drop_element(aDragObject
  */
 function synthesize_drag_start(aWindow, aDispatcher, aListener)
 {
   let dt;
 
   let trapDrag = function(event) {
 
     if (!event.dataTransfer)
-      throw "no DataTransfer";
+      throw new Error("no DataTransfer");
 
     dt = event.dataTransfer;
 
     event.preventDefault();
   };
 
   aListener.addEventListener("dragstart", trapDrag, true);
 
--- a/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/modules/server.js
+++ b/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/modules/server.js
@@ -216,17 +216,17 @@ Bridge.prototype.execFunction = function
   } else if (result) {
     this.session.encodeOut({
       result: true,
       data: null,
       uuid,
     });
   } else {
     log("jsbridge threw unknown data in execFunc");
-    throw "JSBridge unknown data in execFunc";
+    throw new Error("JSBridge unknown data in execFunc");
   }
 };
 
 var backstage = this;
 
 function Session(transport) {
   this.transpart = transport;  // XXX Unused, needed to hold reference? Note the typo.
   let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
--- a/mail/test/resources/mozmill/mozmill/extension/content/stdlib/EventUtils.jsm
+++ b/mail/test/resources/mozmill/mozmill/extension/content/stdlib/EventUtils.jsm
@@ -446,17 +446,17 @@ function _createKeyboardEventDictionary(
       aKeyEvent.keyCode : 0;
   var keyName = "Unidentified";
   if (aKey.indexOf("KEY_") == 0) {
     keyName = aKey.substr("KEY_".length);
     result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (aKey.indexOf("VK_") == 0) {
     keyCode = _getKeyboardEvent(aWindow)["DOM_" + aKey];
     if (!keyCode) {
-      throw "Unknown key: " + aKey;
+      throw new Error("Unknown key: " + aKey);
     }
     keyName = _guessKeyNameFromKeyCode(keyCode, aWindow);
     result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (aKey != "") {
     keyName = aKey;
     if (!keyCodeIsDefined) {
       keyCode = _computeKeyCodeFromChar(aKey.charAt(0), aWindow);
     }
--- a/mail/test/resources/mozmill/mozmill/extension/content/stdlib/httpd.jsm
+++ b/mail/test/resources/mozmill/mozmill/extension/content/stdlib/httpd.jsm
@@ -1462,17 +1462,17 @@ RequestReader.prototype = {
       dumpn("*** No HTTP version in line");
       throw HTTP_400;
     }
 
     // determine HTTP version
     try {
       metadata._httpVersion = new nsHttpVersion(match[1]);
       if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_0))
-        throw "unsupported HTTP version";
+        throw new Error("unsupported HTTP version");
     } catch (e) {
       // we support HTTP/1.0 and HTTP/1.1 only
       throw HTTP_501;
     }
 
 
     var fullPath = request[1];
     var serverIdentity = this._connection.server.identity;
@@ -4259,27 +4259,27 @@ function htmlEscape(str) {
  *   a string of the form "#.#", where # is an non-negative decimal integer with
  *   or without leading zeros
  * @throws
  *   if versionString does not specify a valid HTTP version number
  */
 function nsHttpVersion(versionString) {
   var matches = /^(\d+)\.(\d+)$/.exec(versionString);
   if (!matches)
-    throw "Not a valid HTTP version!";
+    throw new Error("Not a valid HTTP version!");
 
   /** The major version number of this, as a number. */
   this.major = parseInt(matches[1], 10);
 
   /** The minor version number of this, as a number. */
   this.minor = parseInt(matches[2], 10);
 
   if (isNaN(this.major) || isNaN(this.minor) ||
       this.major < 0 || this.minor < 0)
-    throw "Not a valid HTTP version!";
+    throw new Error("Not a valid HTTP version!");
 }
 nsHttpVersion.prototype = {
   /**
    * Returns the standard string representation of the HTTP version represented
    * by this (e.g., "1.1").
    */
   toString() {
     return this.major + "." + this.minor;
--- a/mailnews/addrbook/test/unit/test_collection.js
+++ b/mailnews/addrbook/test/unit/test_collection.js
@@ -189,18 +189,18 @@ var collectChecker = {
 
   checkAddress(aDetails) {
     try {
       this.addressCollect.collectAddress(aDetails.emailHeader, true,
                                          aDetails.mailFormat);
 
       this.checkCardResult(aDetails, false);
     } catch (e) {
-      throw "FAILED in checkAddress emailHeader: " + aDetails.emailHeader +
-      " part: " + this.part + " : " + e;
+      throw new Error("FAILED in checkAddress emailHeader: " + aDetails.emailHeader +
+        " part: " + this.part + " : " + e);
     }
     ++this.part;
   },
 
   checkAll(aDetailsArray) {
     try {
       // Formulate the string to add.
       var emailHeader = "";
@@ -213,17 +213,17 @@ var collectChecker = {
 
       // Now add it. In this case we just set the Mail format Type to unknown.
       this.addressCollect.collectAddress(emailHeader, true,
                                          nsIAbPMF.unknown);
 
       for (i = 0; i < aDetailsArray.length; ++i)
         this.checkCardResult(aDetailsArray[i], true);
     } catch (e) {
-      throw "FAILED in checkAll item: " + i + " : " + e;
+      throw new Error("FAILED in checkAll item: " + i + " : " + e);
     }
   },
 
   checkCardResult(aDetails, overrideMailFormat) {
     try {
       var card = this.AB.cardForEmailAddress(aDetails.primaryEmail);
 
       Assert.ok(card != null);
@@ -238,17 +238,17 @@ var collectChecker = {
       else
         Assert.equal(card.getProperty("PreferMailFormat", "BAD"), aDetails.mailFormat);
 
       Assert.equal(card.displayName, aDetails.displayName);
       Assert.equal(card.firstName, aDetails.firstName);
       Assert.equal(card.lastName, aDetails.lastName);
       Assert.equal(card.getProperty("_AimScreenName", ""), aDetails.screenName);
     } catch (e) {
-      throw "FAILED in checkCardResult emailHeader: " + aDetails.emailHeader + " : " + e;
+      throw new Error("FAILED in checkCardResult emailHeader: " + aDetails.emailHeader + " : " + e);
     }
   },
 };
 
 function run_test() {
   // Test - Get the address collecter
 
   // XXX Getting all directories ensures we create all ABs because the
--- a/mailnews/test/fakeserver/auth.js
+++ b/mailnews/test/fakeserver/auth.js
@@ -31,27 +31,27 @@ var AuthPLAIN = {
    * @returns {Object { username : value, password : value } }
    * @throws {String}   error to return to client
    */
   decodeLine(line) {
     dump("AUTH PLAIN line -" + line + "-\n");
     line = atob(line); // base64 decode
     let aap = line.split("\u0000"); // 0-charater is delimiter
     if (aap.length != 3)
-      throw "Expected three parts";
+      throw new Error("Expected three parts");
     /* aap is: authorize-id, authenticate-id, password.
        Generally, authorize-id = authenticate-id = username.
        authorize-id may thus be empty and then defaults to authenticate-id. */
     var result = {};
     var authzid = aap[0];
     result.username = aap[1];
     result.password = aap[2];
     dump("authorize-id: -" + authzid + "-, username: -" + result.username + "-, password: -" + result.password + "-\n");
     if (authzid && authzid != result.username)
-      throw "Expecting a authorize-id that's either the same as authenticate-id or empty";
+      throw new Error("Expecting a authorize-id that's either the same as authenticate-id or empty");
     return result;
   },
 
   /**
    * Create an AUTH PLAIN line, to allow a client to authenticate to a server.
    * Useful for tests.
    */
   encodeLine(username, password) {
@@ -113,17 +113,17 @@ var AuthCRAM = {
    * @throws {String}   error to return to client
    */
   decodeLine(line) {
     dump("AUTH CRAM-MD5 line -" + line + "-\n");
     line = atob(line);
     dump("base64 decoded -" + line + "-\n");
     var sp = line.split(" ");
     if (sp.length != 2)
-      throw "Expected one space";
+      throw new Error("Expected one space");
     var result = {};
     result.username = sp[0];
     result.digest = sp[1];
     return result;
   },
   /**
    * @param text {String}   server challenge (base64-encoded)
    * @param key {String}   user's password
@@ -170,17 +170,17 @@ var AuthCRAM = {
     for (var i = 0; i < text.length; i++)
       array.push(text.charCodeAt(i) & 0xFF); // convert string (only lower byte) to array
     return array;
   },
   arrayToHexString(binary) {
     var result = "";
     for (var i = 0; i < binary.length; i++) {
       if (binary[i] > 255)
-        throw "unexpected that value > 255";
+        throw new Error("unexpected that value > 255");
       let hex = binary[i].toString(16);
       if (hex.length < 2)
         hex = "0" + hex;
       result += hex;
     }
     return result;
   },
 };
--- a/mailnews/test/fakeserver/imapd.js
+++ b/mailnews/test/fakeserver/imapd.js
@@ -497,16 +497,17 @@ function parseCommand(text, partial) {
     } else if (c == "{") {
       let end = text.indexOf("}");
       if (end == -1)
         throw new Error("Expected CLOSE_BRACKET");
       if (end + 1 != text.length)
         throw new Error("Expected CRLF");
       let length = parseInt(text.substring(1, end));
       // Usable state
+      // eslint-disable-next-line no-throw-literal
       throw { length, current, args, stack, text: "" };
     } else if (c == "(") {
       stack.push(current);
       current = [];
     } else if (c == ")") {
       if (atom.length > 0) {
         current.push(atom);
         atom = "";
--- a/mailnews/test/fakeserver/maild.js
+++ b/mailnews/test/fakeserver/maild.js
@@ -246,17 +246,17 @@ nsMailServer.prototype = {
 
   /**
    * Returns the commands run between the server and client.
    * The return is an object with two variables (us and them), both of which
    * are arrays returning the commands given by each server.
    */
   playTransaction() {
     if (this._readers.some(e => e.observer.forced))
-      throw "Server timed out!";
+      throw new Error("Server timed out!");
     if (this._readers.length == 1)
       return this._readers[0].transaction;
     return this._readers.map(e => e.transaction);
   },
 
   /**
    * Prepares for the next test.
    */
--- a/mailnews/test/resources/filterTestUtils.js
+++ b/mailnews/test/resources/filterTestUtils.js
@@ -49,29 +49,29 @@ function createFilter(list, trigger, val
     if (information[3] != null)
       searchTerm.arbitraryHeader = information[3];
     searchTerm.op = information[1];
     var oldValue = searchTerm.value;
     oldValue.attrib = information[0];
     oldValue[information[2]] = value;
     searchTerm.value = oldValue;
   } else {
-    throw "Unknown trigger " + trigger;
+    throw new Error("Unknown trigger " + trigger);
   }
   searchTerm.booleanAnd = true;
   filter.appendTerm(searchTerm);
 
   var filterAction = filter.createAction();
   if (action in ACTION_MAP) {
     let information = ACTION_MAP[action];
     filterAction.type = information[0];
     if (1 in information)
       filterAction[information[1]] = information[2];
   } else {
-    throw "Unknown action " + action;
+    throw new Error("Unknown action " + action);
   }
   filter.appendAction(filterAction);
 
   filter.enabled = true;
 
   // Add to the end
   list.insertFilterAt(list.filterCount, filter);
 }