Bug 1229874: Part 3 - Enable ESLint in WebExtension code. r=billm
authorKris Maglione <maglione.k@gmail.com>
Wed, 02 Dec 2015 16:58:53 -0800
changeset 309777 b1821f8cf26114fe25d5da0c479377379ab53b7b
parent 309776 378ef42875ed296adb790020ebeee26490c026d8
child 309778 0914d89dc7b94e42da4a75e7036032e0e4cbb791
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1229874
milestone45.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 1229874: Part 3 - Enable ESLint in WebExtension code. r=billm The base .eslintrc is essentially a merge of the root Toolkit .eslintrc and the devtools .eslintrc, with some minor changes to match our prevalent style. For the most enforces the coding styles that we've been using most consistently. There are a couple of significant differences, though: * The rule for opening brace alignment can only be applied globally, and doesn't make exceptions for top-level functions. I chose to turn it on, and change the brace style of existing top-level functions that violated it, since the rule seemed worth using, and that's the direction most Toolkit JS code has been headed anyway. * The rule for switch/case statements requires an added indentation level for case statements. Most of our switch statements did not use an extra level of indentation, and I initially wrote the rule to enforce that style, until I came across case statements that used blocks, and required the extra indentation level for sanity.
.eslintignore
browser/components/extensions/.eslintrc
browser/components/extensions/ext-bookmarks.js
browser/components/extensions/ext-browserAction.js
browser/components/extensions/ext-contextMenus.js
browser/components/extensions/ext-pageAction.js
browser/components/extensions/ext-tabs.js
browser/components/extensions/ext-utils.js
browser/components/extensions/ext-windows.js
browser/components/extensions/test/browser/.eslintrc
browser/components/extensions/test/browser/browser_ext_browserAction_context.js
browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
browser/components/extensions/test/browser/browser_ext_contextMenus.js
browser/components/extensions/test/browser/browser_ext_currentWindow.js
browser/components/extensions/test/browser/browser_ext_getViews.js
browser/components/extensions/test/browser/browser_ext_pageAction_context.js
browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
browser/components/extensions/test/browser/browser_ext_popup_api_injection.js
browser/components/extensions/test/browser/browser_ext_simple.js
browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
browser/components/extensions/test/browser/browser_ext_tabs_query.js
browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
browser/components/extensions/test/browser/browser_ext_tabs_update.js
browser/components/extensions/test/browser/browser_ext_windows_update.js
browser/components/extensions/test/browser/head.js
toolkit/components/extensions/.eslintrc
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/ExtensionManagement.jsm
toolkit/components/extensions/ExtensionStorage.jsm
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/ext-alarms.js
toolkit/components/extensions/ext-backgroundPage.js
toolkit/components/extensions/ext-cookies.js
toolkit/components/extensions/ext-extension.js
toolkit/components/extensions/ext-i18n.js
toolkit/components/extensions/ext-idle.js
toolkit/components/extensions/ext-notifications.js
toolkit/components/extensions/ext-runtime.js
toolkit/components/extensions/ext-storage.js
toolkit/components/extensions/ext-test.js
toolkit/components/extensions/ext-webNavigation.js
toolkit/components/extensions/ext-webRequest.js
toolkit/components/extensions/test/mochitest/.eslintrc
toolkit/components/extensions/test/mochitest/file_script_bad.js
toolkit/components/extensions/test/mochitest/file_script_good.js
toolkit/components/extensions/test/mochitest/file_script_redirect.js
toolkit/components/extensions/test/mochitest/file_script_xhr.js
toolkit/components/extensions/test/mochitest/head.js
toolkit/components/extensions/test/xpcshell/.eslintrc
toolkit/modules/addons/.eslintrc
toolkit/modules/addons/MatchPattern.jsm
toolkit/modules/addons/WebNavigation.jsm
toolkit/modules/addons/WebNavigationContent.js
toolkit/modules/addons/WebRequest.jsm
toolkit/modules/addons/WebRequestCommon.jsm
toolkit/modules/addons/WebRequestContent.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -192,15 +192,15 @@ toolkit/components/places/**
 toolkit/mozapps/extensions/**
 
 # Uses preprocessing
 toolkit/content/contentAreaUtils.js
 toolkit/components/jsdownloads/src/DownloadIntegration.jsm
 toolkit/components/search/nsSearchService.js
 toolkit/components/url-classifier/**
 toolkit/components/urlformatter/nsURLFormatter.js
-toolkit/identity/FirefoxAccounts.jsm 
+toolkit/identity/FirefoxAccounts.jsm
 toolkit/modules/AppConstants.jsm
 toolkit/modules/SessionRecorder.jsm
 toolkit/mozapps/downloads/nsHelperAppDlg.js
 toolkit/mozapps/extensions/internal/AddonConstants.jsm
 toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
 toolkit/webapps/**
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/.eslintrc
@@ -0,0 +1,16 @@
+{
+  "extends": "../../../toolkit/components/extensions/.eslintrc",
+
+  "globals": {
+    "currentWindow": true,
+    "EventEmitter": true,
+    "IconDetails": true,
+    "openPanel": true,
+    "makeWidgetId": true,
+    "TabContext": true,
+    "AllWindowEvents": true,
+    "WindowEventManager": true,
+    "WindowListManager": true,
+    "WindowManager": true,
+  },
+}
--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/ext-bookmarks.js
@@ -1,16 +1,19 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 var Bookmarks = PlacesUtils.bookmarks;
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
-  EventManager,
   runSafe,
 } = ExtensionUtils;
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 function getTree(rootGuid, onlyChildren) {
   function convert(node, parent) {
@@ -35,21 +38,22 @@ function getTree(rootGuid, onlyChildren)
         treenode.children = node.children.map(child => convert(child, node));
       }
     }
 
     return treenode;
   }
 
   return PlacesUtils.promiseBookmarksTree(rootGuid, {
-    excludeItemsCallback: aItem => {
-      if (aItem.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR)
+    excludeItemsCallback: item => {
+      if (item.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
         return true;
-      return aItem.annos &&
-             aItem.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
+      }
+      return item.annos &&
+             item.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
     },
   }).then(root => {
     if (onlyChildren) {
       let children = root.children || [];
       return children.map(child => convert(child, root));
     } else {
       // It seems like the array always just contains the root node.
       return [convert(root, null)];
@@ -163,24 +167,24 @@ extensions.registerPrivilegedAPI("bookma
         };
 
         try {
           Bookmarks.insert(info).then(result => {
             if (callback) {
               runSafe(context, callback, convert(result));
             }
           }, failure);
-        } catch(e) {
+        } catch (e) {
           failure(e);
         }
       },
 
       move: function(id, destination, callback) {
         let info = {
-          guid: id
+          guid: id,
         };
 
         if ("parentId" in destination) {
           info.parentGuid = destination.parentId;
         }
         if ("index" in destination) {
           info.index = destination.index;
         }
@@ -199,17 +203,17 @@ extensions.registerPrivilegedAPI("bookma
           }, failure);
         } catch (e) {
           failure(e);
         }
       },
 
       update: function(id, changes, callback) {
         let info = {
-          guid: id
+          guid: id,
         };
 
         if ("title" in changes) {
           info.title = changes.title;
         }
         if ("url" in changes) {
           info.url = changes.url;
         }
@@ -228,17 +232,17 @@ extensions.registerPrivilegedAPI("bookma
           }, failure);
         } catch (e) {
           failure(e);
         }
       },
 
       remove: function(id, callback) {
         let info = {
-          guid: id
+          guid: id,
         };
 
         let failure = reason => {
           if (callback) {
             runSafe(context, callback, null);
           }
         };
 
@@ -247,14 +251,14 @@ extensions.registerPrivilegedAPI("bookma
             if (callback) {
               // The API doesn't give you the old bookmark at the moment
               runSafe(context, callback);
             }
           }, failure);
         } catch (e) {
           failure(e);
         }
-      }
-    }
+      },
+    },
   };
 });
 
 
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -5,34 +5,29 @@
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
-  DefaultWeakMap,
   runSafe,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> BrowserAction]
 var browserActionMap = new WeakMap();
 
-function browserActionOf(extension)
-{
+function browserActionOf(extension) {
   return browserActionMap.get(extension);
 }
 
-var nextActionId = 0;
-
 // Responsible for the browser_action section of the manifest as well
 // as the associated popup.
-function BrowserAction(options, extension)
-{
+function BrowserAction(options, extension) {
   this.extension = extension;
   this.id = makeWidgetId(extension.id) + "-browser-action";
   this.widget = null;
 
   this.tabManager = TabManager.for(extension);
 
   let title = extension.localize(options.default_title || "");
   let popup = extension.localize(options.default_popup || "");
@@ -68,33 +63,33 @@ BrowserAction.prototype = {
         node.id = this.id;
         node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button");
         node.setAttribute("constrain-size", "true");
 
         this.updateButton(node, this.defaults);
 
         let tabbrowser = document.defaultView.gBrowser;
 
-        node.addEventListener("command", event => {
+        node.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
           let tab = tabbrowser.selectedTab;
           let popup = this.getProperty(tab, "popup");
           this.tabManager.addActiveTabPermission(tab);
           if (popup) {
             this.togglePopup(node, popup);
           } else {
             this.emit("click");
           }
         });
 
         return node;
       },
     });
 
-    this.tabContext.on("tab-select",
-                       (evt, tab) => { this.updateWindow(tab.ownerDocument.defaultView); })
+    this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
+                       (evt, tab) => { this.updateWindow(tab.ownerDocument.defaultView); });
 
     this.widget = widget;
   },
 
   togglePopup(node, popupResource) {
     openPanel(node, popupResource, this.extension);
   },
 
@@ -119,17 +114,17 @@ BrowserAction.prototype = {
 
     if (tabData.enabled) {
       node.removeAttribute("disabled");
     } else {
       node.setAttribute("disabled", "true");
     }
 
     let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
-                                        'class', 'toolbarbutton-badge');
+                                        "class", "toolbarbutton-badge");
     if (badgeNode) {
       let color = tabData.badgeBackgroundColor;
       if (Array.isArray(color)) {
         color = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
       }
       badgeNode.style.backgroundColor = color || "";
     }
 
@@ -185,28 +180,30 @@ BrowserAction.prototype = {
   },
 
   shutdown() {
     this.tabContext.shutdown();
     CustomizableUI.destroyWidget(this.id);
   },
 };
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_browser_action", (type, directive, extension, manifest) => {
   let browserAction = new BrowserAction(manifest.browser_action, extension);
   browserAction.build();
   browserActionMap.set(extension, browserAction);
 });
 
 extensions.on("shutdown", (type, extension) => {
   if (browserActionMap.has(extension)) {
     browserActionMap.get(extension).shutdown();
     browserActionMap.delete(extension);
   }
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerAPI((extension, context) => {
   return {
     browserAction: {
       onClicked: new EventManager(context, "browserAction.onClicked", fire => {
         let listener = () => {
           let tab = TabManager.activeTab;
           fire(TabManager.convert(extension, tab));
@@ -268,21 +265,20 @@ extensions.registerAPI((extension, conte
 
       getPopup: function(details, callback) {
         let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
         let popup = browserActionOf(extension).getProperty(tab, "popup");
         runSafe(context, callback, popup);
       },
 
       setBadgeBackgroundColor: function(details) {
-        let color = details.color;
         let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
         browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color);
       },
 
       getBadgeBackgroundColor: function(details, callback) {
         let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
         let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor");
         runSafe(context, callback, color);
       },
-    }
+    },
   };
 });
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -1,36 +1,34 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 Cu.import("resource://gre/modules/MatchPattern.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var {
   EventManager,
-  contextMenuItems,
-  runSafe
+  runSafe,
 } = ExtensionUtils;
 
 
 // Map[Extension -> Map[ID -> MenuItem]]
 // Note: we want to enumerate all the menu items so
 // this cannot be a weak map.
 var contextMenuMap = new Map();
 
 // Not really used yet, will be used for event pages.
 var onClickedCallbacksMap = new WeakMap();
 
 // If id is not specified for an item we use an integer.
 var nextID = 0;
 
-function contextMenuObserver(subject, topic, data) {
-  subject = subject.wrappedJSObject;
-  menuBuilder.build(subject);
-}
-
 // When a new contextMenu is opened, this function is called and
 // we populate the |xulMenu| with all the items from extensions
 // to be displayed. We always clear all the items again when
 // popuphidden fires. Since most of the info we need is already
 // calculated in nsContextMenu.jsm we simple reuse its flags here.
 // For remote processes there is a gContextMenuContentData where all
 // the important info is stored from the child process. We get
 // this data in |contextData|.
@@ -77,17 +75,17 @@ var menuBuilder = {
             // If parentId is set we have to look up its parent
             // and appened to its menupopup.
             let parentElement = parentMap.get(parentId);
             parentElement.appendChild(element);
           } else {
             topLevelItems.add(element);
           }
 
-          element.addEventListener("command", event => {
+          element.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
             item.tabManager.addActiveTabPermission();
             if (item.onclick) {
               let clickData = item.getClickData(contextData, event);
               runSafe(item.extContext, item.onclick, clickData);
             }
           });
         }
       }
@@ -119,68 +117,72 @@ var menuBuilder = {
     for (let item of this._itemsToCleanUp) {
       target.removeChild(item);
     }
     // Let's detach the top level items.
     this._itemsToCleanUp.clear();
   },
 
   _itemsToCleanUp: new Set(),
+};
+
+function contextMenuObserver(subject, topic, data) {
+  subject = subject.wrappedJSObject;
+  menuBuilder.build(subject);
 }
 
 function getContexts(contextData) {
   let contexts = new Set(["all"]);
 
   contexts.add("page");
 
   if (contextData.inFrame) {
-    contexts.add("frame")
+    contexts.add("frame");
   }
 
   if (contextData.isTextSelected) {
-    contexts.add("selection")
+    contexts.add("selection");
   }
 
   if (contextData.onLink) {
-    contexts.add("link")
+    contexts.add("link");
   }
 
   if (contextData.onEditableArea) {
-    contexts.add("editable")
+    contexts.add("editable");
   }
 
   if (contextData.onImage) {
-    contexts.add("image")
+    contexts.add("image");
   }
 
   if (contextData.onVideo) {
-    contexts.add("video")
+    contexts.add("video");
   }
 
   if (contextData.onAudio) {
-    contexts.add("audio")
+    contexts.add("audio");
   }
 
   return contexts;
 }
 
-function MenuItem(extension, extContext, createProperties)
-{
+function MenuItem(extension, extContext, createProperties) {
   this.extension = extension;
   this.extContext = extContext;
 
   this.tabManager = TabManager.for(extension);
 
   this.init(createProperties);
 }
 
 MenuItem.prototype = {
   // init is called from the MenuItem ctor and from update. The |update|
   // flag is for the later case.
-  init(createProperties, update=false) {
+  init(createProperties, update = false) {
     let item = this;
     // return true if the prop was set on |this|
     function parseProp(propName, defaultValue = null) {
       if (propName in createProperties) {
         item[propName] = createProperties[propName];
         return true;
       } else if (!update && defaultValue !== null) {
         item[propName] = defaultValue;
@@ -209,18 +211,18 @@ MenuItem.prototype = {
     if ("onclick" in createProperties.wrappedJSObject) {
       this.onclick = createProperties.wrappedJSObject.onclick;
     }
 
     if (parseProp("parentId")) {
       let found = false;
       let menuMap = contextMenuMap.get(this.extension);
       if (menuMap.has(this.parentId)) {
-          found = true;
-          menuMap.get(this.parentId).isMenu = true;
+        found = true;
+        menuMap.get(this.parentId).isMenu = true;
       }
       if (!found) {
         throw new Error("MenuItem with this parentId not found");
       }
     } else {
       this.parentId = undefined;
     }
 
@@ -260,17 +262,17 @@ MenuItem.prototype = {
       }
       let rv = hasAncestorWithId(parent, id);
       checked.set(item, rv);
       return rv;
     }
 
     let toRemove = new Set();
     toRemove.add(this.id);
-    for (let [id, item] of menuMap) {
+    for (let [, item] of menuMap) {
       if (hasAncestorWithId(item, this.id)) {
         toRemove.add(item.id);
       }
     }
     for (let id of toRemove) {
       menuMap.delete(id);
     }
   },
@@ -283,17 +285,17 @@ MenuItem.prototype = {
     if (contextData.onAudio) {
       mediaType = "audio";
     }
     if (contextData.onImage) {
       mediaType = "image";
     }
 
     let clickData = {
-      menuItemId: this.id
+      menuItemId: this.id,
     };
 
     function setIfDefined(argName, value) {
       if (value) {
         clickData[argName] = value;
       }
     }
 
@@ -313,17 +315,17 @@ MenuItem.prototype = {
   },
 
   enabledForContext(contextData) {
     let enabled = false;
     let contexts = getContexts(contextData);
     for (let c of this.contexts) {
       if (contexts.has(c)) {
         enabled = true;
-        break
+        break;
       }
     }
     if (!enabled) {
       return false;
     }
 
     if (this.documentUrlMatchPattern &&
         !this.documentUrlMatchPattern.matches(contextData.documentURIObject)) {
@@ -337,32 +339,34 @@ MenuItem.prototype = {
       return false;
     }
 
     return true;
   },
 };
 
 var extCount = 0;
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
   contextMenuMap.set(extension, new Map());
   if (++extCount == 1) {
     Services.obs.addObserver(contextMenuObserver,
                              "on-build-contextmenu",
                              false);
   }
 });
 
 extensions.on("shutdown", (type, extension) => {
   contextMenuMap.delete(extension);
   if (--extCount == 0) {
     Services.obs.removeObserver(contextMenuObserver,
                                 "on-build-contextmenu");
   }
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerPrivilegedAPI("contextMenus", (extension, context) => {
   return {
     contextMenus: {
       create: function(createProperties, callback) {
         let menuItem = new MenuItem(extension, context, createProperties);
         contextMenuMap.get(extension).set(menuItem.id, menuItem);
         if (callback) {
@@ -387,17 +391,17 @@ extensions.registerPrivilegedAPI("contex
           menuItem.remove();
         }
         if (callback) {
           runSafe(context, callback);
         }
       },
 
       removeAll: function(callback) {
-        for (let [id, menuItem] of contextMenuMap.get(extension)) {
+        for (let [, menuItem] of contextMenuMap.get(extension)) {
           menuItem.remove();
         }
         if (callback) {
           runSafe(context, callback);
         }
       },
 
       // TODO: implement this once event pages are ready.
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -1,27 +1,25 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
-  DefaultWeakMap,
   runSafe,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> PageAction]
 var pageActionMap = new WeakMap();
 
 
 // Handles URL bar icons, including the |page_action| manifest entry
 // and associated API.
-function PageAction(options, extension)
-{
+function PageAction(options, extension) {
   this.extension = extension;
   this.id = makeWidgetId(extension.id) + "-page-action";
 
   this.tabManager = TabManager.for(extension);
 
   let title = extension.localize(options.default_title || "");
   let popup = extension.localize(options.default_popup || "");
   if (popup) {
@@ -34,17 +32,17 @@ function PageAction(options, extension)
     icon: IconDetails.normalize({ path: options.default_icon }, extension,
                                 null, true),
     popup: popup && extension.baseURI.resolve(popup),
   };
 
   this.tabContext = new TabContext(tab => Object.create(this.defaults),
                                    extension);
 
-  this.tabContext.on("location-change", this.handleLocationChange.bind(this));
+  this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
 
   // WeakMap[ChromeWindow -> <xul:image>]
   this.buttons = new WeakMap();
 
   EventEmitter.decorate(this);
 }
 
 PageAction.prototype = {
@@ -105,17 +103,17 @@ PageAction.prototype = {
   // container in the given window.
   addButton(window) {
     let document = window.document;
 
     let button = document.createElement("image");
     button.id = this.id;
     button.setAttribute("class", "urlbar-icon");
 
-    button.addEventListener("click", event => {
+    button.addEventListener("click", event => { // eslint-disable-line mozilla/balanced-listeners
       if (event.button == 0) {
         this.handleClick(window);
       }
     });
 
     document.getElementById("urlbar-icons").appendChild(button);
 
     return button;
@@ -168,27 +166,29 @@ PageAction.prototype = {
   },
 };
 
 PageAction.for = extension => {
   return pageActionMap.get(extension);
 };
 
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_page_action", (type, directive, extension, manifest) => {
   let pageAction = new PageAction(manifest.page_action, extension);
   pageActionMap.set(extension, pageAction);
 });
 
 extensions.on("shutdown", (type, extension) => {
   if (pageActionMap.has(extension)) {
     pageActionMap.get(extension).shutdown();
     pageActionMap.delete(extension);
   }
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 
 extensions.registerAPI((extension, context) => {
   return {
     pageAction: {
       onClicked: new EventManager(context, "pageAction.onClicked", fire => {
         let listener = (evt, tab) => {
           fire(TabManager.convert(extension, tab));
@@ -239,11 +239,11 @@ extensions.registerAPI((extension, conte
         PageAction.for(extension).setProperty(tab, "popup", url);
       },
 
       getPopup(details, callback) {
         let tab = TabManager.getTab(details.tabId);
         let popup = PageAction.for(extension).getProperty(tab, "popup");
         runSafe(context, callback, popup);
       },
-    }
+    },
   };
 });
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -1,57 +1,59 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
+/* globals aboutNewTabService */
+
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   EventManager,
   ignoreEvent,
   runSafe,
 } = ExtensionUtils;
 
 // This function is pretty tightly tied to Extension.jsm.
 // Its job is to fill in the |tab| property of the sender.
-function getSender(context, target, sender)
-{
+function getSender(context, target, sender) {
   // The message was sent from a content script to a <browser> element.
   // We can just get the |tab| from |target|.
   if (target instanceof Ci.nsIDOMXULElement) {
     // The message came from a content script.
     let tabbrowser = target.ownerDocument.defaultView.gBrowser;
     if (!tabbrowser) {
       return;
     }
     let tab = tabbrowser.getTabForBrowser(target);
 
     sender.tab = TabManager.convert(context.extension, tab);
-  } else {
+  } else if ("tabId" in sender) {
     // The message came from an ExtensionPage. In that case, it should
     // include a tabId property (which is filled in by the page-open
     // listener below).
-    if ("tabId" in sender) {
-      sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
-      delete sender.tabId;
-    }
+    sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
+    delete sender.tabId;
   }
 }
 
 // WeakMap[ExtensionPage -> {tab, parentWindow}]
 var pageDataMap = new WeakMap();
 
+/* eslint-disable mozilla/balanced-listeners */
 // This listener fires whenever an extension page opens in a tab
 // (either initiated by the extension or the user). Its job is to fill
 // in some tab-specific details and keep data around about the
 // ExtensionPage.
 extensions.on("page-load", (type, page, params, sender, delegate) => {
   if (params.type == "tab" || params.type == "popup") {
     let browser = params.docShell.chromeEventHandler;
 
@@ -90,25 +92,25 @@ extensions.on("fill-browser-data", (type
   let tabId = TabManager.getBrowserId(browser);
   if (tabId == -1) {
     result.cancel = true;
     return;
   }
 
   data.tabId = tabId;
 });
+/* eslint-enable mozilla/balanced-listeners */
 
-global.currentWindow = function(context)
-{
+global.currentWindow = function(context) {
   let pageData = pageDataMap.get(context);
   if (pageData) {
     return pageData.parentWindow;
   }
   return WindowManager.topWindow;
-}
+};
 
 // TODO: activeTab permission
 
 extensions.registerAPI((extension, context) => {
   let self = {
     tabs: {
       onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
         let tab = event.originalTarget;
@@ -206,17 +208,17 @@ extensions.registerAPI((extension, conte
             if (!webProgress.isTopLevel) {
               return;
             }
             let gBrowser = browser.ownerDocument.defaultView.gBrowser;
             let tab = gBrowser.getTabForBrowser(browser);
             let tabId = TabManager.getId(tab);
             let [needed, changeInfo] = sanitize(extension, {
               status: webProgress.isLoadingDocument ? "loading" : "complete",
-              url: locationURI.spec
+              url: locationURI.spec,
             });
             if (needed) {
               fire(tabId, changeInfo, TabManager.convert(extension, tab));
             }
           },
         };
 
         AllWindowEvents.addListener("progress", progressListener);
@@ -384,17 +386,17 @@ extensions.registerAPI((extension, conte
           tab = TabManager.convert(extension, TabManager.getTab(context.tabId));
         }
         runSafe(context, callback, tab);
       },
 
       getAllInWindow: function(...args) {
         let window, callback;
         if (args.length == 1) {
-          callbacks = args[0];
+          callback = args[0];
         } else {
           window = WindowManager.getWindow(args[0]);
           callback = args[1];
         }
 
         if (!window) {
           window = WindowManager.topWindow;
         }
@@ -430,20 +432,18 @@ extensions.registerAPI((extension, conte
             return false;
           }
 
           if ("windowId" in queryInfo) {
             if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) {
               if (currentWindow(context) != window) {
                 return false;
               }
-            } else {
-              if (queryInfo.windowId != tab.windowId) {
-                return false;
-              }
+            } else if (queryInfo.windowId != tab.windowId) {
+              return false;
             }
           }
 
           if ("currentWindow" in queryInfo) {
             let eq = window == currentWindow(context);
             if (queryInfo.currentWindow != eq) {
               return false;
             }
@@ -490,17 +490,17 @@ extensions.registerAPI((extension, conte
           // If we have the "activeTab" permission for this tab, ignore
           // the host whitelist.
           options.matchesHost = ["<all_urls>"];
         } else {
           options.matchesHost = extension.whiteListedHosts.serialize();
         }
 
         if (details.code) {
-          options[kind + 'Code'] = details.code;
+          options[kind + "Code"] = details.code;
         }
         if (details.file) {
           let url = context.uri.resolve(details.file);
           if (extension.isExtensionURL(url)) {
             // We should really set |lastError| here, and go straight to
             // the callback, but we don't have |lastError| yet.
             options[kind].push(url);
           }
@@ -517,27 +517,27 @@ extensions.registerAPI((extension, conte
         mm.sendAsyncMessage("Extension:Execute",
                             {extensionId: extension.id, options});
 
         // TODO: Call the callback with the result (which is what???).
       },
 
       executeScript: function(...args) {
         if (args.length == 1) {
-          self.tabs._execute(undefined, args[0], 'js', undefined);
+          self.tabs._execute(undefined, args[0], "js", undefined);
         } else {
-          self.tabs._execute(args[0], args[1], 'js', args[2]);
+          self.tabs._execute(args[0], args[1], "js", args[2]);
         }
       },
 
-      insertCss: function(tabId, details, callback) {
+      insertCss: function(...args) {
         if (args.length == 1) {
-          self.tabs._execute(undefined, args[0], 'css', undefined);
+          self.tabs._execute(undefined, args[0], "css", undefined);
         } else {
-          self.tabs._execute(args[0], args[1], 'css', args[2]);
+          self.tabs._execute(args[0], args[1], "css", args[2]);
         }
       },
 
       connect: function(tabId, connectInfo) {
         let tab = TabManager.getTab(tabId);
         let mm = tab.linkedBrowser.messageManager;
 
         let name = connectInfo.name || "";
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -26,17 +26,17 @@ global.IconDetails = {
   // with icon size as key and icon URL as value.
   //
   // If a context is specified (function is called from an extension):
   // Throws an error if an invalid icon size was provided or the
   // extension is not allowed to load the specified resources.
   //
   // If no context is specified, instead of throwing an error, this
   // function simply logs a warning message.
-  normalize(details, extension, context=null, localize=false) {
+  normalize(details, extension, context = null, localize = false) {
     let result = {};
 
     try {
       if (details.imageData) {
         let imageData = details.imageData;
 
         if (imageData instanceof Cu.getGlobalForObject(imageData).ImageData) {
           imageData = {"19": imageData};
@@ -107,24 +107,24 @@ global.IconDetails = {
   convertImageDataToPNG(imageData, context) {
     let document = context.contentWindow.document;
     let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = imageData.width;
     canvas.height = imageData.height;
     canvas.getContext("2d").putImageData(imageData, 0, 0);
 
     return canvas.toDataURL("image/png");
-  }
+  },
 };
 
 global.makeWidgetId = id => {
   id = id.toLowerCase();
   // FIXME: This allows for collisions.
   return id.replace(/[^a-z0-9_-]/g, "_");
-}
+};
 
 // Open a panel anchored to the given node, containing a browser opened
 // to the given URL, owned by the given extension. If |popupURL| is not
 // an absolute URL, it is resolved relative to the given extension's
 // base URL.
 global.openPanel = (node, popupURL, extension) => {
   let document = node.ownerDocument;
 
@@ -156,24 +156,26 @@ global.openPanel = (node, popupURL, exte
   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   let browser = document.createElementNS(XUL_NS, "browser");
   browser.setAttribute("type", "content");
   browser.setAttribute("disableglobalhistory", "true");
   panel.appendChild(browser);
 
   let titleChangedListener = () => {
     panel.setAttribute("aria-label", browser.contentTitle);
-  }
+  };
 
   let context;
-  panel.addEventListener("popuphidden", () => {
+  let popuphidden = () => {
+    panel.removeEventListener("popuphidden", popuphidden);
     browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
     context.unload();
     panel.remove();
-  });
+  };
+  panel.addEventListener("popuphidden", popuphidden);
 
   let loadListener = () => {
     panel.removeEventListener("load", loadListener);
 
     context = new ExtensionPage(extension, {
       type: "popup",
       contentWindow: browser.contentWindow,
       uri: popupURI,
@@ -211,31 +213,31 @@ global.openPanel = (node, popupURL, exte
     };
     browser.addEventListener("load", contentLoadListener, true);
 
     browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
   };
   panel.addEventListener("load", loadListener);
 
   return panel;
-}
+};
 
 // Manages tab-specific context data, and dispatching tab select events
 // across all windows.
 global.TabContext = function TabContext(getDefaults, extension) {
   this.extension = extension;
   this.getDefaults = getDefaults;
 
   this.tabData = new WeakMap();
 
   AllWindowEvents.addListener("progress", this);
   AllWindowEvents.addListener("TabSelect", this);
 
   EventEmitter.decorate(this);
-}
+};
 
 TabContext.prototype = {
   get(tab) {
     if (!this.tabData.has(tab)) {
       this.tabData.set(tab, this.getDefaults(tab));
     }
 
     return this.tabData.get(tab);
@@ -306,17 +308,16 @@ ExtensionTabManager.prototype = {
   },
 
   hasTabPermission(tab) {
     return this.extension.hasPermission("tabs") || this.hasActiveTabPermission(tab);
   },
 
   convert(tab) {
     let window = tab.ownerDocument.defaultView;
-    let windowActive = window == WindowManager.topWindow;
 
     let result = {
       id: TabManager.getId(tab),
       index: tab._tPos,
       windowId: WindowManager.getId(window),
       selected: tab.selected,
       highlighted: tab.selected,
       active: tab.selected,
@@ -406,26 +407,28 @@ global.TabManager = {
   },
 };
 
 // WeakMap[Extension -> ExtensionTabManager]
 let tabManagers = new WeakMap();
 
 // Returns the extension-specific tab manager for the given extension, or
 // creates one if it doesn't already exist.
-TabManager.for = function (extension) {
+TabManager.for = function(extension) {
   if (!tabManagers.has(extension)) {
     tabManagers.set(extension, new ExtensionTabManager(extension));
   }
   return tabManagers.get(extension);
 };
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("shutdown", (type, extension) => {
   tabManagers.delete(extension);
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 // Manages mapping between XUL windows and extension window IDs.
 global.WindowManager = {
   _windows: new WeakMap(),
   _nextId: 0,
 
   WINDOW_ID_NONE: -1,
   WINDOW_ID_CURRENT: -2,
@@ -484,17 +487,17 @@ global.WindowManager = {
 // considered open when the "load" event fires on it. A window is
 // closed when a "domwindowclosed" notification fires for it.
 global.WindowListManager = {
   _openListeners: new Set(),
   _closeListeners: new Set(),
 
   // Returns an iterator for all browser windows. Unless |includeIncomplete| is
   // true, only fully-loaded windows are returned.
-  *browserWindows(includeIncomplete = false) {
+  * browserWindows(includeIncomplete = false) {
     // The window type parameter is only available once the window's document
     // element has been created. This means that, when looking for incomplete
     // browser windows, we need to ignore the type entirely for windows which
     // haven't finished loading, since we would otherwise skip browser windows
     // in their early loading stages.
     // This is particularly important given that the "domwindowcreated" event
     // fires for browser windows when they're in that in-between state, and just
     // before we register our own "domwindowcreated" listener.
@@ -649,20 +652,19 @@ global.AllWindowEvents = {
     }
   },
 };
 
 AllWindowEvents.openListener = AllWindowEvents.openListener.bind(AllWindowEvents);
 
 // Subclass of EventManager where we just need to call
 // add/removeEventListener on each XUL window.
-global.WindowEventManager = function(context, name, event, listener)
-{
+global.WindowEventManager = function(context, name, event, listener) {
   EventManager.call(this, context, name, fire => {
     let listener2 = (...args) => listener(fire, ...args);
     AllWindowEvents.addListener(event, listener2);
     return () => {
       AllWindowEvents.removeListener(event, listener2);
-    }
+    };
   });
-}
+};
 
 WindowEventManager.prototype = Object.create(EventManager.prototype);
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -1,12 +1,18 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
+/* globals aboutNewTabService */
+
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
 extensions.registerAPI((extension, context) => {
   return {
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/.eslintrc
@@ -0,0 +1,43 @@
+{
+  "extends": "../../.eslintrc",
+
+  "globals": {
+    // DOM window globals
+    "CustomEvent": false,
+    "document": false,
+    "ImageData": false,
+    "MouseEvent": false,
+    "window": false,
+    "XMLHttpRequest": false,
+
+    "gBrowser": false,
+
+    "sendAsyncMessage": false,
+
+    "NetUtil": true,
+    "XPCOMUtils": true,
+    "Task": true,
+
+    "browser": false,
+
+    // Test harness globals
+    "add_task": false,
+    "BrowserTestUtils": false,
+    "ContentTask": false,
+    "EventUtils": false,
+    "ExtensionTestUtils": false,
+    "info": false,
+    "is": false,
+    "ok": false,
+    "registerCleanupFunction": false,
+    "SimpleTest": false,
+    "SpecialPowers": false,
+    "waitForFocus": false,
+
+    "clickBrowserAction": true,
+    "clickPageAction": true,
+    "CustomizableUI": true,
+    "focusWindow": true,
+    "makeWidgetId": true,
+  }
+}
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
@@ -1,26 +1,25 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(function* testTabSwitchContext() {
-
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {
         "default_icon": "default.png",
         "default_popup": "default.html",
         "default_title": "Default Title",
       },
       "permissions": ["tabs"],
     },
 
-    background: function () {
-      var details = [
+    background: function() {
+      let details = [
         { "icon": browser.runtime.getURL("default.png"),
           "popup": browser.runtime.getURL("default.html"),
           "title": "Default Title",
           "badge": "",
           "badgeBackgroundColor": null },
         { "icon": browser.runtime.getURL("1.png"),
           "popup": browser.runtime.getURL("default.html"),
           "title": "Default Title",
@@ -46,19 +45,20 @@ add_task(function* testTabSwitchContext(
           "disabled": false },
         { "icon": browser.runtime.getURL("default-2.png"),
           "popup": browser.runtime.getURL("default-2.html"),
           "title": "Default Title 2",
           "badge": "d2",
           "badgeBackgroundColor": [0, 0xff, 0] },
       ];
 
-      var tabs = [];
+      let tabs = [];
 
-      var tests = [
+      let expectDefaults;
+      let tests = [
         expect => {
           browser.test.log("Initial state, expect default properties.");
           expectDefaults(details[0]).then(() => {
             expect(details[0]);
           });
         },
         expect => {
           browser.test.log("Change the icon in the current tab. Expect default properties excluding the icon.");
@@ -73,17 +73,17 @@ add_task(function* testTabSwitchContext(
             tabs.push(tab.id);
             expectDefaults(details[0]).then(() => {
               expect(details[0]);
             });
           });
         },
         expect => {
           browser.test.log("Change properties. Expect new properties.");
-          var tabId = tabs[1];
+          let tabId = tabs[1];
           browser.browserAction.setIcon({ tabId, path: "2.png" });
           browser.browserAction.setPopup({ tabId, popup: "2.html" });
           browser.browserAction.setTitle({ tabId, title: "Title 2" });
           browser.browserAction.setBadgeText({ tabId, text: "2" });
           browser.browserAction.setBadgeBackgroundColor({ tabId, color: [0xff, 0, 0] });
           browser.browserAction.disable(tabId);
 
           expectDefaults(details[0]).then(() => {
@@ -186,24 +186,24 @@ add_task(function* testTabSwitchContext(
                                 "expected value from getBadge");
 
           browser.test.assertEq(String(expecting.badgeBackgroundColor),
                                 String(details.badgeBackgroundColor),
                                 "expected value from getBadgeBackgroundColor");
         });
       }
 
-      function expectDefaults(expecting) {
+      expectDefaults = expecting => {
         return checkDetails(expecting);
-      }
+      };
 
       // Runs the next test in the `tests` array, checks the results,
       // and passes control back to the outer test scope.
       function nextTest() {
-        var test = tests.shift();
+        let test = tests.shift();
 
         test(expecting => {
           // Check that the API returns the expected values, and then
           // run the next test.
           new Promise(resolve => {
             return browser.tabs.query({ active: true, currentWindow: true }, resolve);
           }).then(tabs => {
             return checkDetails(expecting, tabs[0].id);
@@ -242,35 +242,35 @@ add_task(function* testTabSwitchContext(
     is(button.getAttribute("tooltiptext"), details.title, "image title is correct");
     is(button.getAttribute("label"), details.title, "image label is correct");
     is(button.getAttribute("aria-label"), details.title, "image aria-label is correct");
     is(button.getAttribute("badge"), details.badge, "badge text is correct");
     is(button.getAttribute("disabled") == "true", Boolean(details.disabled), "disabled state is correct");
 
     if (details.badge && details.badgeBackgroundColor) {
       let badge = button.ownerDocument.getAnonymousElementByAttribute(
-        button, 'class', 'toolbarbutton-badge');
+        button, "class", "toolbarbutton-badge");
 
       let badgeColor = window.getComputedStyle(badge).backgroundColor;
       let color = details.badgeBackgroundColor;
-      let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`
+      let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
 
       is(badgeColor, expectedColor, "badge color is correct");
     }
 
 
     // TODO: Popup URL.
   }
 
   let awaitFinish = new Promise(resolve => {
     extension.onMessage("nextTest", (expecting, testsRemaining) => {
       checkDetails(expecting);
 
       if (testsRemaining) {
-        extension.sendMessage("runNextTest")
+        extension.sendMessage("runNextTest");
       } else {
         resolve();
       }
     });
   });
 
   yield extension.startup();
 
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
@@ -1,21 +1,20 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(function* testDisabled() {
-
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "browser_action": {}
+      "browser_action": {},
     },
 
-    background: function () {
-      var clicked = false;
+    background: function() {
+      let clicked = false;
 
       browser.browserAction.onClicked.addListener(() => {
         browser.test.log("Got click event");
         clicked = true;
       });
 
       browser.test.onMessage.addListener((msg, expectClick) => {
         if (msg == "enable") {
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -3,37 +3,38 @@
 "use strict";
 
 // Test that various combinations of icon details specs, for both paths
 // and ImageData objects, result in the correct image being displayed in
 // all display resolutions.
 add_task(function* testDetailsObjects() {
   function background() {
     function getImageData(color) {
-      var canvas = document.createElement("canvas");
+      let canvas = document.createElement("canvas");
       canvas.width = 2;
       canvas.height = 2;
-      var canvasContext = canvas.getContext("2d");
+      let canvasContext = canvas.getContext("2d");
 
       canvasContext.clearRect(0, 0, canvas.width, canvas.height);
       canvasContext.fillStyle = color;
       canvasContext.fillRect(0, 0, 1, 1);
 
       return {
         url: canvas.toDataURL("image/png"),
         imageData: canvasContext.getImageData(0, 0, canvas.width, canvas.height),
       };
     }
 
-    var imageData = {
+    let imageData = {
       red: getImageData("red"),
       green: getImageData("green"),
     };
 
-    var iconDetails = [
+    /* eslint-disable comma-dangle, indent */
+    let iconDetails = [
       // Only paths.
       { details: { "path": "a.png" },
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
           "2": browser.runtime.getURL("data/a.png"), } },
       { details: { "path": "/a.png" },
         resolutions: {
           "1": browser.runtime.getURL("a.png"),
@@ -124,28 +125,28 @@ add_task(function* testDetailsObjects() 
         resolutions: {
           "1": browser.runtime.getURL("data/18.png"),
           "2": browser.runtime.getURL("data/48.png"), } },
     ];
 
     // Allow serializing ImageData objects for logging.
     ImageData.prototype.toJSON = () => "<ImageData>";
 
-    var tabId;
+    let tabId;
 
     browser.test.onMessage.addListener((msg, test) => {
       if (msg != "setIcon") {
         browser.test.fail("expecting 'setIcon' message");
       }
 
-      var details = iconDetails[test.index];
-      var expectedURL = details.resolutions[test.resolution];
+      let details = iconDetails[test.index];
+      let expectedURL = details.resolutions[test.resolution];
 
-      var detailString = JSON.stringify(details);
-      browser.test.log(`Setting browerAction/pageAction to ${detailString} expecting URL ${expectedURL}`)
+      let detailString = JSON.stringify(details);
+      browser.test.log(`Setting browerAction/pageAction to ${detailString} expecting URL ${expectedURL}`);
 
       browser.browserAction.setIcon(Object.assign({tabId}, details.details));
       browser.pageAction.setIcon(Object.assign({tabId}, details.details));
 
       browser.test.sendMessage("imageURL", expectedURL);
     });
 
     // Generate a list of tests and resolutions to send back to the test
@@ -153,19 +154,19 @@ add_task(function* testDetailsObjects() 
     //
     // This process is a bit convoluted, because the outer test context needs
     // to handle checking the button nodes and changing the screen resolution,
     // but it can't pass us icon definitions with ImageData objects. This
     // shouldn't be a problem, since structured clones should handle ImageData
     // objects without issue. Unfortunately, |cloneInto| implements a slightly
     // different algorithm than we use in web APIs, and does not handle them
     // correctly.
-    var tests = [];
-    for (var [idx, icon] of iconDetails.entries()) {
-      for (var res of Object.keys(icon.resolutions)) {
+    let tests = [];
+    for (let [idx, icon] of iconDetails.entries()) {
+      for (let res of Object.keys(icon.resolutions)) {
         tests.push({ index: idx, resolution: Number(res) });
       }
     }
 
     // Sort by resolution, so we don't needlessly switch back and forth
     // between each test.
     tests.sort(test => test.resolution);
 
@@ -216,45 +217,44 @@ add_task(function* testDetailsObjects() 
     let pageActionImage = document.getElementById(pageActionId);
     is(pageActionImage.src, imageURL, "page action has the correct image");
   }
 
   yield extension.unload();
 });
 
 // Test that an error is thrown when providing invalid icon sizes
-add_task(function *testInvalidIconSizes() {
-
+add_task(function* testInvalidIconSizes() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {},
       "page_action": {},
     },
 
-    background: function () {
+    background: function() {
       browser.tabs.query({ active: true, currentWindow: true }, tabs => {
-        var tabId = tabs[0].id;
+        let tabId = tabs[0].id;
 
-        for (var api of ["pageAction", "browserAction"]) {
+        for (let api of ["pageAction", "browserAction"]) {
           // helper function to run setIcon and check if it fails
           let assertSetIconThrows = function(detail, error, message) {
             try {
               detail.tabId = tabId;
               browser[api].setIcon(detail);
 
               browser.test.fail("Expected an error on invalid icon size.");
               browser.test.notifyFail("setIcon with invalid icon size");
               return;
             } catch (e) {
               browser.test.succeed("setIcon with invalid icon size");
             }
-          }
+          };
 
           // test invalid icon size inputs
-          for (var type of ["path", "imageData"]) {
+          for (let type of ["path", "imageData"]) {
             assertSetIconThrows({ [type]: { "abcdef": "test.png" } });
             assertSetIconThrows({ [type]: { "48px": "test.png" } });
             assertSetIconThrows({ [type]: { "20.5": "test.png" } });
             assertSetIconThrows({ [type]: { "5.0": "test.png" } });
             assertSetIconThrows({ [type]: { "-300": "test.png" } });
             assertSetIconThrows({ [type]: {
               "abc": "test.png",
               "5": "test.png"
@@ -273,17 +273,17 @@ add_task(function *testInvalidIconSizes(
   yield Promise.all([extension.startup(), extension.awaitFinish("setIcon with invalid icon size")]);
 
   yield extension.unload();
 });
 
 
 // Test that default icon details in the manifest.json file are handled
 // correctly.
-add_task(function *testDefaultDetails() {
+add_task(function* testDefaultDetails() {
   // TODO: Test localized variants.
   let icons = [
     "foo/bar.png",
     "/foo/bar.png",
     { "19": "foo/bar.png" },
     { "38": "foo/bar.png" },
     { "19": "foo/bar.png", "38": "baz/quux.png" },
   ];
@@ -292,19 +292,19 @@ add_task(function *testDefaultDetails() 
 
   for (let icon of icons) {
     let extension = ExtensionTestUtils.loadExtension({
       manifest: {
         "browser_action": { "default_icon": icon },
         "page_action": { "default_icon": icon },
       },
 
-      background: function () {
+      background: function() {
         browser.tabs.query({ active: true, currentWindow: true }, tabs => {
-          var tabId = tabs[0].id;
+          let tabId = tabs[0].id;
 
           browser.pageAction.show(tabId);
           browser.test.sendMessage("ready");
         });
       }
     });
 
     yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
@@ -327,34 +327,33 @@ add_task(function *testDefaultDetails() 
     let node = document.getElementById(pageActionId);
     is(node, undefined, "pageAction image removed from document");
   }
 });
 
 
 // Check that attempts to load a privileged URL as an icon image fail.
 add_task(function* testSecureURLsDenied() {
-
   // Test URLs passed to setIcon.
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {},
       "page_action": {},
     },
 
-    background: function () {
+    background: function() {
       browser.tabs.query({ active: true, currentWindow: true }, tabs => {
-        var tabId = tabs[0].id;
+        let tabId = tabs[0].id;
 
-        var urls = ["chrome://browser/content/browser.xul",
+        let urls = ["chrome://browser/content/browser.xul",
                     "javascript:true"];
 
-        for (var url of urls) {
-          for (var api of ["pageAction", "browserAction"]) {
+        for (let url of urls) {
+          for (let api of ["pageAction", "browserAction"]) {
             try {
               browser[api].setIcon({tabId, path: url});
 
               browser.test.fail(`Load of '${url}' succeeded. Expected failure.`);
               browser.test.notifyFail("setIcon security tests");
               return;
             } catch (e) {
               // We can't actually inspect the error here, since the
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -15,38 +15,39 @@ function promisePopupShown(popup) {
     }
   });
 }
 
 add_task(function* testPageActionPopup() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "background": {
-        "page": "data/background.html"
+        "page": "data/background.html",
       },
       "browser_action": {
-        "default_popup": "popup-a.html"
-      }
+        "default_popup": "popup-a.html",
+      },
     },
 
     files: {
       "popup-a.html": `<script src="popup-a.js"></script>`,
       "popup-a.js": function() {
         browser.runtime.sendMessage("from-popup-a");
       },
 
       "data/popup-b.html": `<script src="popup-b.js"></script>`,
       "data/popup-b.js": function() {
         browser.runtime.sendMessage("from-popup-b");
       },
 
       "data/background.html": `<script src="background.js"></script>`,
 
       "data/background.js": function() {
-        var tests = [
+        let sendClick;
+        let tests = [
           () => {
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
           () => {
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
           () => {
             browser.browserAction.setPopup({ popup: "popup-b.html" });
@@ -63,21 +64,21 @@ add_task(function* testPageActionPopup()
             sendClick({ expectEvent: true, expectPopup: null });
           },
           () => {
             browser.browserAction.setPopup({ popup: "/popup-a.html" });
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
         ];
 
-        var expect = {};
-        function sendClick({ expectEvent, expectPopup }) {
+        let expect = {};
+        sendClick = ({ expectEvent, expectPopup }) => {
           expect = { event: expectEvent, popup: expectPopup };
           browser.test.sendMessage("send-click");
-        }
+        };
 
         browser.runtime.onMessage.addListener(msg => {
           if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
             browser.test.fail("unexpected popup");
           }
@@ -98,17 +99,17 @@ add_task(function* testPageActionPopup()
         });
 
         browser.test.onMessage.addListener((msg) => {
           if (msg != "next-test") {
             browser.test.fail("Expecting 'next-test' message");
           }
 
           if (tests.length) {
-            var test = tests.shift();
+            let test = tests.shift();
             test();
           } else {
             browser.test.notifyPass("browseraction-tests-done");
           }
         });
 
         browser.test.sendMessage("next-test");
       },
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
@@ -1,27 +1,31 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {
-        "default_popup": "popup.html"
-      }
+        "default_popup": "popup.html",
+      },
     },
 
     files: {
       "popup.html": `
       <!DOCTYPE html>
       <html><body>
       <script src="popup.js"></script>
       </body></html>
       `,
 
       "popup.js": function() {
         browser.runtime.sendMessage("from-popup");
-      }
+      },
     },
 
     background: function() {
       browser.runtime.onMessage.addListener(msg => {
         browser.test.assertEq(msg, "from-popup", "correct message received");
         browser.test.sendMessage("popup");
       });
     },
@@ -31,17 +35,17 @@ add_task(function* () {
 
   let widgetId = makeWidgetId(extension.id) + "-browser-action";
   let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
 
   // Do this a few times to make sure the pop-up is reloaded each time.
   for (let i = 0; i < 3; i++) {
     let evt = new CustomEvent("command", {
       bubbles: true,
-      cancelable: true
+      cancelable: true,
     });
     node.dispatchEvent(evt);
 
     yield extension.awaitMessage("popup");
 
     let panel = node.querySelector("panel");
     if (panel) {
       panel.hidePopup();
--- a/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
+++ b/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
@@ -1,58 +1,62 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["http://mochi.test/"]
+      "permissions": ["http://mochi.test/"],
     },
 
     background: function() {
-      var ports_received = 0;
-      var port_messages_received = 0;
+      let ports_received = 0;
+      let port_messages_received = 0;
 
       browser.runtime.onConnect.addListener((port) => {
         browser.test.assertTrue(!!port, "port1 received");
 
         ports_received++;
         browser.test.assertEq(1, ports_received, "1 port received");
 
         port.onMessage.addListener((msg, sender) => {
           browser.test.assertEq("port message", msg, "listener1 port message received");
 
-          port_messages_received++
+          port_messages_received++;
           browser.test.assertEq(1, port_messages_received, "1 port message received");
-        })
+        });
       });
       browser.runtime.onConnect.addListener((port) => {
         browser.test.assertTrue(!!port, "port2 received");
 
         ports_received++;
         browser.test.assertEq(2, ports_received, "2 ports received");
 
         port.onMessage.addListener((msg, sender) => {
           browser.test.assertEq("port message", msg, "listener2 port message received");
 
-          port_messages_received++
+          port_messages_received++;
           browser.test.assertEq(2, port_messages_received, "2 port messages received");
 
           browser.test.notifyPass("contentscript_connect.pass");
         });
       });
 
       browser.tabs.executeScript({ file: "script.js" });
     },
 
     files: {
       "script.js": function() {
-        var port = browser.runtime.connect();
+        let port = browser.runtime.connect();
         port.postMessage("port message");
-      }
-    }
+      },
+    },
   });
 
   yield extension.startup();
   yield extension.awaitFinish("contentscript_connect.pass");
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -1,48 +1,55 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* globals content */
+/* eslint-disable mozilla/no-cpows-in-tests */
+
 add_task(function* () {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
     "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
 
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["contextMenus"]
+      "permissions": ["contextMenus"],
     },
 
     background: function() {
       // A generic onclick callback function.
       function genericOnClick(info) {
         browser.test.sendMessage("menuItemClick", JSON.stringify(info));
       }
 
       browser.contextMenus.create({ "contexts": ["all"], "type": "separator" });
 
-      var contexts = ["page", "selection", "image"];
-      for (var i = 0; i < contexts.length; i++) {
-        var context = contexts[i];
-        var title = context;
-        var id = browser.contextMenus.create({ "title": title, "contexts": [context], "id": "ext-" + context,
-                                               "onclick": genericOnClick });
+      let contexts = ["page", "selection", "image"];
+      for (let i = 0; i < contexts.length; i++) {
+        let context = contexts[i];
+        let title = context;
+        browser.contextMenus.create({ "title": title, "contexts": [context], "id": "ext-" + context,
+                                      "onclick": genericOnClick });
         if (context == "selection") {
           browser.contextMenus.update("ext-selection", { "title": "selection-edited" });
         }
       }
 
-      var parent = browser.contextMenus.create({ "title": "parent" });
-      var child1 = browser.contextMenus.create(
+      let parent = browser.contextMenus.create({ "title": "parent" });
+      browser.contextMenus.create(
         { "title": "child1", "parentId": parent, "onclick": genericOnClick });
-      var child2 = browser.contextMenus.create(
+      browser.contextMenus.create(
         { "title": "child2", "parentId": parent, "onclick": genericOnClick });
 
-      var parentToDel = browser.contextMenus.create({ "title": "parentToDel" });
-      var child1ToDel = browser.contextMenus.create(
+      let parentToDel = browser.contextMenus.create({ "title": "parentToDel" });
+      browser.contextMenus.create(
         { "title": "child1", "parentId": parentToDel, "onclick": genericOnClick });
-      var child2ToDel = browser.contextMenus.create(
+      browser.contextMenus.create(
         { "title": "child2", "parentId": parentToDel, "onclick": genericOnClick });
       browser.contextMenus.remove(parentToDel);
 
       browser.test.notifyPass();
     },
   });
 
   let expectedClickInfo;
@@ -79,17 +86,17 @@ add_task(function* () {
   is(items.length, 0, "contextMenu item for selection was not found (context=image)");
 
   items = top.getElementsByAttribute("label", "parentToDel");
   is(items.length, 0, "contextMenu item for removed parent was not found (context=image)");
 
   items = top.getElementsByAttribute("label", "parent");
   is(items.length, 1, "contextMenu item for parent was found (context=image)");
 
-  is(items.item(0).childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)")
+  is(items.item(0).childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
 
   // Click on ext-image item and check the results
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
   expectedClickInfo = {
     menuItemId: "ext-image",
     mediaType: "image",
     srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
     pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
--- a/browser/components/extensions/test/browser/browser_ext_currentWindow.js
+++ b/browser/components/extensions/test/browser/browser_ext_currentWindow.js
@@ -1,42 +1,45 @@
-function genericChecker()
-{
-  var kind = "background";
-  var path = window.location.pathname;
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+function genericChecker() {
+  let kind = "background";
+  let path = window.location.pathname;
   if (path.indexOf("popup") != -1) {
     kind = "popup";
   } else if (path.indexOf("page") != -1) {
     kind = "page";
   }
 
   browser.test.onMessage.addListener((msg, ...args) => {
     if (msg == kind + "-check-current1") {
       browser.tabs.query({
-        currentWindow: true
+        currentWindow: true,
       }, function(tabs) {
         browser.test.sendMessage("result", tabs[0].windowId);
       });
     } else if (msg == kind + "-check-current2") {
       browser.tabs.query({
-        windowId: browser.windows.WINDOW_ID_CURRENT
+        windowId: browser.windows.WINDOW_ID_CURRENT,
       }, function(tabs) {
         browser.test.sendMessage("result", tabs[0].windowId);
       });
     } else if (msg == kind + "-check-current3") {
       browser.windows.getCurrent(function(window) {
         browser.test.sendMessage("result", window.id);
       });
     } else if (msg == kind + "-open-page") {
-      browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("page.html")});
+      browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("page.html")});
     } else if (msg == kind + "-close-page") {
       browser.tabs.query({
         windowId: args[0],
       }, tabs => {
-        var tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
+        let tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
         browser.tabs.remove(tab.id, () => {
           browser.test.sendMessage("closed");
         });
       });
     }
   });
   browser.test.sendMessage(kind + "-ready");
 }
@@ -53,17 +56,17 @@ add_task(function* () {
   yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
   yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
 
       "browser_action": {
-        "default_popup": "popup.html"
+        "default_popup": "popup.html",
       },
     },
 
     files: {
       "page.html": `
       <!DOCTYPE html>
       <html><body>
       <script src="page.js"></script>
@@ -82,17 +85,17 @@ add_task(function* () {
       "popup.js": genericChecker,
     },
 
     background: genericChecker,
   });
 
   yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
 
-  let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
+  let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
   let winId1 = WindowManager.getId(win1);
   let winId2 = WindowManager.getId(win2);
 
   function* checkWindow(kind, winId, name) {
     extension.sendMessage(kind + "-check-current1");
     is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 1) [${kind}]`);
     extension.sendMessage(kind + "-check-current2");
@@ -107,17 +110,17 @@ add_task(function* () {
   yield checkWindow("background", winId2, "win2");
 
   function* triggerPopup(win, callback) {
     let widgetId = makeWidgetId(extension.id) + "-browser-action";
     let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
 
     let evt = new CustomEvent("command", {
       bubbles: true,
-      cancelable: true
+      cancelable: true,
     });
     node.dispatchEvent(evt);
 
     yield extension.awaitMessage("popup-ready");
 
     yield callback();
 
     let panel = node.querySelector("panel");
--- a/browser/components/extensions/test/browser/browser_ext_getViews.js
+++ b/browser/components/extensions/test/browser/browser_ext_getViews.js
@@ -1,44 +1,47 @@
-function genericChecker()
-{
-  var kind = "background";
-  var path = window.location.pathname;
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+function genericChecker() {
+  let kind = "background";
+  let path = window.location.pathname;
   if (path.indexOf("popup") != -1) {
     kind = "popup";
   } else if (path.indexOf("tab") != -1) {
     kind = "tab";
   }
   window.kind = kind;
 
   browser.test.onMessage.addListener((msg, ...args) => {
     if (msg == kind + "-check-views") {
-      var views = browser.extension.getViews();
-      var counts = {
+      let views = browser.extension.getViews();
+      let counts = {
         "background": 0,
         "tab": 0,
-        "popup": 0
+        "popup": 0,
       };
-      for (var i = 0; i < views.length; i++) {
-        var view = views[i];
+      for (let i = 0; i < views.length; i++) {
+        let view = views[i];
         browser.test.assertTrue(view.kind in counts, "view type is valid");
         counts[view.kind]++;
         if (view.kind == "background") {
           browser.test.assertTrue(view === browser.extension.getBackgroundPage(),
                                   "background page is correct");
         }
       }
       browser.test.sendMessage("counts", counts);
     } else if (msg == kind + "-open-tab") {
-      browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("tab.html")});
+      browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("tab.html")});
     } else if (msg == kind + "-close-tab") {
       browser.tabs.query({
         windowId: args[0],
       }, tabs => {
-        var tab = tabs.find(tab => tab.url.indexOf("tab.html") != -1);
+        let tab = tabs.find(tab => tab.url.indexOf("tab.html") != -1);
         browser.tabs.remove(tab.id, () => {
           browser.test.sendMessage("closed");
         });
       });
     }
   });
   browser.test.sendMessage(kind + "-ready");
 }
@@ -47,17 +50,17 @@ add_task(function* () {
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
 
       "browser_action": {
-        "default_popup": "popup.html"
+        "default_popup": "popup.html",
       },
     },
 
     files: {
       "tab.html": `
       <!DOCTYPE html>
       <html><body>
       <script src="tab.js"></script>
@@ -78,17 +81,17 @@ add_task(function* () {
 
     background: genericChecker,
   });
 
   yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
 
   info("started");
 
-  let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
+  let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
   let winId1 = WindowManager.getId(win1);
   let winId2 = WindowManager.getId(win2);
 
   function* openTab(winId) {
     extension.sendMessage("background-open-tab", winId);
     yield extension.awaitMessage("tab-ready");
   }
@@ -113,17 +116,17 @@ add_task(function* () {
   yield checkViews("background", 2, 0);
 
   function* triggerPopup(win, callback) {
     let widgetId = makeWidgetId(extension.id) + "-browser-action";
     let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
 
     let evt = new CustomEvent("command", {
       bubbles: true,
-      cancelable: true
+      cancelable: true,
     });
     node.dispatchEvent(evt);
 
     yield extension.awaitMessage("popup-ready");
 
     yield callback();
 
     let panel = node.querySelector("panel");
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
@@ -1,40 +1,39 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(function* testTabSwitchContext() {
-
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "page_action": {
         "default_icon": "default.png",
         "default_popup": "default.html",
         "default_title": "Default Title",
       },
       "permissions": ["tabs"],
     },
 
-    background: function () {
-      var details = [
+    background: function() {
+      let details = [
         { "icon": browser.runtime.getURL("default.png"),
           "popup": browser.runtime.getURL("default.html"),
           "title": "Default Title" },
         { "icon": browser.runtime.getURL("1.png"),
           "popup": browser.runtime.getURL("default.html"),
           "title": "Default Title" },
         { "icon": browser.runtime.getURL("2.png"),
           "popup": browser.runtime.getURL("2.html"),
           "title": "Title 2" },
       ];
 
-      var tabs;
-      var tests;
-      var allTests = [
+      let tabs;
+      let tests;
+      let allTests = [
         expect => {
           browser.test.log("Initial state. No icon visible.");
           expect(null);
         },
         expect => {
           browser.test.log("Show the icon on the first tab, expect default properties.");
           browser.pageAction.show(tabs[0]);
           expect(details[0]);
@@ -48,17 +47,17 @@ add_task(function* testTabSwitchContext(
           browser.test.log("Create a new tab. No icon visible.");
           browser.tabs.create({ active: true, url: "about:blank?0" }, tab => {
             tabs.push(tab.id);
             expect(null);
           });
         },
         expect => {
           browser.test.log("Change properties. Expect new properties.");
-          var tabId = tabs[1];
+          let tabId = tabs[1];
           browser.pageAction.show(tabId);
           browser.pageAction.setIcon({ tabId, path: "2.png" });
           browser.pageAction.setPopup({ tabId, popup: "2.html" });
           browser.pageAction.setTitle({ tabId, title: "Title 2" });
 
           expect(details[2]);
         },
         expect => {
@@ -107,31 +106,31 @@ add_task(function* testTabSwitchContext(
       ];
 
       // Gets the current details of the page action, and returns a
       // promise that resolves to an object containing them.
       function getDetails() {
         return new Promise(resolve => {
           return browser.tabs.query({ active: true, currentWindow: true }, resolve);
         }).then(tabs => {
-          var tabId = tabs[0].id;
+          let tabId = tabs[0].id;
           return Promise.all([
             new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
-            new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))])
+            new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
         }).then(details => {
           return Promise.resolve({ title: details[0],
                                    popup: details[1] });
         });
       }
 
 
       // Runs the next test in the `tests` array, checks the results,
       // and passes control back to the outer test scope.
       function nextTest() {
-        var test = tests.shift();
+        let test = tests.shift();
 
         test(expecting => {
           function finish() {
             // Check that the actual icon has the expected values, then
             // run the next test.
             browser.test.sendMessage("nextTest", expecting, tests.length);
           }
 
@@ -148,37 +147,37 @@ add_task(function* testTabSwitchContext(
               finish();
             });
           } else {
             finish();
           }
         });
       }
 
-      browser.test.onMessage.addListener((msg) => {
-        if (msg == "runTests") {
-          runTests();
-        } else if (msg == "runNextTest") {
-          nextTest();
-        } else {
-          browser.test.fail(`Unexpected message: ${msg}`);
-        }
-      });
-
       function runTests() {
         tabs = [];
         tests = allTests.slice();
 
         browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
           tabs[0] = resultTabs[0].id;
 
           nextTest();
         });
       }
 
+      browser.test.onMessage.addListener((msg) => {
+        if (msg == "runTests") {
+          runTests();
+        } else if (msg == "runNextTest") {
+          nextTest();
+        } else {
+          browser.test.fail(`Unexpected message: ${msg}`);
+        }
+      });
+
       runTests();
     },
   });
 
   let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let currentWindow = window;
   let windows = [];
 
@@ -198,26 +197,26 @@ add_task(function* testTabSwitchContext(
 
   let testNewWindows = 1;
 
   let awaitFinish = new Promise(resolve => {
     extension.onMessage("nextTest", (expecting, testsRemaining) => {
       checkDetails(expecting);
 
       if (testsRemaining) {
-        extension.sendMessage("runNextTest")
+        extension.sendMessage("runNextTest");
       } else if (testNewWindows) {
         testNewWindows--;
 
         BrowserTestUtils.openNewBrowserWindow().then(window => {
           windows.push(window);
           currentWindow = window;
           return focusWindow(window);
         }).then(() => {
-          extension.sendMessage("runTests")
+          extension.sendMessage("runTests");
         });
       } else {
         resolve();
       }
     });
   });
 
   yield extension.startup();
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -15,40 +15,41 @@ function promisePopupShown(popup) {
     }
   });
 }
 
 add_task(function* testPageActionPopup() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "background": {
-        "page": "data/background.html"
+        "page": "data/background.html",
       },
       "page_action": {
-        "default_popup": "popup-a.html"
-      }
+        "default_popup": "popup-a.html",
+      },
     },
 
     files: {
       "popup-a.html": `<script src="popup-a.js"></script>`,
       "popup-a.js": function() {
         browser.runtime.sendMessage("from-popup-a");
       },
 
       "data/popup-b.html": `<script src="popup-b.js"></script>`,
       "data/popup-b.js": function() {
         browser.runtime.sendMessage("from-popup-b");
       },
 
       "data/background.html": `<script src="background.js"></script>`,
 
       "data/background.js": function() {
-        var tabId;
+        let tabId;
 
-        var tests = [
+        let sendClick;
+        let tests = [
           () => {
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
           () => {
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
           () => {
             browser.pageAction.setPopup({ tabId, popup: "popup-b.html" });
@@ -65,21 +66,21 @@ add_task(function* testPageActionPopup()
             sendClick({ expectEvent: true, expectPopup: null });
           },
           () => {
             browser.pageAction.setPopup({ tabId, popup: "/popup-a.html" });
             sendClick({ expectEvent: false, expectPopup: "a" });
           },
         ];
 
-        var expect = {};
-        function sendClick({ expectEvent, expectPopup }) {
+        let expect = {};
+        sendClick = ({ expectEvent, expectPopup }) => {
           expect = { event: expectEvent, popup: expectPopup };
           browser.test.sendMessage("send-click");
-        }
+        };
 
         browser.runtime.onMessage.addListener(msg => {
           if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
             browser.test.fail("unexpected popup");
           }
@@ -100,17 +101,17 @@ add_task(function* testPageActionPopup()
         });
 
         browser.test.onMessage.addListener((msg) => {
           if (msg != "next-test") {
             browser.test.fail("Expecting 'next-test' message");
           }
 
           if (tests.length) {
-            var test = tests.shift();
+            let test = tests.shift();
             test();
           } else {
             browser.test.notifyPass("pageaction-tests-done");
           }
         });
 
         browser.tabs.query({ active: true, currentWindow: true }, tabs => {
           tabId = tabs[0].id;
@@ -154,20 +155,16 @@ add_task(function* testPageActionPopup()
   let panel = document.getElementById(panelId);
   is(panel, undefined, "pageAction panel removed from document");
 });
 
 
 add_task(function* testPageActionSecurity() {
   const URL = "chrome://browser/content/browser.xul";
 
-  let matchURLForbidden = url => ({
-    message: new RegExp(`Loading extension.*Access to.*'${URL}' denied`),
-  });
-
   let messages = [/Access to restricted URI denied/,
                   /Access to restricted URI denied/];
 
   let waitForConsole = new Promise(resolve => {
     // Not necessary in browser-chrome tests, but monitorConsole gripes
     // if we don't call it.
     SimpleTest.waitForExplicitFinish();
 
@@ -175,19 +172,19 @@ add_task(function* testPageActionSecurit
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": { "default_popup": URL },
       "page_action": { "default_popup": URL },
     },
 
-    background: function () {
+    background: function() {
       browser.tabs.query({ active: true, currentWindow: true }, tabs => {
-        var tabId = tabs[0].id;
+        let tabId = tabs[0].id;
 
         browser.pageAction.show(tabId);
         browser.test.sendMessage("ready");
       });
     },
   });
 
   yield extension.startup();
--- a/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js
@@ -21,17 +21,17 @@ add_task(function* testPageActionPopup()
       </script></head></html>`,
 
       "popup-b.html": String.raw`<html><head><meta charset="utf-8"><script type="application/javascript">
         browser.test.sendMessage("from-popup-b");
       </script></head></html>`,
     },
 
     background: function() {
-      let tabId
+      let tabId;
       browser.tabs.query({ active: true, currentWindow: true }, tabs => {
         tabId = tabs[0].id;
         browser.pageAction.show(tabId);
         browser.test.sendMessage("ready");
       });
 
       browser.test.onMessage.addListener(() => {
         browser.browserAction.setPopup({ popup: "/popup-a.html" });
@@ -48,17 +48,17 @@ add_task(function* testPageActionPopup()
   function openPopup(buttonId) {
     let button = document.getElementById(buttonId);
     if (buttonId == pageActionId) {
       // TODO: I don't know why a proper synthesized event doesn't work here.
       button.dispatchEvent(new MouseEvent("click", {}));
     } else {
       EventUtils.synthesizeMouseAtCenter(button, {}, window);
     }
-  };
+  }
 
   let promiseConsoleMessage = pattern => new Promise(resolve => {
     Services.console.registerListener(function listener(msg) {
       if (pattern.test(msg.message)) {
         resolve(msg.message);
         Services.console.unregisterListener(listener);
       }
     });
--- a/browser/components/extensions/test/browser/browser_ext_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_simple.js
@@ -1,16 +1,20 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* test_simple() {
   let extensionData = {
     manifest: {
       "name": "Simple extension test",
       "version": "1.0",
       "manifest_version": 2,
-      "description": ""
-    }
+      "description": "",
+    },
   };
 
   let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   yield extension.startup();
   info("startup complete");
   yield extension.unload();
   info("extension unloaded successfully");
@@ -31,18 +35,18 @@ add_task(function* test_background() {
   }
 
   let extensionData = {
     background: "(" + backgroundScript.toString() + ")()",
     manifest: {
       "name": "Simple extension test",
       "version": "1.0",
       "manifest_version": 2,
-      "description": ""
-    }
+      "description": "",
+    },
   };
 
   let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   let [, x] = yield Promise.all([extension.startup(), extension.awaitMessage("running")]);
   is(x, 1, "got correct value from extension");
   info("startup complete");
   extension.sendMessage(10, 20);
--- a/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
+++ b/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
@@ -1,24 +1,28 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
-      var messages_received = [];
+      let messages_received = [];
 
-      var tabId;
+      let tabId;
 
       browser.runtime.onConnect.addListener((port) => {
         browser.test.assertTrue(!!port, "tab to background port received");
-        browser.test.assertEq("tab-connection-name", port.name, "port name should be defined and equal to connectInfo.name")
+        browser.test.assertEq("tab-connection-name", port.name, "port name should be defined and equal to connectInfo.name");
         browser.test.assertTrue(!!port.sender.tab, "port.sender.tab should be defined");
         browser.test.assertEq(tabId, port.sender.tab.id, "port.sender.tab.id should be equal to the expected tabId");
 
         port.onMessage.addListener((msg) => {
           messages_received.push(msg);
 
           if (messages_received.length == 1) {
             browser.test.assertEq("tab to background port message", msg, "'tab to background' port message received");
@@ -26,27 +30,26 @@ add_task(function* () {
           }
 
           if (messages_received.length == 2) {
             browser.test.assertTrue(!!msg.tabReceived, "'background to tab' reply port message received");
             browser.test.assertEq("background to tab port message", msg.tabReceived, "reply port content contains the message received");
 
             browser.test.notifyPass("tabRuntimeConnect.pass");
           }
-        })
+        });
       });
 
-      browser.tabs.create({
-        url: "tab.html"
-      }, (tab) => { tabId = tab.id });
+      browser.tabs.create({ url: "tab.html" },
+                          (tab) => { tabId = tab.id; });
     },
 
     files: {
       "tab.js": function() {
-        var port = browser.runtime.connect({ name: "tab-connection-name"});
+        let port = browser.runtime.connect({ name: "tab-connection-name"});
         port.postMessage("tab to background port message");
         port.onMessage.addListener((msg) => {
           port.postMessage({ tabReceived: msg });
         });
       },
       "tab.html": `
         <!DOCTYPE html>
         <html>
@@ -54,18 +57,18 @@ add_task(function* () {
             <title>test tab extension page</title>
             <meta charset="utf-8">
             <script src="tab.js" async></script>
           </head>
           <body>
             <h1>test tab extension page</h1>
           </body>
         </html>
-      `
-    }
+      `,
+    },
   });
 
   yield extension.startup();
   yield extension.awaitFinish("tabRuntimeConnect.pass");
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
@@ -1,57 +1,59 @@
 "use strict";
 
 // This is a pretty terrible hack, but it's the best we can do until we
 // support |executeScript| callbacks and |lastError|.
 function* testHasNoPermission(params) {
   let contentSetup = params.contentSetup || (() => Promise.resolve());
 
+  function background(contentSetup) {
+    browser.runtime.onMessage.addListener((msg, sender) => {
+      browser.test.assertEq(msg, "second script ran", "second script ran");
+      browser.test.notifyPass("executeScript");
+    });
+
+    browser.test.onMessage.addListener(msg => {
+      browser.test.assertEq(msg, "execute-script");
+
+      browser.tabs.query({ activeWindow: true }, tabs => {
+        browser.tabs.executeScript({
+          file: "script.js",
+        });
+
+        // Execute a script we know we have permissions for in the
+        // second tab, in the hopes that it will execute after the
+        // first one. This has intermittent failure written all over
+        // it, but it's just about the best we can do until we
+        // support callbacks for executeScript.
+        browser.tabs.executeScript(tabs[1].id, {
+          file: "second-script.js",
+        });
+      });
+    });
+
+    contentSetup().then(() => {
+      browser.test.sendMessage("ready");
+    });
+  }
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: params.manifest,
 
-    background: `(${function(contentSetup) {
-      browser.runtime.onMessage.addListener((msg, sender) => {
-        browser.test.assertEq(msg, "second script ran", "second script ran");
-        browser.test.notifyPass("executeScript");
-      });
-
-      browser.test.onMessage.addListener(msg => {
-        browser.test.assertEq(msg, "execute-script");
-
-        browser.tabs.query({ activeWindow: true }, tabs => {
-          browser.tabs.executeScript({
-            file: "script.js"
-          });
-
-          // Execute a script we know we have permissions for in the
-          // second tab, in the hopes that it will execute after the
-          // first one. This has intermittent failure written all over
-          // it, but it's just about the best we can do until we
-          // support callbacks for executeScript.
-          browser.tabs.executeScript(tabs[1].id, {
-            file: "second-script.js"
-          });
-        });
-      });
-
-      contentSetup().then(() => {
-        browser.test.sendMessage("ready");
-      });
-    }})(${contentSetup})`,
+    background: `(${background})(${contentSetup})`,
 
     files: {
       "script.js": function() {
         browser.runtime.sendMessage("first script ran");
       },
 
       "second-script.js": function() {
         browser.runtime.sendMessage("second script ran");
-      }
-    }
+      },
+    },
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
   if (params.setup) {
     yield params.setup(extension);
   }
@@ -63,22 +65,22 @@ function* testHasNoPermission(params) {
 }
 
 add_task(function* testBadPermissions() {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
   let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
 
   info("Test no special permissions");
   yield testHasNoPermission({
-    manifest: { "permissions": ["http://example.com/"] }
+    manifest: { "permissions": ["http://example.com/"] },
   });
 
   info("Test tabs permissions");
   yield testHasNoPermission({
-    manifest: { "permissions": ["http://example.com/", "tabs"] }
+    manifest: { "permissions": ["http://example.com/", "tabs"] },
   });
 
   info("Test active tab, browser action, no click");
   yield testHasNoPermission({
     manifest: {
       "permissions": ["http://example.com/", "activeTab"],
       "browser_action": {},
     },
@@ -92,17 +94,17 @@ add_task(function* testBadPermissions() 
     },
     contentSetup() {
       return new Promise(resolve => {
         browser.tabs.query({ active: true, currentWindow: true }, tabs => {
           browser.pageAction.show(tabs[0].id);
           resolve();
         });
       });
-    }
+    },
   });
 
   yield BrowserTestUtils.removeTab(tab2);
   yield BrowserTestUtils.removeTab(tab1);
 });
 
 // TODO: Test that |executeScript| fails if the tab has navigated to a
 // new page, and no longer matches our expected state. This involves
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
@@ -1,40 +1,44 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 function* testHasPermission(params) {
   let contentSetup = params.contentSetup || (() => Promise.resolve());
 
+  function background(contentSetup) {
+    browser.runtime.onMessage.addListener((msg, sender) => {
+      browser.test.assertEq(msg, "script ran", "script ran");
+      browser.test.notifyPass("executeScript");
+    });
+
+    browser.test.onMessage.addListener(msg => {
+      browser.test.assertEq(msg, "execute-script");
+
+      browser.tabs.executeScript({
+        file: "script.js",
+      });
+    });
+
+    contentSetup().then(() => {
+      browser.test.sendMessage("ready");
+    });
+  }
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: params.manifest,
 
-    background: `(${function(contentSetup) {
-      browser.runtime.onMessage.addListener((msg, sender) => {
-        browser.test.assertEq(msg, "script ran", "script ran");
-        browser.test.notifyPass("executeScript");
-      });
-
-      browser.test.onMessage.addListener(msg => {
-        browser.test.assertEq(msg, "execute-script");
-
-        browser.tabs.executeScript({
-          file: "script.js"
-        });
-      });
-
-      contentSetup().then(() => {
-        browser.test.sendMessage("ready");
-      });
-    }})(${contentSetup})`,
+    background: `(${background})(${contentSetup})`,
 
     files: {
       "script.js": function() {
         browser.runtime.sendMessage("script ran");
-      }
-    }
+      },
+    },
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
   if (params.setup) {
     yield params.setup(extension);
   }
@@ -45,27 +49,27 @@ function* testHasPermission(params) {
   yield extension.unload();
 }
 
 add_task(function* testGoodPermissions() {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
 
   info("Test explicit host permission");
   yield testHasPermission({
-    manifest: { "permissions": ["http://mochi.test/"] }
+    manifest: { "permissions": ["http://mochi.test/"] },
   });
 
   info("Test explicit host subdomain permission");
   yield testHasPermission({
-    manifest: { "permissions": ["http://*.mochi.test/"] }
+    manifest: { "permissions": ["http://*.mochi.test/"] },
   });
 
   info("Test explicit <all_urls> permission");
   yield testHasPermission({
-    manifest: { "permissions": ["<all_urls>"] }
+    manifest: { "permissions": ["<all_urls>"] },
   });
 
   info("Test activeTab permission with a browser action click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab"],
       "browser_action": {},
     },
--- a/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
@@ -1,8 +1,10 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(function* () {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
 
       "browser_action": { "default_popup": "popup.html" },
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
@@ -1,103 +1,107 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
 
   yield focusWindow(win1);
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
       "content_scripts": [{
         "matches": ["http://mochi.test/*/context_tabs_onUpdated_page.html"],
         "js": ["content-script.js"],
-        "run_at": "document_start"
-      },],
+        "run_at": "document_start",
+      }],
     },
 
     background: function() {
-      var pageURL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
+      let pageURL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
 
-      var expectedSequence = [
+      let expectedSequence = [
         { status: "loading" },
         { status: "loading", url: pageURL },
-        { status: "complete" }
+        { status: "complete" },
       ];
-      var collectedSequence = [];
+      let collectedSequence = [];
 
-      browser.tabs.onUpdated.addListener(function (tabId, updatedInfo) {
+      browser.tabs.onUpdated.addListener(function(tabId, updatedInfo) {
         collectedSequence.push(updatedInfo);
       });
 
-      browser.runtime.onMessage.addListener(function () {
-          if (collectedSequence.length !== expectedSequence.length) {
+      browser.runtime.onMessage.addListener(function() {
+        if (collectedSequence.length !== expectedSequence.length) {
+          browser.test.assertEq(
+            JSON.stringify(expectedSequence),
+            JSON.stringify(collectedSequence),
+            "got unexpected number of updateInfo data"
+          );
+        } else {
+          for (let i = 0; i < expectedSequence.length; i++) {
             browser.test.assertEq(
-              JSON.stringify(expectedSequence),
-              JSON.stringify(collectedSequence),
-              "got unexpected number of updateInfo data"
+              expectedSequence[i].status,
+              collectedSequence[i].status,
+              "check updatedInfo status"
             );
-          } else {
-            for (var i = 0; i < expectedSequence.length; i++) {
+            if (expectedSequence[i].url || collectedSequence[i].url) {
               browser.test.assertEq(
-                expectedSequence[i].status,
-                collectedSequence[i].status,
-                "check updatedInfo status"
+                expectedSequence[i].url,
+                collectedSequence[i].url,
+                "check updatedInfo url"
               );
-              if (expectedSequence[i].url || collectedSequence[i].url) {
-                browser.test.assertEq(
-                  expectedSequence[i].url,
-                  collectedSequence[i].url,
-                  "check updatedInfo url"
-                );
-              }
             }
           }
+        }
 
-          browser.test.notifyPass("tabs.onUpdated");
+        browser.test.notifyPass("tabs.onUpdated");
       });
 
       browser.tabs.create({ url: pageURL });
     },
     files: {
       "content-script.js": `
         window.addEventListener("message", function(evt) {
           if (evt.data == "frame-updated") {
             browser.runtime.sendMessage("load-completed");
           }
         }, true);
       `,
-    }
+    },
   });
 
   yield Promise.all([
     extension.startup(),
-    extension.awaitFinish("tabs.onUpdated")
+    extension.awaitFinish("tabs.onUpdated"),
   ]);
 
   yield extension.unload();
 
   yield BrowserTestUtils.closeWindow(win1);
 });
 
 function* do_test_update(background) {
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
 
   yield focusWindow(win1);
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: background,
   });
 
   yield Promise.all([
     yield extension.startup(),
-    yield extension.awaitFinish("finish")
+    yield extension.awaitFinish("finish"),
   ]);
 
   yield extension.unload();
 
   yield BrowserTestUtils.closeWindow(win1);
 }
 
 add_task(function* test_pinned() {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_query.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_query.js
@@ -1,26 +1,30 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
   let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
 
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query({
-        lastFocusedWindow: true
+        lastFocusedWindow: true,
       }, function(tabs) {
         browser.test.assertEq(tabs.length, 3, "should have three tabs");
 
-        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+        tabs.sort((tab1, tab2) => tab1.index - tab2.index);
 
         browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
         tabs.shift();
 
         browser.test.assertTrue(tabs[0].active, "tab 0 active");
         browser.test.assertFalse(tabs[1].active, "tab 1 inactive");
 
         browser.test.assertFalse(tabs[0].pinned, "tab 0 unpinned");
@@ -48,26 +52,26 @@ add_task(function* () {
 
   tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
   tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
   let tab3 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://test1.example.org/MochiKit/");
 
   // test simple queries
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query({
-        url: "<all_urls>"
+        url: "<all_urls>",
       }, function(tabs) {
         browser.test.assertEq(tabs.length, 3, "should have three tabs");
 
-        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+        tabs.sort((tab1, tab2) => tab1.index - tab2.index);
 
         browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
         browser.test.assertEq(tabs[1].url, "http://example.net/", "tab 1 url correct");
         browser.test.assertEq(tabs[2].url, "http://test1.example.org/MochiKit/", "tab 2 url correct");
 
         browser.test.notifyPass("tabs.query");
       });
     },
@@ -75,22 +79,22 @@ add_task(function* () {
 
   yield extension.startup();
   yield extension.awaitFinish("tabs.query");
   yield extension.unload();
 
   // match pattern
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query({
-        url: "http://*/MochiKit*"
+        url: "http://*/MochiKit*",
       }, function(tabs) {
         browser.test.assertEq(tabs.length, 1, "should have one tab");
 
         browser.test.assertEq(tabs[0].url, "http://test1.example.org/MochiKit/", "tab 0 url correct");
 
         browser.test.notifyPass("tabs.query");
       });
     },
@@ -98,26 +102,26 @@ add_task(function* () {
 
   yield extension.startup();
   yield extension.awaitFinish("tabs.query");
   yield extension.unload();
 
   // match array of patterns
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query({
-        url: ["http://*/MochiKit*", "http://*.com/*"]
+        url: ["http://*/MochiKit*", "http://*.com/*"],
       }, function(tabs) {
         browser.test.assertEq(tabs.length, 2, "should have two tabs");
 
-        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+        tabs.sort((tab1, tab2) => tab1.index - tab2.index);
 
         browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
         browser.test.assertEq(tabs[1].url, "http://test1.example.org/MochiKit/", "tab 1 url correct");
 
         browser.test.notifyPass("tabs.query");
       });
     },
   });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
@@ -1,31 +1,35 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
-      chrome.tabs.create({ url: "about:robots"}, function (tab) {
-        var exception;
+      browser.tabs.create({ url: "about:robots"}, tab => {
+        let exception;
         try {
           browser.tabs.sendMessage(tab.id, "message");
           browser.tabs.sendMessage(tab.id + 100, "message");
-        } catch(e) {
+        } catch (e) {
           exception = e;
         }
 
         browser.test.assertEq(undefined, exception, "no exception should be raised on tabs.sendMessage to unexistent tabs");
-        chrome.tabs.remove(tab.id, function() {
+        browser.tabs.remove(tab.id, function() {
           browser.test.notifyPass("tabs.sendMessage");
-        })
-      })
+        });
+      });
     },
   });
 
   yield Promise.all([
     extension.startup(),
-    extension.awaitFinish("tabs.sendMessage")
+    extension.awaitFinish("tabs.sendMessage"),
   ]);
 
   yield extension.unload();
 });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_update.js
@@ -1,26 +1,30 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
   let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
 
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["tabs"]
+      "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query({
         lastFocusedWindow: true,
       }, function(tabs) {
         browser.test.assertEq(tabs.length, 3, "should have three tabs");
 
-        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+        tabs.sort((tab1, tab2) => tab1.index - tab2.index);
 
         browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
         tabs.shift();
 
         browser.test.assertTrue(tabs[0].active, "tab 0 active");
         browser.test.assertFalse(tabs[1].active, "tab 1 inactive");
 
         browser.tabs.update(tabs[1].id, {active: true}, function() {
--- a/browser/components/extensions/test/browser/browser_ext_windows_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_update.js
@@ -1,27 +1,31 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
 add_task(function* () {
-  function promiseWaitForFocus(aWindow) {
-    return new Promise(function(aResolve, aReject) {
+  function promiseWaitForFocus(window) {
+    return new Promise(resolve => {
       waitForFocus(function() {
-        ok(Services.focus.activeWindow === aWindow, "correct window focused");
-        aResolve();
-      }, aWindow);
+        ok(Services.focus.activeWindow === window, "correct window focused");
+        resolve();
+      }, window);
     });
   }
 
   let window1 = window;
   let window2 = yield BrowserTestUtils.openNewBrowserWindow();
 
   Services.focus.activeWindow = window2;
   yield promiseWaitForFocus(window2);
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["windows"]
+      "permissions": ["windows"],
     },
 
     background: function() {
       browser.windows.getAll(undefined, function(wins) {
         browser.test.assertEq(wins.length, 2, "should have two windows");
 
         // Sort the unfocused window to the lower index.
         wins.sort(function(win1, win2) {
@@ -30,17 +34,16 @@ add_task(function* () {
           }
 
           return win1.focused ? 1 : -1;
         });
 
         browser.windows.update(wins[0].id, {focused: true}, function() {
           browser.test.sendMessage("check");
         });
-
       });
     },
   });
 
   yield Promise.all([extension.startup(), extension.awaitMessage("check")]);
 
   yield promiseWaitForFocus(window1);
 
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -1,18 +1,22 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* exported CustomizableUI makeWidgetId focusWindow clickBrowserAction clickPageAction */
+
 var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
 
-function makeWidgetId(id)
-{
+function makeWidgetId(id) {
   id = id.toLowerCase();
   return id.replace(/[^a-z0-9_-]/g, "_");
 }
 
-var focusWindow = Task.async(function* focusWindow(win)
-{
+var focusWindow = Task.async(function* focusWindow(win) {
   let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   if (fm.activeWindow == win) {
     return;
   }
 
   let promise = new Promise(resolve => {
     win.addEventListener("focus", function listener() {
       win.removeEventListener("focus", listener, true);
@@ -34,16 +38,17 @@ function clickBrowserAction(extension, w
 
 function clickPageAction(extension, win = window) {
   // This would normally be set automatically on navigation, and cleared
   // when the user types a value into the URL bar, to show and hide page
   // identity info and icons such as page action buttons.
   //
   // Unfortunately, that doesn't happen automatically in browser chrome
   // tests.
+  /* globals SetPageProxyState */
   SetPageProxyState("valid");
 
   let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let elem = win.document.getElementById(pageActionId);
 
   EventUtils.synthesizeMouseAtCenter(elem, {}, win);
   return new Promise(SimpleTest.executeSoon);
 }
copy from devtools/.eslintrc
copy to toolkit/components/extensions/.eslintrc
--- a/devtools/.eslintrc
+++ b/toolkit/components/extensions/.eslintrc
@@ -1,413 +1,499 @@
 {
+  "extends": "../../.eslintrc",
+
   "globals": {
     "Cc": true,
     "Ci": true,
     "Components": true,
-    "console": true,
     "Cr": true,
     "Cu": true,
-    "devtools": true,
     "dump": true,
-    "EventEmitter": true,
-    "exports": true,
-    "loader": true,
-    "module": true,
-    "require": true,
+    "TextDecoder": false,
+    "TextEncoder": false,
+    // Specific to WebExtensions:
+    "extensions": true,
+    "global": true,
+    "Extension": true,
+    "ExtensionManagement": true,
+    "ExtensionPage": true,
+    "GlobalManager": true,
+    "runSafe": true,
+    "runSafeSync": true,
+    "runSafeSyncWithoutClone": true,
     "Services": true,
-    "Task": true,
-    "XPCNativeWrapper": true,
+    "TabManager": true,
     "XPCOMUtils": true,
   },
+
   "rules": {
-    // These are the rules that have been configured so far to match the
-    // devtools coding style.
-
     // Rules from the mozilla plugin
     "mozilla/balanced-listeners": 2,
     "mozilla/components-imports": 1,
     "mozilla/import-headjs-globals": 1,
     "mozilla/mark-test-function-used": 1,
     "mozilla/no-aArgs": 1,
     "mozilla/no-cpows-in-tests": 1,
     "mozilla/var-only-at-top-level": 1,
 
-    // Disallow using variables outside the blocks they are defined (especially
-    // since only let and const are used, see "no-var").
-    "block-scoped-var": 2,
+    // Braces only needed for multi-line arrow function blocks
+    // "arrow-body-style": [2, "as-needed"],
+
+    // Require spacing around =>
+    "arrow-spacing": 2,
+
+    // Always require spacing around a single line block
+    "block-spacing": 1,
+
     // Enforce one true brace style (opening brace on the same line) and avoid
     // start and end braces on the same line.
-    "brace-style": [2, "1tbs", {"allowSingleLine": false}],
-    // Require camel case names
-    "camelcase": 2,
-    // Allow trailing commas for easy list extension.  Having them does not
-    // impair readability, but also not required either.
-    "comma-dangle": 0,
-    // Enforce spacing before and after comma
+    "brace-style": [2, "1tbs", { "allowSingleLine": true }],
+
+    // No space before always a space after a comma
     "comma-spacing": [2, {"before": false, "after": true}],
-    // Enforce one true comma style.
-    "comma-style": [2, "last"],
-    // Warn about cyclomatic complexity in functions.
-    "complexity": 1,
-    // Require return statements to either always or never specify values.
-    "consistent-return": 2,
-    // Don't warn for inconsistent naming when capturing this (not so important
-    // with auto-binding fat arrow functions).
-    "consistent-this": 0,
-    // Enforce curly brace conventions for all control statements.
-    "curly": 2,
-    // Don't require a default case in switch statements. Avoid being forced to
-    // add a bogus default when you know all possible cases are handled.
-    "default-case": 0,
-    // Enforce dots on the next line with property name.
-    "dot-location": [1, "property"],
-    // Encourage the use of dot notation whenever possible.
-    "dot-notation": 2,
-    // Enforce newline at the end of file, with no multiple empty lines.
+
+    // Commas at the end of the line not the start
+    "comma-style": 2,
+
+    // Don't require spaces around computed properties
+    "computed-property-spacing": [1, "never"],
+
+    // Functions are not required to consistently return something or nothing
+    "consistent-return": 0,
+
+    // Require braces around blocks that start a new line
+    "curly": [2, "multi-line"],
+
+    // Always require a trailing EOL
     "eol-last": 2,
-    // Allow using == instead of ===, in the interest of landing something since
-    // the devtools codebase is split on convention here.
-    "eqeqeq": 0,
-    // Don't require function expressions to have a name.
-    // This makes the code more verbose and hard to read. Our engine already
-    // does a fantastic job assigning a name to the function, which includes
-    // the enclosing function name, and worst case you have a line number that
-    // you can just look up.
-    "func-names": 0,
-    // Allow use of function declarations and expressions.
-    "func-style": 0,
-    // Deprecated, will be removed in 1.0.
-    "generator-star": 0,
-    // Enforce the spacing around the * in generator functions.
-    "generator-star-spacing": [1, "after"],
-    // Deprecated, will be removed in 1.0.
-    "global-strict": 0,
-    // Only useful in a node environment.
-    "handle-callback-err": 0,
-    // Tab width.
-    "indent": [2, 2, {"SwitchCase": 1}],
-    // Enforces spacing between keys and values in object literal properties.
-    "key-spacing": [1, {"beforeColon": false, "afterColon": true}],
-    // Allow mixed 'LF' and 'CRLF' as linebreaks.
-    "linebreak-style": 0,
-    // Don't enforce the maximum depth that blocks can be nested. The complexity
-    // rule is a better rule to check this.
-    "max-depth": 0,
-    // Maximum length of a line.
-    "max-len": [1, 80],
-    // Maximum depth callbacks can be nested.
-    "max-nested-callbacks": [2, 3],
-    // Don't limit the number of parameters that can be used in a function.
-    "max-params": 0,
-    // Don't limit the maximum number of statement allowed in a function. We
-    // already have the complexity rule that's a better measurement.
-    "max-statements": 0,
-    // Require a capital letter for constructors, only check if all new
-    // operators are followed by a capital letter. Don't warn when capitalized
-    // functions are used without the new operator.
-    "new-cap": [2, {"capIsNew": false}],
-    // Disallow the omission of parentheses when invoking a constructor with no
-    // arguments.
+
+    // Require function* name()
+    "generator-star-spacing": [2, {"before": false, "after": true}],
+
+    // Two space indent
+    "indent": [2, 2, { "SwitchCase": 1 }],
+
+    // Space after colon not before in property declarations
+    "key-spacing": [2, { "beforeColon": false, "afterColon": true, "mode": "minimum" }],
+
+    // Unix linebreaks
+    "linebreak-style": [2, "unix"],
+
+    // Always require parenthesis for new calls
     "new-parens": 2,
-    // Disallow use of the Array constructor.
+
+    // Use [] instead of Array()
     "no-array-constructor": 2,
-    // Allow use of bitwise operators.
-    "no-bitwise": 0,
-    // Disallow use of arguments.caller or arguments.callee.
-    "no-caller": 2,
-    // Disallow the catch clause parameter name being the same as a variable in
-    // the outer scope, to avoid confusion.
-    "no-catch-shadow": 1,
-    // Deprecated, will be removed in 1.0.
-    "no-comma-dangle": 0,
-    // Disallow assignment in conditional expressions.
-    "no-cond-assign": 2,
-    // Allow using the console API.
-    "no-console": 0,
-    // Allow using constant expressions in conditions like while (true)
-    "no-constant-condition": 0,
-    // Allow use of the continue statement.
-    "no-continue": 0,
-    // Disallow control characters in regular expressions.
-    "no-control-regex": 2,
-    // Disallow use of debugger.
-    "no-debugger": 2,
-    // Disallow deletion of variables (deleting properties is fine).
-    "no-delete-var": 2,
-    // Allow division operators explicitly at beginning of regular expression.
-    "no-div-regex": 0,
-    // Disallow duplicate arguments in functions.
+
+    // No duplicate arguments in function declarations
     "no-dupe-args": 2,
-    // Disallow duplicate keys when creating object literals.
+
+    // No duplicate keys in object declarations
     "no-dupe-keys": 2,
-    // Disallow a duplicate case label.
+
+    // No duplicate cases in switch statements
     "no-duplicate-case": 2,
-    // Disallow else after a return in an if. The else around the second return
-    // here is useless:
-    // if (something) { return false; } else { return true; }
-    "no-else-return": 2,
+
+    // No labels
+    // "no-labels": 2,
+
+    // If an if block ends with a return no need for an else block
+    // "no-else-return": 2,
+
     // Disallow empty statements. This will report an error for:
     // try { something(); } catch (e) {}
     // but will not report it for:
     // try { something(); } catch (e) { /* Silencing the error because ...*/ }
     // which is a valid use case.
     "no-empty": 2,
-    // Disallow the use of empty character classes in regular expressions.
+
+    // No empty character classes in regex
     "no-empty-character-class": 2,
-    // Disallow use of labels for anything other then loops and switches.
-    "no-empty-label": 2,
-    // Disallow use of eval(). We have other APIs to evaluate code in content.
-    "no-eval": 2,
-    // Disallow assigning to the exception in a catch block.
+
+    // Disallow empty destructuring
+    "no-empty-pattern": 2,
+
+    // No assiging to exception variable
     "no-ex-assign": 2,
-    // Disallow adding to native types
-    "no-extend-native": 2,
-    // Disallow unnecessary function binding.
-    "no-extra-bind": 2,
-    // Disallow double-negation boolean casts in a boolean context.
-    "no-extra-boolean-cast": 2,
-    // Allow unnecessary parentheses, as they may make the code more readable.
-    "no-extra-parens": 0,
-    // Disallow unnecessary semicolons.
+
+    // No using !! where casting to boolean is already happening
+    "no-extra-boolean-cast": 1,
+
+    // No double semicolon
     "no-extra-semi": 2,
-    // Deprecated, will be removed in 1.0.
-    "no-extra-strict": 0,
-    // Disallow fallthrough of case statements, except if there is a comment.
-    "no-fallthrough": 2,
-    // Allow the use of leading or trailing decimal points in numeric literals.
-    "no-floating-decimal": 0,
-    // Disallow comments inline after code.
-    "no-inline-comments": 1,
-    // Disallow if as the only statement in an else block.
-    "no-lonely-if": 2,
-    // Allow mixing regular variable and require declarations (not a node env).
-    "no-mixed-requires": 0,
-    // Disallow mixed spaces and tabs for indentation.
-    "no-mixed-spaces-and-tabs": 2,
+
+    // No overwriting defined functions
+    "no-func-assign": 2,
+
+    // No invalid regular expresions
+    "no-invalid-regexp": 2,
+
+    // No odd whitespace characters
+    "no-irregular-whitespace": 2,
+
+    // No single if block inside an else block
+    "no-lonely-if": 1,
+
+    // No mixing spaces and tabs in indent
+    "no-mixed-spaces-and-tabs": [2, "smart-tabs"],
+
     // Disallow use of multiple spaces (sometimes used to align const values,
     // array or object items, etc.). It's hard to maintain and doesn't add that
     // much benefit.
     "no-multi-spaces": 1,
+
+    // No reassigning native JS objects
+    "no-native-reassign": 2,
+
+    // No (!foo in bar)
+    "no-negated-in-lhs": 2,
+
+    // Nested ternary statements are confusing
+    "no-nested-ternary": 2,
+
+    // Use {} instead of new Object()
+    "no-new-object": 2,
+
+    // No Math() or JSON()
+    "no-obj-calls": 2,
+
+    // No octal literals
+    "no-octal": 2,
+
+    // No redeclaring variables
+    "no-redeclare": 2,
+
+    // No unnecessary comparisons
+    "no-self-compare": 2,
+
+    // No declaring variables from an outer scope
+    "no-shadow": 1,
+
+    // No declaring variables that hide things like arguments
+    "no-shadow-restricted-names": 2,
+
+    // No spaces between function name and parentheses
+    "no-spaced-func": 1,
+
+    // No trailing whitespace
+    "no-trailing-spaces": 2,
+
+    // No using undeclared variables
+    "no-undef": 2,
+
+    // Error on newline where a semicolon is needed
+    "no-unexpected-multiline": 2,
+
+    // No unreachable statements
+    "no-unreachable": 2,
+
+    // No expressions where a statement is expected
+    "no-unused-expressions": 2,
+
+    // No declaring variables that are never used
+    "no-unused-vars": [2, { "args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$" }],
+
+    // No using variables before defined
+    "no-use-before-define": 2,
+
+    // No using with
+    "no-with": 2,
+
+    // Always require semicolon at end of statement
+    "semi": [2, "always"],
+
+    // Require space after keywords
+    "space-after-keywords": 2,
+
+    // Require space before blocks
+    "space-before-blocks": 2,
+
+    // Never use spaces before function parentheses
+    "space-before-function-paren": [2, { "anonymous": "never", "named": "never" }],
+
+    // Require spaces before finally, catch, etc.
+    "space-before-keywords": [2, "always"],
+
+    // No space padding in parentheses
+    "space-in-parens": [2, "never"],
+
+    // Require spaces around operators, except for a|0.
+    "space-infix-ops": [2, {"int32Hint": true}],
+
+
+    // Require spaces after return, throw and case
+    "space-return-throw-case": 2,
+
+    // ++ and -- should not need spacing
+    "space-unary-ops": [1, { "nonwords": false }],
+
+    // No comparisons to NaN
+    "use-isnan": 2,
+
+    // Only check typeof against valid results
+    "valid-typeof": 2,
+
+    // Disallow using variables outside the blocks they are defined (especially
+    // since only let and const are used, see "no-var").
+    "block-scoped-var": 2,
+
+    // Allow trailing commas for easy list extension.  Having them does not
+    // impair readability, but also not required either.
+    "comma-dangle": [1, "always-multiline"],
+
+    // Warn about cyclomatic complexity in functions.
+    "complexity": 1,
+
+    // Don't warn for inconsistent naming when capturing this (not so important
+    // with auto-binding fat arrow functions).
+    // "consistent-this": [2, "self"],
+
+    // Don't require a default case in switch statements. Avoid being forced to
+    // add a bogus default when you know all possible cases are handled.
+    "default-case": 0,
+
+    // Enforce dots on the next line with property name.
+    "dot-location": [1, "property"],
+
+    // Encourage the use of dot notation whenever possible.
+    "dot-notation": 2,
+
+    // Allow using == instead of ===, in the interest of landing something since
+    // the devtools codebase is split on convention here.
+    "eqeqeq": 0,
+
+    // Don't require function expressions to have a name.
+    // This makes the code more verbose and hard to read. Our engine already
+    // does a fantastic job assigning a name to the function, which includes
+    // the enclosing function name, and worst case you have a line number that
+    // you can just look up.
+    "func-names": 0,
+
+    // Allow use of function declarations and expressions.
+    "func-style": 0,
+
+    // Don't enforce the maximum depth that blocks can be nested. The complexity
+    // rule is a better rule to check this.
+    "max-depth": 0,
+
+    // Maximum length of a line.
+    // Disabled because we exceed this in too many places.
+    "max-len": [0, 80],
+
+    // Maximum depth callbacks can be nested.
+    "max-nested-callbacks": [2, 4],
+
+    // Don't limit the number of parameters that can be used in a function.
+    "max-params": 0,
+
+    // Don't limit the maximum number of statement allowed in a function. We
+    // already have the complexity rule that's a better measurement.
+    "max-statements": 0,
+
+    // Don't require a capital letter for constructors, only check if all new
+    // operators are followed by a capital letter. Don't warn when capitalized
+    // functions are used without the new operator.
+    "new-cap": [0, {"capIsNew": false}],
+
+    // Allow use of bitwise operators.
+    "no-bitwise": 0,
+
+    // Disallow use of arguments.caller or arguments.callee.
+    "no-caller": 2,
+
+    // Disallow the catch clause parameter name being the same as a variable in
+    // the outer scope, to avoid confusion.
+    "no-catch-shadow": 0,
+
+    // Disallow assignment in conditional expressions.
+    "no-cond-assign": 2,
+
+    // Disallow using the console API.
+    "no-console": 2,
+
+    // Allow using constant expressions in conditions like while (true)
+    "no-constant-condition": 0,
+
+    // Allow use of the continue statement.
+    "no-continue": 0,
+
+    // Disallow control characters in regular expressions.
+    "no-control-regex": 2,
+
+    // Disallow use of debugger.
+    "no-debugger": 2,
+
+    // Disallow deletion of variables (deleting properties is fine).
+    "no-delete-var": 2,
+
+    // Allow division operators explicitly at beginning of regular expression.
+    "no-div-regex": 0,
+
+    // Disallow use of labels for anything other then loops and switches.
+    "no-empty-label": 2,
+
+    // Disallow use of eval(). We have other APIs to evaluate code in content.
+    "no-eval": 2,
+
+    // Disallow adding to native types
+    "no-extend-native": 2,
+
+    // Disallow unnecessary function binding.
+    "no-extra-bind": 2,
+
+    // Allow unnecessary parentheses, as they may make the code more readable.
+    "no-extra-parens": 0,
+
+    // Disallow fallthrough of case statements, except if there is a comment.
+    "no-fallthrough": 2,
+
+    // Allow the use of leading or trailing decimal points in numeric literals.
+    "no-floating-decimal": 0,
+
+    // Allow comments inline after code.
+    "no-inline-comments": 0,
+
     // Disallow use of multiline strings (use template strings instead).
     "no-multi-str": 1,
+
     // Disallow multiple empty lines.
-    "no-multiple-empty-lines": [1, {"max": 1}],
-    // Disallow reassignments of native objects.
-    "no-native-reassign": 2,
-    // Disallow nested ternary expressions, they make the code hard to read.
-    "no-nested-ternary": 2,
-    // Allow use of new operator with the require function.
-    "no-new-require": 0,
-    // Disallow use of octal literals.
-    "no-octal": 1,
+    "no-multiple-empty-lines": [1, {"max": 2}],
+
     // Allow reassignment of function parameters.
     "no-param-reassign": 0,
+
     // Allow string concatenation with __dirname and __filename (not a node env).
     "no-path-concat": 0,
+
     // Allow use of unary operators, ++ and --.
     "no-plusplus": 0,
+
     // Allow using process.env (not a node environment).
     "no-process-env": 0,
+
     // Allow using process.exit (not a node environment).
     "no-process-exit": 0,
+
     // Disallow usage of __proto__ property.
     "no-proto": 2,
-    // Disallow declaring the same variable more than once (we use let anyway).
-    "no-redeclare": 2,
+
     // Disallow multiple spaces in a regular expression literal.
     "no-regex-spaces": 2,
+
     // Allow reserved words being used as object literal keys.
     "no-reserved-keys": 0,
+
     // Don't restrict usage of specified node modules (not a node environment).
     "no-restricted-modules": 0,
+
     // Disallow use of assignment in return statement. It is preferable for a
     // single line of code to have only one easily predictable effect.
     "no-return-assign": 2,
-    // Allow use of javascript: urls.
-    "no-script-url": 0,
-    // Disallow comparisons where both sides are exactly the same.
-    "no-self-compare": 2,
-    // Disallow use of comma operator.
-    "no-sequences": 2,
-    // Warn about declaration of variables already declared in the outer scope.
-    // This isn't an error because it sometimes is useful to use the same name
-    // in a small helper function rather than having to come up with another
-    // random name.
-    // Still, making this a warning can help people avoid being confused.
-    "no-shadow": 1,
+
+    // Don't warn about declaration of variables already declared in the outer scope.
+    "no-shadow": 0,
+
     // Disallow shadowing of names such as arguments.
     "no-shadow-restricted-names": 2,
-    // Deprecated, will be removed in 1.0.
-    "no-space-before-semi": 0,
-    // Disallow space between function identifier and application.
-    "no-spaced-func": 1,
-    // Disallow sparse arrays, eg. let arr = [,,2].
-    // Array destructuring is fine though:
-    // for (let [, breakpointPromise] of aPromises)
-    "no-sparse-arrays": 2,
+
     // Allow use of synchronous methods (not a node environment).
     "no-sync": 0,
+
     // Allow the use of ternary operators.
     "no-ternary": 0,
+
     // Disallow throwing literals (eg. throw "error" instead of
     // throw new Error("error")).
     "no-throw-literal": 2,
-    // Disallow trailing whitespace at the end of lines.
-    "no-trailing-spaces": 2,
-    // Disallow use of undeclared variables unless mentioned in a /*global */
+
+    // Disallow use of undeclared variables unless mentioned in a /* global */
     // block. Note that globals from head.js are automatically imported in tests
     // by the import-headjs-globals rule form the mozilla eslint plugin.
     "no-undef": 2,
+
     // Allow dangling underscores in identifiers (for privates).
     "no-underscore-dangle": 0,
+
     // Allow use of undefined variable.
     "no-undefined": 0,
+
     // Disallow the use of Boolean literals in conditional expressions.
     "no-unneeded-ternary": 2,
-    // Disallow unreachable statements after a return, throw, continue, or break
-    // statement.
-    "no-unreachable": 2,
-    // Disallow declaration of variables that are not used in the code
-    "no-unused-vars": 2,
-    // Allow using variables before they are defined.
-    "no-use-before-define": 0,
+
     // We use var-only-at-top-level instead of no-var as we allow top level
     // vars.
     "no-var": 0,
+
     // Allow using TODO/FIXME comments.
     "no-warning-comments": 0,
-    // Disallow use of the with statement.
-    "no-with": 2,
+
     // Don't require method and property shorthand syntax for object literals.
     // We use this in the code a lot, but not consistently, and this seems more
     // like something to check at code review time.
     "object-shorthand": 0,
+
     // Allow more than one variable declaration per function.
     "one-var": 0,
+
     // Disallow padding within blocks.
     "padded-blocks": [1, "never"],
+
     // Don't require quotes around object literal property names.
     "quote-props": 0,
+
     // Double quotes should be used.
     "quotes": [1, "double", "avoid-escape"],
+
     // Require use of the second argument for parseInt().
     "radix": 2,
-    // Always require use of semicolons wherever they are valid.
-    "semi": [1, "always"],
+
     // Enforce spacing after semicolons.
-    "semi-spacing": [1, {"before": false, "after": true}],
+    "semi-spacing": [2, {"before": false, "after": true}],
+
     // Don't require to sort variables within the same declaration block.
     // Anyway, one-var is disabled.
     "sort-vars": 0,
-    // Deprecated, will be removed in 1.0.
-    "space-after-function-name": 0,
+
     // Require a space after keywords.
     "space-after-keywords": [1, "always"],
-    // Deprecated, will be removed in 1.0.
-    "space-after-function-name": 0,
-    // Require a space before the start brace of a block.
-    "space-before-blocks": [1, "always"],
-    // Deprecated, will be removed in 1.0.
-    "space-before-function-parentheses": 0,
-    // Disallow space before function opening parenthesis.
-    "space-before-function-paren": [1, "never"],
-    // Disable the rule that checks if spaces inside {} and [] are there or not.
-    // Our code is split on conventions, and it'd be nice to have 2 rules
-    // instead, one for [] and one for {}. So, disabling until we write them.
-    "space-in-brackets": 0,
-    // Disallow spaces inside parentheses.
-    "space-in-parens": [1, "never"],
-    // Require spaces around operators, except for a|0.
-    "space-infix-ops": [1, {"int32Hint": true}],
-    // Require a space after return, throw, and case.
-    "space-return-throw-case": 1,
-    // Require spaces before/after unary operators (words on by default,
-    // nonwords off by default).
-    "space-unary-ops": [1, { "words": true, "nonwords": false }],
-    // Deprecated, will be removed in 1.0.
-    "space-unary-word-ops": 0,
+
     // Require a space immediately following the // in a line comment.
-    "spaced-comment": [1, "always"],
+    "spaced-comment": [2, "always"],
+
     // Require "use strict" to be defined globally in the script.
     "strict": [2, "global"],
-    // Disallow comparisons with the value NaN.
-    "use-isnan": 2,
+
     // Warn about invalid JSDoc comments.
-    // Disabled for now because of https://github.com/eslint/eslint/issues/2270
-    // The rule fails on some jsdoc comments like in:
-    // devtools/client/webconsole/console-output.js
     "valid-jsdoc": 0,
-    // Ensure that the results of typeof are compared against a valid string.
-    "valid-typeof": 2,
+
     // Allow vars to be declared anywhere in the scope.
     "vars-on-top": 0,
+
     // Don't require immediate function invocation to be wrapped in parentheses.
     "wrap-iife": 0,
+
     // Don't require regex literals to be wrapped in parentheses (which
     // supposedly prevent them from being mistaken for division operators).
     "wrap-regex": 0,
+
     // Disallow Yoda conditions (where literal value comes first).
     "yoda": 2,
 
-    // And these are the rules that haven't been discussed so far, and that are
-    // disabled for now until we introduce them, one at a time.
+    // disallow use of eval()-like methods
+    "no-implied-eval": 2,
+
+    // Disallow function or variable declarations in nested blocks
+    "no-inner-declarations": 2,
+
+    // Disallow usage of __iterator__ property
+    "no-iterator": 2,
 
-    // Require for-in loops to have an if statement.
-    "guard-for-in": 0,
-    // allow/disallow an empty newline after var statement
-    "newline-after-var": 0,
-    // disallow the use of alert, confirm, and prompt
-    "no-alert": 0,
-    // disallow comparisons to null without a type-checking operator
-    "no-eq-null": 0,
-    // disallow overwriting functions written as function declarations
-    "no-func-assign": 0,
-    // disallow use of eval()-like methods
-    "no-implied-eval": 0,
-    // disallow function or variable declarations in nested blocks
-    "no-inner-declarations": 0,
-    // disallow invalid regular expression strings in the RegExp constructor
-    "no-invalid-regexp": 0,
-    // disallow irregular whitespace outside of strings and comments
-    "no-irregular-whitespace": 0,
-    // disallow usage of __iterator__ property
-    "no-iterator": 0,
-    // disallow labels that share a name with a variable
-    "no-label-var": 0,
-    // disallow use of labeled statements
-    "no-labels": 0,
-    // disallow unnecessary nested blocks
-    "no-lone-blocks": 0,
-    // disallow creation of functions within loops
-    "no-loop-func": 0,
-    // disallow negation of the left operand of an in expression
-    "no-negated-in-lhs": 0,
-    // disallow use of new operator when not part of the assignment or
-    // comparison
-    "no-new": 0,
-    // disallow use of new operator for Function object
-    "no-new-func": 0,
-    // disallow use of the Object constructor
-    "no-new-object": 0,
-    // disallows creating new instances of String,Number, and Boolean
-    "no-new-wrappers": 0,
-    // disallow the use of object properties of the global object (Math and
-    // JSON) as functions
-    "no-obj-calls": 0,
-    // disallow use of octal escape sequences in string literals, such as
-    // var foo = "Copyright \251";
-    "no-octal-escape": 0,
-    // disallow use of undefined when initializing variables
-    "no-undef-init": 0,
-    // disallow usage of expressions in statement position
-    "no-unused-expressions": 0,
-    // disallow use of void operator
-    "no-void": 0,
-    // disallow wrapping of non-IIFE statements in parens
-    "no-wrap-func": 0,
-    // require assignment operator shorthand where possible or prohibit it
-    // entirely
-    "operator-assignment": 0,
-    // enforce operators to be placed before or after line breaks
-    "operator-linebreak": 0,
+    // Disallow labels that share a name with a variable
+    "no-label-var": 2,
+
+    // Disallow negation of the left operand of an in expression
+    "no-negated-in-lhs": 2,
+
+    // Disallow creating new instances of String, Number, and Boolean
+    "no-new-wrappers": 2,
   }
 }
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1,33 +1,36 @@
 /* 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";
 
 this.EXPORTED_SYMBOLS = ["Extension", "ExtensionData"];
 
+/* globals Extension ExtensionData */
+
 /*
  * This file is the main entry point for extensions. When an extension
  * loads, its bootstrap.js file creates a Extension instance
  * and calls .startup() on it. It calls .shutdown() when the extension
  * unloads. Extension manages any extension-specific state in
  * the chrome process.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://devtools/shared/event-emitter.js");
 
+XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
+                                  "resource://devtools/shared/event-emitter.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Locale",
                                   "resource://gre/modules/Locale.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
@@ -62,23 +65,24 @@ ExtensionManagement.registerScript("chro
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   LocaleData,
   MessageBroker,
   Messenger,
   injectAPI,
   extend,
   flushJarCache,
-  instanceOf,
 } = ExtensionUtils;
 
 const LOGGER_ID_BASE = "addons.webextension.";
 
 var scriptScope = this;
 
+var ExtensionPage, GlobalManager;
+
 // This object loads the ext-*.js scripts that define the extension API.
 var Management = {
   initialized: false,
   scopes: [],
   apis: [],
   emitter: new EventEmitter(),
 
   // Loads all the ext-*.js scripts currently registered.
@@ -160,17 +164,17 @@ var Management = {
   // Ask to run all the callbacks that are registered for a given hook.
   emit(hook, ...args) {
     this.lazyInit();
     this.emitter.emit(hook, ...args);
   },
 
   off(hook, callback) {
     this.emitter.off(hook, callback);
-  }
+  },
 };
 
 // A MessageBroker that's used to send and receive messages for
 // extension pages (which run in the chrome process).
 var globalBroker = new MessageBroker([Services.mm, Services.ppmm]);
 
 // An extension page is an execution context for any extension content
 // that runs in the chrome process. It's used for background pages
@@ -178,19 +182,18 @@ var globalBroker = new MessageBroker([Se
 // content loaded into browser tabs (type="tab").
 //
 // |params| is an object with the following properties:
 // |type| is one of "background", "popup", or "tab".
 // |contentWindow| is the DOM window the content runs in.
 // |uri| is the URI of the content (optional).
 // |docShell| is the docshell the content runs in (optional).
 // |incognito| is the content running in a private context (default: false).
-function ExtensionPage(extension, params)
-{
-  let {type, contentWindow, uri, docShell} = params;
+ExtensionPage = function(extension, params) {
+  let {type, contentWindow, uri} = params;
   this.extension = extension;
   this.type = type;
   this.contentWindow = contentWindow || null;
   this.uri = uri || extension.baseURI;
   this.incognito = params.incognito || false;
   this.onClose = new Set();
 
   // This is the sender property passed to the Messenger for this
@@ -201,17 +204,17 @@ function ExtensionPage(extension, params
   }
   let delegate = {};
   Management.emit("page-load", this, params, sender, delegate);
 
   let filter = {id: extension.id};
   this.messenger = new Messenger(this, globalBroker, sender, filter, delegate);
 
   this.extension.views.add(this);
-}
+};
 
 ExtensionPage.prototype = {
   get cloneScope() {
     return this.contentWindow;
   },
 
   get principal() {
     return this.contentWindow.document.nodePrincipal;
@@ -259,17 +262,17 @@ ExtensionPage.prototype = {
 
     for (let obj of this.onClose) {
       obj.close();
     }
   },
 };
 
 // Responsible for loading extension APIs into the right globals.
-var GlobalManager = {
+GlobalManager = {
   // Number of extensions currently enabled.
   count: 0,
 
   // Map[docShell -> {extension, context}] where context is an ExtensionPage.
   docShells: new Map(),
 
   // Map[extension ID -> Extension]. Determines which extension is
   // responsible for content under a particular extension ID.
@@ -360,27 +363,26 @@ var GlobalManager = {
 // Represents the data contained in an extension, contained either
 // in a directory or a zip file, which may or may not be installed.
 // This class implements the functionality of the Extension class,
 // primarily related to manifest parsing and localization, which is
 // useful prior to extension installation or initialization.
 //
 // No functionality of this class is guaranteed to work before
 // |readManifest| has been called, and completed.
-this.ExtensionData = function(rootURI)
-{
+this.ExtensionData = function(rootURI) {
   this.rootURI = rootURI;
 
   this.manifest = null;
   this.id = null;
   this.localeData = null;
   this._promiseLocales = null;
 
   this.errors = [];
-}
+};
 
 ExtensionData.prototype = {
   get logger() {
     let id = this.id || "<unknown>";
     return Log.repository.getLogger(LOGGER_ID_BASE + id);
   },
 
   // Report an error about the extension's manifest file.
@@ -401,21 +403,22 @@ ExtensionData.prototype = {
 
       let iter = new OS.File.DirectoryIterator(fullPath);
       let results = [];
 
       try {
         yield iter.forEach(entry => {
           results.push(entry);
         });
-      } catch (e) {}
+      } catch (e) {
+        // Always return a list, even if the directory does not exist (or is
+        // not a directory) for symmetry with the ZipReader behavior.
+      }
       iter.close();
 
-      // Always return a list, even if the directory does not exist (or is
-      // not a directory) for symmetry with the ZipReader behavior.
       return results;
     }
 
     if (!(this.rootURI instanceof Ci.nsIJARURI &&
           this.rootURI.JARFile instanceof Ci.nsIFileURL)) {
       // This currently happens for app:// URLs passed to us by
       // UserCustomizations.jsm
       return [];
@@ -483,17 +486,19 @@ ExtensionData.prototype = {
   // Reads the extension's |manifest.json| file, and stores its
   // parsed contents in |this.manifest|.
   readManifest() {
     return this.readJSON("manifest.json").then(manifest => {
       this.manifest = manifest;
 
       try {
         this.id = this.manifest.applications.gecko.id;
-      } catch (e) {}
+      } catch (e) {
+        // Errors are handled by the type check below.
+      }
 
       if (typeof this.id != "string") {
         this.manifestError("Missing required `applications.gecko.id` property");
       }
 
       return manifest;
     });
   },
@@ -621,18 +626,17 @@ ExtensionData.prototype = {
 };
 
 // All moz-extension URIs use a machine-specific UUID rather than the
 // extension's own ID in the host component. This makes it more
 // difficult for web pages to detect whether a user has a given add-on
 // installed (by trying to load a moz-extension URI referring to a
 // web_accessible_resource from the extension). getExtensionUUID
 // returns the UUID for a given add-on ID.
-function getExtensionUUID(id)
-{
+function getExtensionUUID(id) {
   const PREF_NAME = "extensions.webextensions.uuids";
 
   let pref = Preferences.get(PREF_NAME, "{}");
   let map = {};
   try {
     map = JSON.parse(pref);
   } catch (e) {
     Cu.reportError(`Error parsing ${PREF_NAME}.`);
@@ -648,18 +652,17 @@ function getExtensionUUID(id)
 
   map[id] = uuid;
   Preferences.set(PREF_NAME, JSON.stringify(map));
   return uuid;
 }
 
 // We create one instance of this class per extension. |addonData|
 // comes directly from bootstrap.js when initializing.
-this.Extension = function(addonData)
-{
+this.Extension = function(addonData) {
   ExtensionData.call(this, addonData.resourceURI);
 
   this.uuid = getExtensionUUID(addonData.id);
 
   if (addonData.cleanupFile) {
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     this.cleanupFile = addonData.cleanupFile || null;
     delete addonData.cleanupFile;
@@ -678,17 +681,17 @@ this.Extension = function(addonData)
   this.hasShutdown = false;
   this.onShutdown = new Set();
 
   this.permissions = new Set();
   this.whiteListedHosts = null;
   this.webAccessibleResources = new Set();
 
   this.emitter = new EventEmitter();
-}
+};
 
 /**
  * This code is designed to make it easy to test a WebExtension
  * without creating a bunch of files. Everything is contained in a
  * single JSON blob.
  *
  * Properties:
  *   "background": "<JS code>"
@@ -705,18 +708,17 @@ this.Extension = function(addonData)
  *     to file names)
  *
  * To make things easier, the value of "background" and "files"[] can
  * be a function, which is converted to source that is run.
  *
  * The generated extension is stored in the system temporary directory,
  * and an nsIFile object pointing to it is returned.
  */
-this.Extension.generateXPI = function(id, data)
-{
+this.Extension.generateXPI = function(id, data) {
   let manifest = data.manifest;
   if (!manifest) {
     manifest = {};
   }
 
   let files = data.files;
   if (!files) {
     files = {};
@@ -794,30 +796,29 @@ this.Extension.generateXPI = function(id
 
   return file;
 };
 
 /**
  * Generates a new extension using |Extension.generateXPI|, and initializes a
  * new |Extension| instance which will execute it.
  */
-this.Extension.generate = function(id, data)
-{
+this.Extension.generate = function(id, data) {
   let file = this.generateXPI(id, data);
 
   flushJarCache(file);
   Services.ppmm.broadcastAsyncMessage("Extension:FlushJarCache", {path: file.path});
 
   let fileURI = Services.io.newFileURI(file);
   let jarURI = Services.io.newURI("jar:" + fileURI.spec + "!/", null, null);
 
   return new Extension({
     id,
     resourceURI: jarURI,
-    cleanupFile: file
+    cleanupFile: file,
   });
 };
 
 Extension.prototype = extend(Object.create(ExtensionData.prototype), {
   on(hook, f) {
     return this.emitter.on(hook, f);
   },
 
@@ -851,17 +852,17 @@ Extension.prototype = extend(Object.crea
   // need.
   serialize() {
     return {
       id: this.id,
       uuid: this.uuid,
       manifest: this.manifest,
       resourceURL: this.addonData.resourceURI.spec,
       baseURL: this.baseURI.spec,
-      content_scripts: this.manifest.content_scripts || [],
+      content_scripts: this.manifest.content_scripts || [],  // eslint-disable-line camelcase
       webAccessibleResources: this.webAccessibleResources,
       whiteListedHosts: this.whiteListedHosts.serialize(),
       localeData: this.localeData.serialize(),
     };
   },
 
   broadcast(msg, data) {
     return new Promise(resolve => {
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -1,16 +1,18 @@
 /* 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";
 
 this.EXPORTED_SYMBOLS = ["ExtensionContent"];
 
+/* globals ExtensionContent */
+
 /*
  * This file handles the content process side of extensions. It mainly
  * takes care of content script injection, content script APIs, and
  * messaging.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
@@ -33,83 +35,84 @@ var {
   runSafeSyncWithoutClone,
   LocaleData,
   MessageBroker,
   Messenger,
   injectAPI,
   flushJarCache,
 } = ExtensionUtils;
 
-function isWhenBeforeOrSame(when1, when2)
-{
+function isWhenBeforeOrSame(when1, when2) {
   let table = {"document_start": 0,
                "document_end": 1,
                "document_idle": 2};
   return table[when1] <= table[when2];
 }
 
 // This is the fairly simple API that we inject into content
 // scripts.
-var api = context => { return {
-  runtime: {
-    connect: function(extensionId, connectInfo) {
-      if (!connectInfo) {
-        connectInfo = extensionId;
-        extensionId = null;
-      }
-      let name = connectInfo && connectInfo.name || "";
-      let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
-      return context.messenger.connect(context.messageManager, name, recipient);
-    },
+var api = context => {
+  return {
+    runtime: {
+      connect: function(extensionId, connectInfo) {
+        if (!connectInfo) {
+          connectInfo = extensionId;
+          extensionId = null;
+        }
+        let name = connectInfo && connectInfo.name || "";
+        let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
+        return context.messenger.connect(context.messageManager, name, recipient);
+      },
+
+      getManifest: function() {
+        return Cu.cloneInto(context.extension.manifest, context.cloneScope);
+      },
 
-    getManifest: function() {
-      return Cu.cloneInto(context.extension.manifest, context.cloneScope);
-    },
+      getURL: function(url) {
+        return context.extension.baseURI.resolve(url);
+      },
+
+      onConnect: context.messenger.onConnect("runtime.onConnect"),
+
+      onMessage: context.messenger.onMessage("runtime.onMessage"),
 
-    getURL: function(url) {
-      return context.extension.baseURI.resolve(url);
+      sendMessage: function(...args) {
+        let options; // eslint-disable-line no-unused-vars
+        let extensionId, message, responseCallback;
+        if (args.length == 1) {
+          message = args[0];
+        } else if (args.length == 2) {
+          [message, responseCallback] = args;
+        } else {
+          [extensionId, message, options, responseCallback] = args;
+        }
+
+        let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
+        context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
+      },
     },
 
-    onConnect: context.messenger.onConnect("runtime.onConnect"),
-
-    onMessage: context.messenger.onMessage("runtime.onMessage"),
+    extension: {
+      getURL: function(url) {
+        return context.extension.baseURI.resolve(url);
+      },
 
-    sendMessage: function(...args) {
-      let extensionId, message, options, responseCallback;
-      if (args.length == 1) {
-        message = args[0];
-      } else if (args.length == 2) {
-        [message, responseCallback] = args;
-      } else {
-        [extensionId, message, options, responseCallback] = args;
-      }
-
-      let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
-      context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
-    },
-  },
-
-  extension: {
-    getURL: function(url) {
-      return context.extension.baseURI.resolve(url);
+      inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
     },
 
-    inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
-  },
-
-  i18n: {
-    getMessage: function(messageName, substitutions) {
-      return context.extension.localizeMessage(messageName, substitutions);
+    i18n: {
+      getMessage: function(messageName, substitutions) {
+        return context.extension.localizeMessage(messageName, substitutions);
+      },
     },
-  },
-}};
+  };
+};
 
 // Represents a content script.
-function Script(options)
-{
+function Script(options) {
   this.options = options;
   this.run_at = this.options.run_at;
   this.js = this.options.js || [];
   this.css = this.options.css || [];
 
   this.matches_ = new MatchPattern(this.options.matches);
   this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null);
   // TODO: MatchPattern should pre-mangle host-only patterns so that we
@@ -150,18 +153,18 @@ Script.prototype = {
   },
 
   tryInject(extension, window, sandbox, shouldRun) {
     if (!this.matches(window)) {
       return;
     }
 
     if (shouldRun("document_start")) {
-      let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
-        getInterface(Ci.nsIDOMWindowUtils);
+      let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
 
       for (let url of this.css) {
         url = extension.baseURI.resolve(url);
         runSafeSyncWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
       }
 
       if (this.options.cssCode) {
         let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode);
@@ -178,46 +181,46 @@ Script.prototype = {
         if (AppConstants.platform == "gonk" && scheduled != "document_idle") {
           Cu.reportError(`Script injection: ignoring ${url} at ${scheduled}`);
         }
         url = extension.baseURI.resolve(url);
 
         let options = {
           target: sandbox,
           charset: "UTF-8",
-          async: AppConstants.platform == "gonk"
-        }
+          async: AppConstants.platform == "gonk",
+        };
         runSafeSyncWithoutClone(Services.scriptloader.loadSubScriptWithOptions, url, options);
       }
 
       if (this.options.jsCode) {
         Cu.evalInSandbox(this.options.jsCode, sandbox, "latest");
       }
     }
   },
 };
 
-function getWindowMessageManager(contentWindow)
-{
+function getWindowMessageManager(contentWindow) {
   let ir = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDocShell)
                         .QueryInterface(Ci.nsIInterfaceRequestor);
   try {
     return ir.getInterface(Ci.nsIContentFrameMessageManager);
   } catch (e) {
     // Some windows don't support this interface (hidden window).
     return null;
   }
 }
 
+var ExtensionManager;
+
 // Scope in which extension content script code can run. It uses
 // Cu.Sandbox to run the code. There is a separate scope for each
 // frame.
-function ExtensionContext(extensionId, contentWindow)
-{
+function ExtensionContext(extensionId, contentWindow) {
   this.extension = ExtensionManager.get(extensionId);
   this.extensionId = extensionId;
   this.contentWindow = contentWindow;
 
   this.onClose = new Set();
 
   let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
@@ -234,17 +237,17 @@ function ExtensionContext(extensionId, c
     prin = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal);
   }
 
   this.sandbox = Cu.Sandbox(prin, {sandboxPrototype: contentWindow, wantXrays: true, isWebExtensionContentScript: true});
 
   let delegate = {
     getSender(context, target, sender) {
       // Nothing to do here.
-    }
+    },
   };
 
   let url = contentWindow.location.href;
   let broker = ExtensionContent.getBroker(mm);
   this.messenger = new Messenger(this, broker, {id: extensionId, frameId, url},
                                  {id: extensionId, frameId}, delegate);
 
   let chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "browser"});
@@ -275,18 +278,17 @@ ExtensionContext.prototype = {
   close() {
     for (let obj of this.onClose) {
       obj.close();
     }
     Cu.nukeSandbox(this.sandbox);
   },
 };
 
-function windowId(window)
-{
+function windowId(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils)
                .currentInnerWindowID;
 }
 
 // Responsible for creating ExtensionContexts and injecting content
 // scripts into them when new documents are created.
 var DocumentManager = {
@@ -327,26 +329,28 @@ var DocumentManager = {
       // Make sure we only load into frames that ExtensionContent.init
       // was called on (i.e., not frames for social or sidebars).
       let mm = getWindowMessageManager(window);
       if (!mm || !ExtensionContent.globals.has(mm)) {
         return;
       }
 
       this.trigger("document_start", window);
+      /* eslint-disable mozilla/balanced-listeners */
       window.addEventListener("DOMContentLoaded", this, true);
       window.addEventListener("load", this, true);
+      /* eslint-enable mozilla/balanced-listeners */
     } else if (topic == "inner-window-destroyed") {
       let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
       if (!this.windows.has(id)) {
         return;
       }
 
       let extensions = this.windows.get(id);
-      for (let [extensionId, context] of extensions) {
+      for (let [, context] of extensions) {
         context.close();
       }
 
       this.windows.delete(id);
     }
   },
 
   handleEvent: function(event) {
@@ -380,17 +384,17 @@ var DocumentManager = {
     // FIXME: Script should be executed only if current state has
     // already reached its run_at state, or we have to keep it around
     // somewhere to execute later.
     context.execute(script, scheduled => true);
   },
 
   enumerateWindows: function*(docShell) {
     let window = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow)
+                         .getInterface(Ci.nsIDOMWindow);
     yield [window, this.getWindowState(window)];
 
     for (let i = 0; i < docShell.childCount; i++) {
       let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell);
       yield* this.enumerateWindows(child);
     }
   },
 
@@ -425,17 +429,17 @@ var DocumentManager = {
             context.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state));
           }
         }
       }
     }
   },
 
   shutdownExtension(extensionId) {
-    for (let [windowId, extensions] of this.windows) {
+    for (let [, extensions] of this.windows) {
       let context = extensions.get(extensionId);
       if (context) {
         context.close();
         extensions.delete(extensionId);
       }
     }
 
     this.extensionCount--;
@@ -453,18 +457,17 @@ var DocumentManager = {
           context.execute(script, scheduled => scheduled == state);
         }
       }
     }
   },
 };
 
 // Represents a browser extension in the content process.
-function BrowserExtensionContent(data)
-{
+function BrowserExtensionContent(data) {
   this.id = data.id;
   this.uuid = data.uuid;
   this.data = data;
   this.scripts = data.content_scripts.map(scriptData => new Script(scriptData));
   this.webAccessibleResources = data.webAccessibleResources;
   this.whiteListedHosts = data.whiteListedHosts;
 
   this.localeData = new LocaleData(data.localeData);
@@ -473,17 +476,17 @@ function BrowserExtensionContent(data)
   this.baseURI = Services.io.newURI(data.baseURL, null, null);
 
   let uri = Services.io.newURI(data.resourceURL, null, null);
 
   if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
     // Extension.jsm takes care of this in the parent.
     ExtensionManagement.startupExtension(this.uuid, uri, this);
   }
-};
+}
 
 BrowserExtensionContent.prototype = {
   shutdown() {
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
       ExtensionManagement.shutdownExtension(this.uuid);
     }
   },
 
@@ -491,17 +494,17 @@ BrowserExtensionContent.prototype = {
     return this.localeData.localizeMessage(...args);
   },
 
   localize(...args) {
     return this.localeData.localize(...args);
   },
 };
 
-var ExtensionManager = {
+ExtensionManager = {
   // Map[extensionId, BrowserExtensionContent]
   extensions: new Map(),
 
   init() {
     Services.cpmm.addMessageListener("Extension:Startup", this);
     Services.cpmm.addMessageListener("Extension:Shutdown", this);
     Services.cpmm.addMessageListener("Extension:FlushJarCache", this);
 
@@ -541,17 +544,17 @@ var ExtensionManager = {
         let nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
                                              "initWithPath");
         let file = new nsIFile(data.path);
         flushJarCache(file);
         Services.cpmm.sendAsyncMessage("Extension:FlushJarCacheComplete");
         break;
       }
     }
-  }
+  },
 };
 
 this.ExtensionContent = {
   globals: new Map(),
 
   init(global) {
     let broker = new MessageBroker([global]);
     this.globals.set(global, broker);
@@ -576,18 +579,18 @@ this.ExtensionContent = {
   },
 
   getBroker(messageManager) {
     return this.globals.get(messageManager);
   },
 
   receiveMessage({target, name, data}) {
     switch (name) {
-    case "Extension:Execute":
-      let script = new Script(data.options);
-      let {extensionId} = data;
-      DocumentManager.executeScript(target, extensionId, script);
-      break;
+      case "Extension:Execute":
+        let script = new Script(data.options);
+        let {extensionId} = data;
+        DocumentManager.executeScript(target, extensionId, script);
+        break;
     }
   },
 };
 
 ExtensionManager.init();
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -65,25 +65,25 @@ var Frames = {
     }
 
     // Not a top-level window. Just return the ID as normal.
     return this.getId(parentWindowId);
   },
 
   receiveMessage({name, data}) {
     switch (name) {
-    case "Extension:TopWindowID":
-      // FIXME: Need to handle the case where the content process
-      // crashes. Right now we leak its top window IDs.
-      this.topWindowIds.add(data.windowId);
-      break;
+      case "Extension:TopWindowID":
+        // FIXME: Need to handle the case where the content process
+        // crashes. Right now we leak its top window IDs.
+        this.topWindowIds.add(data.windowId);
+        break;
 
-    case "Extension:RemoveTopWindowID":
-      this.topWindowIds.delete(data.windowId);
-      break;
+      case "Extension:RemoveTopWindowID":
+        this.topWindowIds.delete(data.windowId);
+        break;
     }
   },
 };
 Frames.init();
 
 // Manage the collection of ext-*.js scripts that define the extension API.
 var Scripts = {
   scripts: new Set(),
@@ -156,17 +156,17 @@ var Service = {
   extensionURILoadableByAnyone(uri) {
     let uuid = uri.host;
     let extension = this.uuidMap.get(uuid);
     if (!extension) {
       return false;
     }
 
     let path = uri.path;
-    if (path.length > 0 && path[0] == '/') {
+    if (path.length > 0 && path[0] == "/") {
       path = path.substr(1);
     }
     return extension.webAccessibleResources.has(path);
   },
 
   // Checks whether a given extension can load this URI (typically via
   // an XML HTTP request). The manifest.json |permissions| directive
   // determines this.
--- a/toolkit/components/extensions/ExtensionStorage.jsm
+++ b/toolkit/components/extensions/ExtensionStorage.jsm
@@ -8,19 +8,21 @@ this.EXPORTED_SYMBOLS = ["ExtensionStora
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/osfile.jsm")
+Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 
+/* globals OS ExtensionStorage */
+
 var Path = OS.Path;
 var profileDir = OS.Constants.Path.profileDir;
 
 this.ExtensionStorage = {
   cache: new Map(),
   listeners: new Map(),
 
   extensionDir: Path.join(profileDir, "browser-extension-data"),
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -13,56 +13,52 @@ const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Locale",
                                   "resource://gre/modules/Locale.jsm");
 
 // Run a function and report exceptions.
-function runSafeSyncWithoutClone(f, ...args)
-{
+function runSafeSyncWithoutClone(f, ...args) {
   try {
     return f(...args);
   } catch (e) {
     dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n[[Exception stack\n${e.stack}Current stack\n${Error().stack}]]\n`);
     Cu.reportError(e);
   }
 }
 
 // Run a function and report exceptions.
-function runSafeWithoutClone(f, ...args)
-{
+function runSafeWithoutClone(f, ...args) {
   if (typeof(f) != "function") {
     dump(`Extension error: expected function\n${Error().stack}`);
     return;
   }
 
   Services.tm.currentThread.dispatch(function() {
     runSafeSyncWithoutClone(f, ...args);
   }, Ci.nsIEventTarget.DISPATCH_NORMAL);
 }
 
 // Run a function, cloning arguments into context.cloneScope, and
 // report exceptions. |f| is expected to be in context.cloneScope.
-function runSafeSync(context, f, ...args)
-{
+function runSafeSync(context, f, ...args) {
   try {
     args = Cu.cloneInto(args, context.cloneScope);
   } catch (e) {
     Cu.reportError(e);
     dump(`runSafe failure: cloning into ${context.cloneScope}: ${e}\n\n${Error().stack}`);
   }
   return runSafeSyncWithoutClone(f, ...args);
 }
 
 // Run a function, cloning arguments into context.cloneScope, and
 // report exceptions. |f| is expected to be in context.cloneScope.
-function runSafe(context, f, ...args)
-{
+function runSafe(context, f, ...args) {
   try {
     args = Cu.cloneInto(args, context.cloneScope);
   } catch (e) {
     Cu.reportError(e);
     dump(`runSafe failure: cloning into ${context.cloneScope}: ${e}\n\n${Error().stack}`);
   }
   return runSafeWithoutClone(f, ...args);
 }
@@ -85,18 +81,17 @@ function extend(obj, ...args) {
     }
   }
 
   return obj;
 }
 
 // Similar to a WeakMap, but returns a particular default value for
 // |get| if a key is not present.
-function DefaultWeakMap(defaultValue)
-{
+function DefaultWeakMap(defaultValue) {
   this.defaultValue = defaultValue;
   this.weakmap = new WeakMap();
 }
 
 DefaultWeakMap.prototype = {
   get(key) {
     if (this.weakmap.has(key)) {
       return this.weakmap.get(key);
@@ -118,17 +113,17 @@ function LocaleData(data) {
   this.selectedLocale = data.selectedLocale;
   this.locales = data.locales || new Map();
 
   // Map(locale-name -> Map(message-key -> localized-strings))
   //
   // Contains a key for each loaded locale, each of which is a
   // Map of message keys to their localized strings.
   this.messages = data.messages || new Map();
-};
+}
 
 LocaleData.prototype = {
   // Representation of the object to send to content processes. This
   // should include anything the content process might need.
   serialize() {
     return {
       defaultLocale: this.defaultLocale,
       selectedLocale: this.selectedLocale,
@@ -146,28 +141,28 @@ LocaleData.prototype = {
     let locales = new Set([locale, this.defaultLocale]
                           .filter(locale => this.messages.has(locale)));
 
     // Message names are case-insensitive, so normalize them to lower-case.
     message = message.toLowerCase();
     for (let locale of locales) {
       let messages = this.messages.get(locale);
       if (messages.has(message)) {
-        let str = messages.get(message)
+        let str = messages.get(message);
 
         if (!Array.isArray(substitutions)) {
           substitutions = [substitutions];
         }
 
         let replacer = (matched, index, dollarSigns) => {
           if (index) {
             // This is not quite Chrome-compatible. Chrome consumes any number
             // of digits following the $, but only accepts 9 substitutions. We
             // accept any number of substitutions.
-            index = parseInt(index) - 1;
+            index = parseInt(index, 10) - 1;
             return index in substitutions ? substitutions[index] : "";
           } else {
             // For any series of contiguous `$`s, the first is dropped, and
             // the rest remain in the output string.
             return dollarSigns;
           }
         };
         return str.replace(/\$(?:([1-9]\d*)|(\$+))/g, replacer);
@@ -296,18 +291,17 @@ LocaleData.prototype = {
 //
 // The result is an object with addListener, removeListener, and
 // hasListener methods. |context| is an add-on scope (either an
 // ExtensionPage in the chrome process or ExtensionContext in a
 // content process). |name| is for debugging. |register| is a function
 // to register the listener. |register| is only called once, event if
 // multiple listeners are registered. |register| should return an
 // unregister function that will unregister the listener.
-function EventManager(context, name, register)
-{
+function EventManager(context, name, register) {
   this.context = context;
   this.name = name;
   this.register = register;
   this.unregister = null;
   this.callbacks = new Set();
   this.registered = false;
 }
 
@@ -370,18 +364,17 @@ EventManager.prototype = {
       hasListener: callback => this.hasListener(callback),
     };
   },
 };
 
 // Similar to EventManager, but it doesn't try to consolidate event
 // notifications. Each addListener call causes us to register once. It
 // allows extra arguments to be passed to addListener.
-function SingletonEventManager(context, name, register)
-{
+function SingletonEventManager(context, name, register) {
   this.context = context;
   this.name = name;
   this.register = register;
   this.unregister = new Map();
   context.callOnClose(this);
 }
 
 SingletonEventManager.prototype = {
@@ -392,17 +385,17 @@ SingletonEventManager.prototype = {
 
   removeListener(callback) {
     if (!this.unregister.has(callback)) {
       return;
     }
 
     let unregister = this.unregister.get(callback);
     this.unregister.delete(callback);
-    this.unregister();
+    unregister();
   },
 
   hasListener(callback) {
     return this.unregister.has(callback);
   },
 
   close() {
     for (let unregister of this.unregister.values()) {
@@ -415,46 +408,44 @@ SingletonEventManager.prototype = {
       addListener: (...args) => this.addListener(...args),
       removeListener: (...args) => this.removeListener(...args),
       hasListener: (...args) => this.hasListener(...args),
     };
   },
 };
 
 // Simple API for event listeners where events never fire.
-function ignoreEvent(context, name)
-{
+function ignoreEvent(context, name) {
   return {
     addListener: function(callback) {
       let id = context.extension.id;
       let frame = Components.stack.caller;
       let msg = `In add-on ${id}, attempting to use listener "${name}", which is unimplemented.`;
       let winID = context.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
       let scriptError = Cc["@mozilla.org/scripterror;1"]
         .createInstance(Ci.nsIScriptError);
       scriptError.initWithWindowID(msg, frame.filename, null,
                                    frame.lineNumber, frame.columnNumber,
                                    Ci.nsIScriptError.warningFlag,
                                    "content javascript", winID);
-      let consoleService = Cc['@mozilla.org/consoleservice;1']
+      let consoleService = Cc["@mozilla.org/consoleservice;1"]
         .getService(Ci.nsIConsoleService);
       consoleService.logMessage(scriptError);
     },
     removeListener: function(callback) {},
     hasListener: function(callback) {},
   };
 }
 
 // Copy an API object from |source| into the scope |dest|.
-function injectAPI(source, dest)
-{
+function injectAPI(source, dest) {
   for (let prop in source) {
     // Skip names prefixed with '_'.
-    if (prop[0] == '_') {
+    if (prop[0] == "_") {
       continue;
     }
 
     let value = source[prop];
     if (typeof(value) == "function") {
       Cu.exportFunction(value, dest, {defineAs: prop});
     } else if (typeof(value) == "object") {
       let obj = Cu.createObjectIn(dest, {defineAs: prop});
@@ -479,18 +470,17 @@ var MESSAGES = [
 // Receives messages from multiple message managers and directs them
 // to a set of listeners. On the child side: one broker per frame
 // script.  On the parent side: one broker total, covering both the
 // global MM and the ppmm. Message must be tagged with a recipient,
 // which is an object with properties. Listeners can filter for
 // messages that have a certain value for a particular property in the
 // recipient. (If a message doesn't specify the given property, it's
 // considered a match.)
-function MessageBroker(messageManagers)
-{
+function MessageBroker(messageManagers) {
   this.messageManagers = messageManagers;
   for (let mm of this.messageManagers) {
     for (let message of MESSAGES) {
       mm.addMessageListener(message, this);
     }
   }
 
   this.listeners = {message: [], connect: []};
@@ -511,17 +501,16 @@ MessageBroker.prototype = {
     return nextBrokerId++;
   },
 
   addListener(type, listener, filter) {
     this.listeners[type].push({filter, listener});
   },
 
   removeListener(type, listener) {
-    let index = -1;
     for (let i = 0; i < this.listeners[type].length; i++) {
       if (this.listeners[type][i].listener == listener) {
         this.listeners[type].splice(i, 1);
         return;
       }
     }
   },
 
@@ -545,36 +534,35 @@ MessageBroker.prototype = {
 
     for (let listener of listeners) {
       listener(type, target, data.message, data.sender, data.recipient);
     }
   },
 
   receiveMessage({name, data, target}) {
     switch (name) {
-    case "Extension:Message":
-      this.runListeners("message", target, data);
-      break;
+      case "Extension:Message":
+        this.runListeners("message", target, data);
+        break;
 
-    case "Extension:Connect":
-      this.runListeners("connect", target, data);
-      break;
+      case "Extension:Connect":
+        this.runListeners("connect", target, data);
+        break;
     }
   },
 
   sendMessage(messageManager, type, message, sender, recipient) {
     let data = {message, sender, recipient};
     let names = {message: "Extension:Message", connect: "Extension:Connect"};
     messageManager.sendAsyncMessage(names[type], data);
   },
 };
 
 // Abstraction for a Port object in the extension API. Each port has a unique ID.
-function Port(context, messageManager, name, id, sender)
-{
+function Port(context, messageManager, name, id, sender) {
   this.context = context;
   this.messageManager = messageManager;
   this.name = name;
   this.id = id;
   this.listenerName = `Extension:Port-${this.id}`;
   this.disconnectName = `Extension:Disconnect-${this.id}`;
   this.sender = sender;
   this.disconnected = false;
@@ -592,17 +580,17 @@ Port.prototype = {
 
     let publicAPI = {
       name: this.name,
       disconnect: () => {
         this.disconnect();
       },
       postMessage: json => {
         if (this.disconnected) {
-          throw "Attempt to postMessage on disconnected port";
+          throw new this.context.contentWindow.Error("Attempt to postMessage on disconnected port");
         }
         this.messageManager.sendAsyncMessage(this.listenerName, json);
       },
       onDisconnect: new EventManager(this.context, "Port.onDisconnect", fire => {
         let listener = () => {
           if (!this.disconnected) {
             fire();
           }
@@ -652,29 +640,28 @@ Port.prototype = {
       }
 
       this.handleDisconnection();
     }
   },
 
   disconnect() {
     if (this.disconnected) {
-      throw "Attempt to disconnect() a disconnected port";
+      throw new this.context.contentWindow.Error("Attempt to disconnect() a disconnected port");
     }
     this.handleDisconnection();
     this.messageManager.sendAsyncMessage(this.disconnectName);
   },
 
   close() {
     this.disconnect();
   },
 };
 
-function getMessageManager(target)
-{
+function getMessageManager(target) {
   if (target instanceof Ci.nsIDOMXULElement) {
     return target.messageManager;
   } else {
     return target;
   }
 }
 
 // Each extension scope gets its own Messenger object. It handles the
@@ -682,18 +669,17 @@ function getMessageManager(target)
 //
 // |context| is the extension scope.
 // |broker| is a MessageBroker used to receive and send messages.
 // |sender| is an object describing the sender (usually giving its extensionId, tabId, etc.)
 // |filter| is a recipient filter to apply to incoming messages from the broker.
 // |delegate| is an object that must implement a few methods:
 //    getSender(context, messageManagerTarget, sender): returns a MessageSender
 //      See https://developer.chrome.com/extensions/runtime#type-MessageSender.
-function Messenger(context, broker, sender, filter, delegate)
-{
+function Messenger(context, broker, sender, filter, delegate) {
   this.context = context;
   this.broker = broker;
   this.sender = sender;
   this.filter = filter;
   this.delegate = delegate;
 }
 
 Messenger.prototype = {
@@ -711,17 +697,17 @@ Messenger.prototype = {
       if (response.gotData) {
         // TODO: Handle failure to connect to the extension?
         runSafe(this.context, responseCallback, response.data);
       }
     };
     onClose = {
       close() {
         messageManager.removeMessageListener(replyName, listener);
-      }
+      },
     };
     if (responseCallback) {
       messageManager.addMessageListener(replyName, listener);
       this.context.callOnClose(onClose);
     }
   },
 
   onMessage(name) {
@@ -785,18 +771,17 @@ Messenger.prototype = {
       this.broker.addListener("connect", listener, this.filter);
       return () => {
         this.broker.removeListener("connect", listener);
       };
     }).api();
   },
 };
 
-function flushJarCache(jarFile)
-{
+function flushJarCache(jarFile) {
   Services.obs.notifyObservers(jarFile, "flush-cache-entry", null);
 }
 
 this.ExtensionUtils = {
   runSafeWithoutClone,
   runSafeSyncWithoutClone,
   runSafe,
   runSafeSync,
--- a/toolkit/components/extensions/ext-alarms.js
+++ b/toolkit/components/extensions/ext-alarms.js
@@ -1,25 +1,26 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> Set[Alarm]]
 var alarmsMap = new WeakMap();
 
 // WeakMap[Extension -> Set[callback]]
 var alarmCallbacksMap = new WeakMap();
 
 // Manages an alarm created by the extension (alarms API).
-function Alarm(extension, name, alarmInfo)
-{
+function Alarm(extension, name, alarmInfo) {
   this.extension = extension;
   this.name = name;
   this.when = alarmInfo.when;
   this.delayInMinutes = alarmInfo.delayInMinutes;
   this.periodInMinutes = alarmInfo.periodInMinutes;
   this.canceled = false;
 
   let delay, scheduledTime;
@@ -70,28 +71,30 @@ Alarm.prototype = {
     return {
       name: this.name,
       scheduledTime: this.scheduledTime,
       periodInMinutes: this.periodInMinutes,
     };
   },
 };
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
   alarmsMap.set(extension, new Set());
   alarmCallbacksMap.set(extension, new Set());
 });
 
 extensions.on("shutdown", (type, extension) => {
   for (let alarm of alarmsMap.get(extension)) {
     alarm.clear();
   }
   alarmsMap.delete(extension);
   alarmCallbacksMap.delete(extension);
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerAPI((extension, context) => {
   return {
     alarms: {
       create: function(...args) {
         let name = "", alarmInfo;
         if (args.length == 1) {
           alarmInfo = args[0];
@@ -116,17 +119,17 @@ extensions.registerAPI((extension, conte
             runSafe(context, callback, alarm.data);
             break;
           }
         }
       },
 
       getAll: function(callback) {
         let alarms = alarmsMap.get(extension);
-        result = alarms.map(alarm => alarm.data);
+        let result = alarms.map(alarm => alarm.data);
         runSafe(context, callback, result);
       },
 
       clear: function(...args) {
         let name = "", callback;
         if (args.length == 1) {
           callback = args[0];
         } else {
--- a/toolkit/components/extensions/ext-backgroundPage.js
+++ b/toolkit/components/extensions/ext-backgroundPage.js
@@ -1,18 +1,19 @@
+"use strict";
+
 var { interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 // WeakMap[Extension -> BackgroundPage]
 var backgroundPagesMap = new WeakMap();
 
 // Responsible for the background_page section of the manifest.
-function BackgroundPage(options, extension)
-{
+function BackgroundPage(options, extension) {
   this.extension = extension;
   this.scripts = options.scripts || [];
   this.page = options.page || null;
   this.contentWindow = null;
   this.webNav = null;
   this.context = null;
 }
 
@@ -85,28 +86,30 @@ BackgroundPage.prototype = {
   shutdown() {
     // Navigate away from the background page to invalidate any
     // setTimeouts or other callbacks.
     this.webNav.loadURI("about:blank", 0, null, null, null);
     this.webNav = null;
   },
 };
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_background", (type, directive, extension, manifest) => {
   let bgPage = new BackgroundPage(manifest.background, extension);
   bgPage.build();
   backgroundPagesMap.set(extension, bgPage);
 });
 
 extensions.on("shutdown", (type, extension) => {
   if (backgroundPagesMap.has(extension)) {
     backgroundPagesMap.get(extension).shutdown();
     backgroundPagesMap.delete(extension);
   }
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerAPI((extension, context) => {
   return {
     extension: {
       getBackgroundPage: function() {
         return backgroundPagesMap.get(extension).contentWindow;
       },
     },
--- a/toolkit/components/extensions/ext-cookies.js
+++ b/toolkit/components/extensions/ext-cookies.js
@@ -1,8 +1,10 @@
+"use strict";
+
 const { interfaces: Ci, utils: Cu } = Components;
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
 // Cookies from private tabs currently can't be enumerated.
@@ -13,18 +15,18 @@ function convert(cookie) {
     name: cookie.name,
     value: cookie.value,
     domain: cookie.host,
     hostOnly: !cookie.isDomain,
     path: cookie.path,
     secure: cookie.isSecure,
     httpOnly: cookie.isHttpOnly,
     session: cookie.isSession,
-    storeId: DEFAULT_STORE
-  }
+    storeId: DEFAULT_STORE,
+  };
 
   if (!cookie.isSession) {
     result.expirationDate = cookie.expiry;
   }
 
   return result;
 }
 
@@ -75,17 +77,17 @@ function* query(allDetails, allowed) {
         return false;
       }
 
       return true;
     }
 
     // "Restricts the retrieved cookies to those that would match the given URL."
     if ("url" in details) {
-      var uri = Services.io.newURI(details.url, null, null);
+      let uri = Services.io.newURI(details.url, null, null);
 
       if (!domainMatches(uri.host)) {
         return false;
       }
 
       if (cookie.isSecure && uri.scheme != "https") {
         return false;
       }
@@ -217,17 +219,17 @@ extensions.registerPrivilegedAPI("cookie
         }
 
         for (let cookie of query(details, ["url", "name", "storeId"])) {
           Services.cookies.remove(cookie.host, cookie.name, cookie.path, false);
           if (callback) {
             runSafe(context, callback, {
               url: details.url,
               name: details.name,
-              storeId: DEFAULT_STORE
+              storeId: DEFAULT_STORE,
             });
           }
           // Todo: could there be multiple per subdomain?
           return;
         }
 
         if (callback) {
           runSafe(context, callback, null);
@@ -238,17 +240,17 @@ extensions.registerPrivilegedAPI("cookie
         // Todo: list all the tabIds for non-private tabs
         runSafe(context, callback, [{id: DEFAULT_STORE, tabIds: []}]);
       },
 
       onChanged: new EventManager(context, "cookies.onChanged", fire => {
         let observer = (subject, topic, data) => {
           let notify = (removed, cookie, cause) => {
             fire({removed, cookie: convert(cookie.QueryInterface(Ci.nsICookie2)), cause});
-          }
+          };
 
           // We do our best effort here to map the incompatible states.
           switch (data) {
             case "deleted":
               notify(true, subject, "explicit");
               break;
             case "added":
               notify(false, subject, "explicit");
--- a/toolkit/components/extensions/ext-extension.js
+++ b/toolkit/components/extensions/ext-extension.js
@@ -1,8 +1,10 @@
+"use strict";
+
 extensions.registerAPI((extension, context) => {
   return {
     extension: {
       getURL: function(url) {
         return extension.baseURI.resolve(url);
       },
 
       getViews: function(fetchProperties) {
@@ -24,13 +26,13 @@ extensions.registerAPI((extension, conte
           result.push(view.contentWindow);
         }
 
         return result;
       },
 
       get inIncognitoContext() {
         return context.incognito;
-      }
+      },
     },
   };
 });
 
--- a/toolkit/components/extensions/ext-i18n.js
+++ b/toolkit/components/extensions/ext-i18n.js
@@ -1,8 +1,10 @@
+"use strict";
+
 extensions.registerAPI((extension, context) => {
   return {
     i18n: {
       getMessage: function(messageName, substitutions) {
         return extension.localizeMessage(messageName, substitutions);
       },
     },
   };
--- a/toolkit/components/extensions/ext-idle.js
+++ b/toolkit/components/extensions/ext-idle.js
@@ -1,8 +1,10 @@
+"use strict";
+
 extensions.registerPrivilegedAPI("idle", (extension, context) => {
   return {
     idle: {
       queryState: function(detectionIntervalInSeconds, callback) {
         runSafe(context, callback, "active");
       },
     },
   };
--- a/toolkit/components/extensions/ext-notifications.js
+++ b/toolkit/components/extensions/ext-notifications.js
@@ -1,26 +1,27 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   ignoreEvent,
   runSafe,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> Set[Notification]]
 var notificationsMap = new WeakMap();
 
 // WeakMap[Extension -> Set[callback]]
 var notificationCallbacksMap = new WeakMap();
 
 // Manages a notification popup (notifications API) created by the extension.
-function Notification(extension, id, options)
-{
+function Notification(extension, id, options) {
   this.extension = extension;
   this.id = id;
   this.options = options;
 
   let imageURL;
   if (options.iconUrl) {
     imageURL = this.extension.baseURI.resolve(options.iconUrl);
   }
@@ -58,28 +59,30 @@ Notification.prototype = {
     for (let callback in notificationCallbacksMap.get(this.extension)) {
       callback(this);
     }
 
     notificationsMap.get(this.extension).delete(this);
   },
 };
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
   notificationsMap.set(extension, new Set());
   notificationCallbacksMap.set(extension, new Set());
 });
 
 extensions.on("shutdown", (type, extension) => {
   for (let notification of notificationsMap.get(extension)) {
     notification.clear();
   }
   notificationsMap.delete(extension);
   notificationCallbacksMap.delete(extension);
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 var nextId = 0;
 
 extensions.registerPrivilegedAPI("notifications", (extension, context) => {
   return {
     notifications: {
       create: function(...args) {
         let notificationId, options, callback;
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/ext-runtime.js
@@ -1,8 +1,10 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
@@ -53,17 +55,18 @@ extensions.registerAPI((extension, conte
 
         let name = connectInfo && connectInfo.name || "";
         let recipient = extensionId ? {extensionId} : {extensionId: extension.id};
 
         return context.messenger.connect(Services.cpmm, name, recipient);
       },
 
       sendMessage: function(...args) {
-        let extensionId, message, options, responseCallback;
+        let options; // eslint-disable-line no-unused-vars
+        let extensionId, message, responseCallback;
         if (args.length == 1) {
           message = args[0];
         } else if (args.length == 2) {
           [message, responseCallback] = args;
         } else {
           [extensionId, message, options, responseCallback] = args;
         }
         let recipient = {extensionId: extensionId ? extensionId : extension.id};
@@ -82,17 +85,17 @@ extensions.registerAPI((extension, conte
 
       getPlatformInfo: function(callback) {
         let os = AppConstants.platform;
         if (os == "macosx") {
           os = "mac";
         }
 
         let abi = Services.appinfo.XPCOMABI;
-        let [arch, compiler] = abi.split("-");
+        let [arch] = abi.split("-");
         if (arch == "x86") {
           arch = "x86-32";
         } else if (arch == "x86_64") {
           arch = "x86-64";
         }
 
         let info = {os, arch};
         runSafe(context, callback, info);
--- a/toolkit/components/extensions/ext-storage.js
+++ b/toolkit/components/extensions/ext-storage.js
@@ -1,8 +1,10 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
                                   "resource://gre/modules/ExtensionStorage.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
--- a/toolkit/components/extensions/ext-test.js
+++ b/toolkit/components/extensions/ext-test.js
@@ -1,30 +1,34 @@
+"use strict";
+
 Components.utils.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> Set(callback)]
 var messageHandlers = new WeakMap();
 
+/* eslint-disable mozilla/balanced-listeners */
 extensions.on("startup", (type, extension) => {
   messageHandlers.set(extension, new Set());
 });
 
 extensions.on("shutdown", (type, extension) => {
   messageHandlers.delete(extension);
 });
 
 extensions.on("test-message", (type, extension, ...args) => {
   let handlers = messageHandlers.get(extension);
   for (let handler of handlers) {
     handler(...args);
   }
 });
+/* eslint-enable mozilla/balanced-listeners */
 
 extensions.registerAPI((extension, context) => {
   return {
     test: {
       sendMessage: function(...args) {
         extension.emit("test-message", ...args);
       },
 
@@ -44,21 +48,21 @@ extensions.registerAPI((extension, conte
         extension.emit("test-result", false, msg);
       },
 
       succeed: function(msg) {
         extension.emit("test-result", true, msg);
       },
 
       assertTrue: function(value, msg) {
-        extension.emit("test-result", value ? true : false, msg);
+        extension.emit("test-result", Boolean(value), msg);
       },
 
       assertFalse: function(value, msg) {
-        extension.emit("test-result", !value ? true : false, msg);
+        extension.emit("test-result", !value, msg);
       },
 
       assertEq: function(expected, actual, msg) {
         extension.emit("test-eq", expected === actual, msg, String(expected), String(actual));
       },
 
       onMessage: new EventManager(context, "test.onMessage", fire => {
         let handlers = messageHandlers.get(extension);
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -1,8 +1,10 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
                                   "resource://gre/modules/ExtensionManagement.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
@@ -12,18 +14,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   SingletonEventManager,
   ignoreEvent,
   runSafe,
 } = ExtensionUtils;
 
 // Similar to WebRequestEventManager but for WebNavigation.
-function WebNavigationEventManager(context, eventName)
-{
+function WebNavigationEventManager(context, eventName) {
   let name = `webNavigation.${eventName}`;
   let register = callback => {
     let listener = data => {
       if (!data.browser) {
         return;
       }
 
       let tabId = TabManager.getBrowserId(data.browser);
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -1,8 +1,10 @@
+"use strict";
+
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
                                   "resource://gre/modules/WebRequest.jsm");
@@ -12,18 +14,17 @@ var {
   SingletonEventManager,
   runSafeSync,
   ignoreEvent,
 } = ExtensionUtils;
 
 // EventManager-like class specifically for WebRequest. Inherits from
 // SingletonEventManager. Takes care of converting |details| parameter
 // when invoking listeners.
-function WebRequestEventManager(context, eventName)
-{
+function WebRequestEventManager(context, eventName) {
   let name = `webRequest.${eventName}`;
   let register = (callback, filter, info) => {
     let listener = data => {
       if (!data.browser) {
         return;
       }
 
       let tabId = TabManager.getBrowserId(data.browser);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/.eslintrc
@@ -0,0 +1,19 @@
+{
+  "extends": "../../.eslintrc",
+
+  "globals": {
+    // DOM window globals
+    "window": false,
+    "XMLHttpRequest": false,
+
+    "sendAsyncMessage": false,
+
+    "NetUtil": true,
+    "XPCOMUtils": true,
+
+    // Test harness globals
+    "add_task": false,
+    "ok": false,
+    "SimpleTest": false,
+  }
+}
--- a/toolkit/components/extensions/test/mochitest/file_script_bad.js
+++ b/toolkit/components/extensions/test/mochitest/file_script_bad.js
@@ -1,1 +1,3 @@
+"use strict";
+
 window.failure = true;
--- a/toolkit/components/extensions/test/mochitest/file_script_good.js
+++ b/toolkit/components/extensions/test/mochitest/file_script_good.js
@@ -1,1 +1,3 @@
+"use strict";
+
 window.success = window.success ? window.success + 1 : 1;
--- a/toolkit/components/extensions/test/mochitest/file_script_redirect.js
+++ b/toolkit/components/extensions/test/mochitest/file_script_redirect.js
@@ -1,2 +1,4 @@
+"use strict";
+
 window.failure = true;
 
--- a/toolkit/components/extensions/test/mochitest/file_script_xhr.js
+++ b/toolkit/components/extensions/test/mochitest/file_script_xhr.js
@@ -1,3 +1,5 @@
+"use strict";
+
 var request = new XMLHttpRequest();
 request.open("get", "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/xhr_resource", false);
 request.send();
--- a/toolkit/components/extensions/test/mochitest/head.js
+++ b/toolkit/components/extensions/test/mochitest/head.js
@@ -1,8 +1,12 @@
+"use strict";
+
+/* exported waitForLoad */
+
 function waitForLoad(win) {
   return new Promise(resolve => {
     win.addEventListener("load", function listener() {
       win.removeEventListener("load", listener, true);
       resolve();
     }, true);
   });
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/.eslintrc
@@ -0,0 +1,15 @@
+{
+  "extends": "../../.eslintrc",
+
+  "globals": {
+    "NetUtil": true,
+    "XPCOMUtils": true,
+
+    // Test harness globals
+    "add_task": false,
+    "Assert": false,
+    "do_register_cleanup": false,
+    "equal": false,
+    "ok": false,
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/addons/.eslintrc
@@ -0,0 +1,12 @@
+{
+  "extends": "../../components/extensions/.eslintrc",
+
+  "globals": {
+    "addEventListener": false,
+    "addMessageListener": false,
+    "removeEventListener": false,
+    "sendAsyncMessage": false,
+
+    "initialProcessData": true,
+  },
+}
--- a/toolkit/modules/addons/MatchPattern.jsm
+++ b/toolkit/modules/addons/MatchPattern.jsm
@@ -3,54 +3,54 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = ["MatchPattern"];
 
+/* globals MatchPattern */
+
 const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "app"];
 
 // This function converts a glob pattern (containing * and possibly ?
 // as wildcards) to a regular expression.
-function globToRegexp(pat, allowQuestion)
-{
+function globToRegexp(pat, allowQuestion) {
   // Escape everything except ? and *.
   pat = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&");
 
   if (allowQuestion) {
     pat = pat.replace(/\?/g, ".");
   } else {
     pat = pat.replace(/\?/g, "\\?");
   }
   pat = pat.replace(/\*/g, ".*");
   return new RegExp("^" + pat + "$");
 }
 
 // These patterns follow the syntax in
 // https://developer.chrome.com/extensions/match_patterns
-function SingleMatchPattern(pat)
-{
+function SingleMatchPattern(pat) {
   if (pat == "<all_urls>") {
     this.scheme = PERMITTED_SCHEMES;
     this.host = "*";
-    this.path = new RegExp('.*');
+    this.path = new RegExp(".*");
   } else if (!pat) {
     this.scheme = [];
   } else {
     let re = new RegExp("^(http|https|file|ftp|app|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$");
     let match = re.exec(pat);
     if (!match) {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.scheme = [];
       return;
     }
 
-    if (match[1] == '*') {
+    if (match[1] == "*") {
       this.scheme = ["http", "https"];
     } else {
       this.scheme = [match[1]];
     }
     this.host = match[2];
     this.path = globToRegexp(match[3], false);
 
     // We allow the host to be empty for file URLs.
@@ -64,49 +64,46 @@ function SingleMatchPattern(pat)
 
 SingleMatchPattern.prototype = {
   matches(uri, ignorePath = false) {
     if (this.scheme.indexOf(uri.scheme) == -1) {
       return false;
     }
 
     // This code ignores the port, as Chrome does.
-    if (this.host == '*') {
+    if (this.host == "*") {
       // Don't check anything.
-    } else if (this.host[0] == '*') {
+    } else if (this.host[0] == "*") {
       // It must be *.foo. We also need to allow foo by itself.
       let suffix = this.host.substr(2);
       if (uri.host != suffix && !uri.host.endsWith("." + suffix)) {
         return false;
       }
-    } else {
-      if (this.host != uri.host) {
-        return false;
-      }
+    } else if (this.host != uri.host) {
+      return false;
     }
 
     if (!ignorePath && !this.path.test(uri.path)) {
       return false;
     }
 
     return true;
-  }
+  },
 };
 
-this.MatchPattern = function(pat)
-{
+this.MatchPattern = function(pat) {
   this.pat = pat;
   if (!pat) {
     this.matchers = [];
   } else if (pat instanceof String || typeof(pat) == "string") {
     this.matchers = [new SingleMatchPattern(pat)];
   } else {
     this.matchers = pat.map(p => new SingleMatchPattern(p));
   }
-}
+};
 
 MatchPattern.prototype = {
   // |uri| should be an nsIURI.
   matches(uri) {
     for (let matcher of this.matchers) {
       if (matcher.matches(uri)) {
         return true;
       }
--- a/toolkit/modules/addons/WebNavigation.jsm
+++ b/toolkit/modules/addons/WebNavigation.jsm
@@ -61,27 +61,27 @@ var Manager = {
 
     if (this.listeners.size == 0) {
       this.uninit();
     }
   },
 
   receiveMessage({name, data, target}) {
     switch (name) {
-    case "Extension:StateChange":
-      this.onStateChange(target, data);
-      break;
+      case "Extension:StateChange":
+        this.onStateChange(target, data);
+        break;
 
-    case "Extension:LocationChange":
-      this.onLocationChange(target, data);
-      break;
+      case "Extension:LocationChange":
+        this.onLocationChange(target, data);
+        break;
 
-    case "Extension:DOMContentLoaded":
-      this.onLoad(target, data);
-      break;
+      case "Extension:DOMContentLoaded":
+        this.onLoad(target, data);
+        break;
     }
   },
 
   onStateChange(browser, data) {
     let stateFlags = data.stateFlags;
     if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
       let url = data.requestURL;
       if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
@@ -138,20 +138,20 @@ var Manager = {
 const EVENTS = [
   "onBeforeNavigate",
   "onCommitted",
   "onDOMContentLoaded",
   "onCompleted",
   "onErrorOccurred",
   "onReferenceFragmentUpdated",
 
-  //"onCreatedNavigationTarget",
-  //"onHistoryStateUpdated",
+  // "onCreatedNavigationTarget",
+  // "onHistoryStateUpdated",
 ];
 
 var WebNavigation = {};
 
 for (let event of EVENTS) {
   WebNavigation[event] = {
     addListener: Manager.addListener.bind(Manager, event),
     removeListener: Manager.removeListener.bind(Manager, event),
-  }
+  };
 }
--- a/toolkit/modules/addons/WebNavigationContent.js
+++ b/toolkit/modules/addons/WebNavigationContent.js
@@ -1,24 +1,27 @@
+"use strict";
+
+/* globals docShell */
+
 var Ci = Components.interfaces;
 
-function getWindowId(window)
-{
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function getWindowId(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils)
                .outerWindowID;
 }
 
-function getParentWindowId(window)
-{
+function getParentWindowId(window) {
   return getWindowId(window.parent);
 }
 
-function loadListener(event)
-{
+function loadListener(event) {
   let document = event.target;
   let window = document.defaultView;
   let url = document.documentURI;
   let windowId = getWindowId(window);
   let parentWindowId = getParentWindowId(window);
   sendAsyncMessage("Extension:DOMContentLoaded", {windowId, parentWindowId, url});
 }
 
@@ -76,25 +79,17 @@ var WebProgressListener = {
       location: locationURI ? locationURI.spec : "",
       windowId: webProgress.DOMWindowID,
       parentWindowId: getParentWindowId(webProgress.DOMWindow),
       flags,
     };
     sendAsyncMessage("Extension:LocationChange", data);
   },
 
-  QueryInterface: function QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports)) {
-        return this;
-    }
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
 };
 
 var disabled = false;
 WebProgressListener.init();
 addEventListener("unload", () => {
   if (!disabled) {
     WebProgressListener.uninit();
   }
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -1,16 +1,18 @@
 /* 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";
 
 const EXPORTED_SYMBOLS = ["WebRequest"];
 
+/* exported WebRequest */
+
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
@@ -32,17 +34,17 @@ function parseFilter(filter) {
   // FIXME: Support windowId filtering.
   return {urls: filter.urls || null, types: filter.types || null};
 }
 
 function parseExtra(extra, allowed) {
   if (extra) {
     for (let ex of extra) {
       if (allowed.indexOf(ex) == -1) {
-        throw `Invalid option ${ex}`;
+        throw new Error(`Invalid option ${ex}`);
       }
     }
   }
 
   let result = {};
   for (let al of allowed) {
     if (extra && extra.indexOf(al) != -1) {
       result[al] = true;
@@ -76,17 +78,17 @@ var ContentPolicyManager = {
       }
       let response = null;
       try {
         response = callback({
           url: msg.data.url,
           windowId: msg.data.windowId,
           parentWindowId: msg.data.parentWindowId,
           type: msg.data.type,
-          browser: browser
+          browser: browser,
         });
       } catch (e) {
         Cu.reportError(e);
       }
 
       if (response && response.cancel) {
         return {cancel: true};
       }
@@ -117,18 +119,17 @@ var ContentPolicyManager = {
 
     this.policyData.delete(id);
     this.idMap.delete(callback);
     this.policies.delete(id);
   },
 };
 ContentPolicyManager.init();
 
-function StartStopListener(manager, loadContext)
-{
+function StartStopListener(manager, loadContext) {
   this.manager = manager;
   this.loadContext = loadContext;
   this.orig = null;
 }
 
 StartStopListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
                                          Ci.nsIStreamListener,
@@ -142,17 +143,17 @@ StartStopListener.prototype = {
   onStopRequest(request, context, statusCode) {
     let result = this.orig.onStopRequest(request, context, statusCode);
     this.manager.onStopRequest(request, this.loadContext);
     return result;
   },
 
   onDataAvailable(...args) {
     return this.orig.onDataAvailable(...args);
-  }
+  },
 };
 
 var HttpObserverManager = {
   modifyInitialized: false,
   examineInitialized: false,
 
   listeners: {
     modify: new Map(),
@@ -303,27 +304,27 @@ var HttpObserverManager = {
         return false;
       }
       if (result.redirectUrl) {
         channel.redirectTo(BrowserUtils.makeURI(result.redirectUrl));
         return false;
       }
       if (opts.requestHeaders && result.requestHeaders) {
         // Start by clearing everything.
-        for (let {name, value} of requestHeaders) {
+        for (let {name} of requestHeaders) {
           channel.setRequestHeader(name, "", false);
         }
 
         for (let {name, value} of result.requestHeaders) {
           channel.setRequestHeader(name, value, false);
         }
       }
       if (opts.responseHeaders && result.responseHeaders) {
         // Start by clearing everything.
-        for (let {name, value} of responseHeaders) {
+        for (let {name} of responseHeaders) {
           channel.setResponseHeader(name, "", false);
         }
 
         for (let {name, value} of result.responseHeaders) {
           channel.setResponseHeader(name, value, false);
         }
       }
     }
@@ -367,77 +368,77 @@ var onBeforeRequest = {
     // FIXME: Add requestBody support.
     let opts = parseExtra(opt_extraInfoSpec, ["blocking"]);
     opts.filter = parseFilter(filter);
     ContentPolicyManager.addListener(callback, opts);
   },
 
   removeListener(callback) {
     ContentPolicyManager.removeListener(callback);
-  }
+  },
 };
 
 var onBeforeSendHeaders = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     let opts = parseExtra(opt_extraInfoSpec, ["requestHeaders", "blocking"]);
     opts.filter = parseFilter(filter);
     HttpObserverManager.addListener("modify", callback, opts);
   },
 
   removeListener(callback) {
     HttpObserverManager.removeListener("modify", callback);
-  }
+  },
 };
 
 var onSendHeaders = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     let opts = parseExtra(opt_extraInfoSpec, ["requestHeaders"]);
     opts.filter = parseFilter(filter);
     HttpObserverManager.addListener("afterModify", callback, opts);
   },
 
   removeListener(callback) {
     HttpObserverManager.removeListener("afterModify", callback);
-  }
+  },
 };
 
 var onHeadersReceived = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     let opts = parseExtra(opt_extraInfoSpec, ["blocking", "responseHeaders"]);
     opts.filter = parseFilter(filter);
     HttpObserverManager.addListener("headersReceived", callback, opts);
   },
 
   removeListener(callback) {
     HttpObserverManager.removeListener("headersReceived", callback);
-  }
+  },
 };
 
 var onResponseStarted = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     let opts = parseExtra(opt_extraInfoSpec, ["responseHeaders"]);
     opts.filter = parseFilter(filter);
     HttpObserverManager.addListener("onStart", callback, opts);
   },
 
   removeListener(callback) {
     HttpObserverManager.removeListener("onStart", callback);
-  }
+  },
 };
 
 var onCompleted = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     let opts = parseExtra(opt_extraInfoSpec, ["responseHeaders"]);
     opts.filter = parseFilter(filter);
     HttpObserverManager.addListener("onStop", callback, opts);
   },
 
   removeListener(callback) {
     HttpObserverManager.removeListener("onStop", callback);
-  }
+  },
 };
 
 var WebRequest = {
   // Handled via content policy.
   onBeforeRequest: onBeforeRequest,
 
   // http-on-modify observer.
   onBeforeSendHeaders: onBeforeSendHeaders,
--- a/toolkit/modules/addons/WebRequestCommon.jsm
+++ b/toolkit/modules/addons/WebRequestCommon.jsm
@@ -1,14 +1,18 @@
 /* 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";
+
 const EXPORTED_SYMBOLS = ["WebRequestCommon"];
 
+/* exported WebRequestCommon */
+
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 var WebRequestCommon = {
   typeForPolicyType(type) {
     switch (type) {
@@ -44,10 +48,10 @@ var WebRequestCommon = {
   },
 
   urlMatches(uri, urlFilter) {
     if (urlFilter === null) {
       return true;
     }
 
     return urlFilter.matches(uri);
-  }
+  },
 };
--- a/toolkit/modules/addons/WebRequestContent.js
+++ b/toolkit/modules/addons/WebRequestContent.js
@@ -48,26 +48,26 @@ var ContentPolicy = {
     if (filter.urls) {
       filter.urls = new MatchPattern(filter.urls);
     }
     this.contentPolicies.set(id, {blocking, filter});
   },
 
   receiveMessage(msg) {
     switch (msg.name) {
-    case "WebRequest:AddContentPolicy":
-      this.addContentPolicy(msg.data);
-      break;
+      case "WebRequest:AddContentPolicy":
+        this.addContentPolicy(msg.data);
+        break;
 
-    case "WebRequest:RemoveContentPolicy":
-      this.contentPolicies.delete(msg.data.id);
-      if (this.contentPolicies.size == 0) {
-        this.unregister();
-      }
-      break;
+      case "WebRequest:RemoveContentPolicy":
+        this.contentPolicies.delete(msg.data.id);
+        if (this.contentPolicies.size == 0) {
+          this.unregister();
+        }
+        break;
     }
   },
 
   register() {
     let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
     catMan.addCategoryEntry("content-policy", this._contractID, this._contractID, false, true);
   },
 
@@ -77,18 +77,17 @@ var ContentPolicy = {
   },
 
   shouldLoad(policyType, contentLocation, requestOrigin,
              node, mimeTypeGuess, extra, requestPrincipal) {
     let block = false;
     let ids = [];
     for (let [id, {blocking, filter}] of this.contentPolicies.entries()) {
       if (WebRequestCommon.typeMatches(policyType, filter.types) &&
-          WebRequestCommon.urlMatches(contentLocation, filter.urls))
-      {
+          WebRequestCommon.urlMatches(contentLocation, filter.urls)) {
         if (blocking) {
           block = true;
         }
         ids.push(id);
       }
     }
 
     if (!ids.length) {
@@ -101,18 +100,17 @@ var ContentPolicy = {
 
     function getWindowId(window) {
       return window.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils)
         .outerWindowID;
     }
 
     if (policyType == Ci.nsIContentPolicy.TYPE_SUBDOCUMENT ||
-       (node instanceof Ci.nsIDOMXULElement && node.localName == "browser"))
-    {
+        (node instanceof Ci.nsIDOMXULElement && node.localName == "browser")) {
       // Chrome sets frameId to the ID of the sub-window. But when
       // Firefox loads an iframe, it sets |node| to the <iframe>
       // element, whose window is the parent window. We adopt the
       // Chrome behavior here.
       node = node.contentWindow;
     }
 
     if (node) {