Bug 1333044 - Enable no-undef eslint rule for services/. r?markh draft
authorMark Banner <standard8@mozilla.com>
Mon, 23 Jan 2017 15:15:05 +0000
changeset 467901 0c60b48eabb4ee92693a6aeec89e93e0df78ccd4
parent 467900 a9379944828b3eabea31ec9d8813d012af1f7e0c
child 543796 b9cc6a09df089aac9dbc817e9ccbadf3a587ceb7
push id43297
push userbmo:standard8@mozilla.com
push dateMon, 30 Jan 2017 09:37:19 +0000
reviewersmarkh
bugs1333044
milestone54.0a1
Bug 1333044 - Enable no-undef eslint rule for services/. r?markh MozReview-Commit-ID: IrtWclENDth
services/.eslintrc.js
services/common/modules-testing/storageserver.js
services/common/tests/run_storage_server.js
services/common/tests/unit/test_blocklist_certificates.js
services/common/tests/unit/test_hawkclient.js
services/common/tests/unit/test_restrequest.js
services/common/tests/unit/test_storage_adapter.js
services/common/utils.js
services/crypto/modules/utils.js
services/crypto/tests/unit/head_helpers.js
services/crypto/tests/unit/test_jwcrypto.js
services/fxaccounts/tests/xpcshell/test_accounts.js
services/fxaccounts/tests/xpcshell/test_client.js
services/sync/modules/rest.js
services/sync/modules/service.js
services/sync/modules/stages/enginesync.js
services/sync/tests/unit/fake_login_manager.js
services/sync/tests/unit/head_errorhandler_common.js
services/sync/tests/unit/head_helpers.js
services/sync/tests/unit/head_http_server.js
services/sync/tests/unit/test_bookmark_livemarks.js
services/sync/tests/unit/test_bookmark_smart_bookmarks.js
services/sync/tests/unit/test_bookmark_tracker.js
services/sync/tests/unit/test_forms_tracker.js
services/sync/tests/unit/test_fxa_service_cluster.js
services/sync/tests/unit/test_hmac_error.js
services/sync/tests/unit/test_resource.js
services/sync/tests/unit/test_syncscheduler.js
services/sync/tests/unit/test_syncstoragerequest.js
services/sync/tests/unit/xpcshell.ini
services/sync/tps/extensions/tps/components/tps-cmdline.js
services/sync/tps/extensions/tps/resource/modules/bookmarks.jsm
services/sync/tps/extensions/tps/resource/tps.jsm
testing/mochitest/chrome.eslintrc.js
tools/lint/eslint/modules.json
--- a/services/.eslintrc.js
+++ b/services/.eslintrc.js
@@ -6,10 +6,11 @@ module.exports = {
   ],
   rules: {
     /* These rules are only set to warn temporarily
        until they get fixed, at which point their
        respective line in this file should be removed. */
     "consistent-return": "warn",
     "no-func-assign": "warn",
     "no-nested-ternary": "warn",
+    "no-undef": "error",
   }
 };
--- a/services/common/modules-testing/storageserver.js
+++ b/services/common/modules-testing/storageserver.js
@@ -923,22 +923,22 @@ StorageServer.prototype = {
     try {
       this.server.start(this.port);
       this.port = this.server.identity.primaryPort;
       this.started = true;
       if (cb) {
         cb();
       }
     } catch (ex) {
-      _("==========================================");
-      _("Got exception starting Storage HTTP server on port " + this.port);
-      _("Error: " + Log.exceptionStr(ex));
-      _("Is there a process already listening on port " + this.port + "?");
-      _("==========================================");
-      do_throw(ex);
+      this._log.error("==========================================");
+      this._log.error("Got exception starting Storage HTTP server on port " + this.port);
+      this._log.error("Error: " + Log.exceptionStr(ex));
+      this._log.error("Is there a process already listening on port " + this.port + "?");
+      this._log.error("==========================================");
+      throw ex;
     }
   },
 
   /**
    * Start the server synchronously.
    *
    * @param port
    *        The numeric port on which to start. The default is to choose
--- a/services/common/tests/run_storage_server.js
+++ b/services/common/tests/run_storage_server.js
@@ -7,16 +7,20 @@
  *
  * It is meant to be executed with an xpcshell.
  *
  * The Makefile in this directory contains a target to run it:
  *
  *   $ make storage-server
  */
 
+// Disable eslint no-undef rule for this file, as it is simple and is complicated
+// to check all the imports.
+/* eslint no-undef:off */
+
 Cu.import("resource://testing-common/services/common/storageserver.js");
 
 initTestLogging();
 
 var server = new StorageServer();
 server.allowAllUsers = true;
 server.startSynchronous(SERVER_PORT);
 _("Storage server started on port " + SERVER_PORT);
--- a/services/common/tests/unit/test_blocklist_certificates.js
+++ b/services/common/tests/unit/test_blocklist_certificates.js
@@ -70,17 +70,17 @@ add_task(function* test_something() {
   // Our test data has a single record; it should be in the local collection
   let sqliteHandle = yield FirefoxAdapter.openConnection({path: kintoFilename});
   let collection = do_get_kinto_collection("certificates", sqliteHandle);
   let list = yield collection.list();
   do_check_eq(list.data.length, 1);
   yield sqliteHandle.close();
 
   // Test the db is updated when we call again with a later lastModified value
-  result = yield OneCRLBlocklistClient.maybeSync(4000, Date.now());
+  yield OneCRLBlocklistClient.maybeSync(4000, Date.now());
 
   // Open the collection, verify it's been updated:
   // Our test data now has two records; both should be in the local collection
   sqliteHandle = yield FirefoxAdapter.openConnection({path: kintoFilename});
   collection = do_get_kinto_collection("certificates", sqliteHandle);
   list = yield collection.list();
   do_check_eq(list.data.length, 3);
   yield sqliteHandle.close();
--- a/services/common/tests/unit/test_hawkclient.js
+++ b/services/common/tests/unit/test_hawkclient.js
@@ -379,17 +379,17 @@ add_task(function* test_500_no_retry() {
   let credentials = {
     id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x",
     key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=",
     algorithm: "sha256"
   };
   let method = "GET";
 
   let server = httpd_setup({
-    "/no-shutup": function() {
+    "/no-shutup": function(request, response) {
       let message = "Cannot get ye flask.";
       response.setStatusLine(request.httpVersion, 500, "Internal server error");
       response.bodyOutputStream.write(message, message.length);
     }
   });
 
   let client = new HawkClient(server.baseURI);
 
--- a/services/common/tests/unit/test_restrequest.js
+++ b/services/common/tests/unit/test_restrequest.js
@@ -767,17 +767,17 @@ add_test(function test_timeout() {
  * An exception thrown in 'onProgress' propagates to the 'onComplete' handler.
  */
 add_test(function test_exception_in_onProgress() {
   let handler = httpd_handler(200, "OK", "Foobar");
   let server = httpd_setup({"/resource": handler});
 
   let request = new RESTRequest(server.baseURI + "/resource");
   request.onProgress = function onProgress() {
-    it.does.not.exist();
+    it.does.not.exist(); // eslint-disable-line no-undef
   };
   request.get(function onComplete(error) {
     do_check_eq(error, "ReferenceError: it is not defined");
     do_check_eq(this.status, this.ABORTED);
 
     server.stop(run_next_test);
   });
 });
--- a/services/common/tests/unit/test_storage_adapter.js
+++ b/services/common/tests/unit/test_storage_adapter.js
@@ -122,17 +122,17 @@ function test_collection_operations() {
       yield adapter.execute((transaction) => {
         transaction.create(record);
         throw new Error("unexpected");
       });
     } catch (e) {
       error = e;
     }
     do_check_neq(error, null);
-    records = yield adapter.list();
+    let records = yield adapter.list();
     do_check_eq(records.length, 0);
     yield sqliteHandle.close();
   });
 
   // test save and get last modified
   add_task(function* test_kinto_last_modified() {
     const initialValue = 0;
     const intendedValue = 12345678;
@@ -174,17 +174,17 @@ function test_collection_operations() {
     deepEqual(record2, newRecord2);
     yield sqliteHandle.close();
   });
 
   add_task(function* test_kinto_import_records_should_override_existing() {
     let sqliteHandle = yield do_get_kinto_connection();
     let adapter = do_get_kinto_adapter(sqliteHandle);
     yield adapter.clear();
-    records = yield adapter.list();
+    let records = yield adapter.list();
     do_check_eq(records.length, 0);
     let impactedRecords = yield adapter.loadDump([
       {id: 1, foo: "bar"},
       {id: 2, foo: "baz"},
     ]);
     do_check_eq(impactedRecords.length, 2);
     yield adapter.loadDump([
       {id: 1, foo: "baz"},
--- a/services/common/utils.js
+++ b/services/common/utils.js
@@ -127,30 +127,16 @@ this.CommonUtils = {
    */
   laterTickResolvingPromise(value, prototype) {
     let deferred = Promise.defer(prototype);
     this.nextTick(deferred.resolve.bind(deferred, value));
     return deferred.promise;
   },
 
   /**
-   * Spin the event loop and return once the next tick is executed.
-   *
-   * This is an evil function and should not be used in production code. It
-   * exists in this module for ease-of-use.
-   */
-  waitForNextTick: function waitForNextTick() {
-    let cb = Async.makeSyncCallback();
-    this.nextTick(cb);
-    Async.waitForSyncCallback(cb);
-
-
-  },
-
-  /**
    * Return a timer that is scheduled to call the callback after waiting the
    * provided time or as soon as possible. The timer will be set as a property
    * of the provided object with the given timer name.
    */
   namedTimer: function namedTimer(callback, wait, thisObj, name) {
     if (!thisObj || !name) {
       throw "You must provide both an object and a property name for the timer!";
     }
--- a/services/crypto/modules/utils.js
+++ b/services/crypto/modules/utils.js
@@ -4,17 +4,16 @@
 
 var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 this.EXPORTED_SYMBOLS = ["CryptoUtils"];
 
 Cu.import("resource://services-common/observers.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://services-sync/constants.js");
 
 this.CryptoUtils = {
   xor: function xor(a, b) {
     let bytes = [];
 
     if (a.length != b.length) {
       throw new Error("can't xor unequal length strings: " + a.length + " vs " + b.length);
     }
@@ -182,19 +181,18 @@ this.CryptoUtils = {
    *
    * The output is an octet string of length dkLen, which you
    * can encode as you wish.
    */
   pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen,
                        hmacAlg = Ci.nsICryptoHMAC.SHA1, hmacLen = 20) {
 
     // We don't have a default in the algo itself, as NSS does.
-    // Use the constant.
     if (!dkLen) {
-      dkLen = SYNC_KEY_DECODED_LENGTH;
+      throw new Error("dkLen should be defined");
     }
 
     function F(S, c, i, h) {
 
       function XOR(a, b, isA) {
         if (a.length != b.length) {
           return false;
         }
--- a/services/crypto/tests/unit/head_helpers.js
+++ b/services/crypto/tests/unit/head_helpers.js
@@ -42,17 +42,17 @@ function base64UrlDecode(s) {
       break; // No pad chars in this case
     case 2:
       s += "==";
       break; // Two pad chars
     case 3:
       s += "=";
       break; // One pad char
     default:
-      throw new InputException("Illegal base64url string!");
+      throw new Error("Illegal base64url string!");
   }
 
   // With correct padding restored, apply the standard base64 decoder
   return atob(s);
 }
 
 // Register resource alias. Normally done in SyncComponents.manifest.
 function addResourceAlias() {
--- a/services/crypto/tests/unit/test_jwcrypto.js
+++ b/services/crypto/tests/unit/test_jwcrypto.js
@@ -57,17 +57,17 @@ function test_get_assertion() {
       });
     });
 }
 
 function test_rsa() {
   do_test_pending();
   function checkRSA(err, kpo) {
     do_check_neq(kpo, undefined);
-    log(kpo.serializedPublicKey);
+    do_print(kpo.serializedPublicKey);
     let pk = JSON.parse(kpo.serializedPublicKey);
     do_check_eq(pk.algorithm, "RS");
 /* TODO
     do_check_neq(kpo.sign, null);
     do_check_eq(typeof kpo.sign, "function");
     do_check_neq(kpo.userID, null);
     do_check_neq(kpo.url, null);
     do_check_eq(kpo.url, INTERNAL_ORIGIN);
@@ -87,17 +87,17 @@ function test_rsa() {
 
   jwcrypto.generateKeyPair("RS256", checkRSA);
 }
 
 function test_dsa() {
   do_test_pending();
   function checkDSA(err, kpo) {
     do_check_neq(kpo, undefined);
-    log(kpo.serializedPublicKey);
+    do_print(kpo.serializedPublicKey);
     let pk = JSON.parse(kpo.serializedPublicKey);
     do_check_eq(pk.algorithm, "DS");
 /* TODO
     do_check_neq(kpo.sign, null);
     do_check_eq(typeof kpo.sign, "function");
     do_check_neq(kpo.userID, null);
     do_check_neq(kpo.url, null);
     do_check_eq(kpo.url, INTERNAL_ORIGIN);
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts.js
@@ -204,17 +204,17 @@ add_task(function* test_non_https_remote
   Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
   Services.prefs.clearUserPref("identity.fxaccounts.allowHttp");
 });
 
 add_task(function* test_non_https_remote_server_uri() {
   Services.prefs.setCharPref(
     "identity.fxaccounts.remote.signup.uri",
     "http://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
-  rejects(fxAccounts.promiseAccountsSignUpURI(), null, "Firefox Accounts server must use HTTPS");
+  Assert.rejects(fxAccounts.promiseAccountsSignUpURI(), null, "Firefox Accounts server must use HTTPS");
   Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
 });
 
 add_task(function* test_get_signed_in_user_initially_unset() {
   _("Check getSignedInUser initially and after signout reports no user");
   let account = MakeFxAccounts();
   let credentials = {
     email: "foo@example.com",
--- a/services/fxaccounts/tests/xpcshell/test_client.js
+++ b/services/fxaccounts/tests/xpcshell/test_client.js
@@ -737,16 +737,17 @@ add_task(function* test_updateDevice() {
   }
 
   yield deferredStop(server);
 });
 
 add_task(function* test_signOutAndDestroyDevice() {
   const DEVICE_ID = "device id";
   const ERROR_ID = "test that the client promise rejects";
+  let emptyMessage = "{}";
 
   const server = httpd_setup({
     "/account/device/destroy": function(request, response) {
       const body = JSON.parse(CommonUtils.readBytesFromInputStream(request.bodyInputStream));
 
       if (!body.id) {
         response.setStatusLine(request.httpVersion, 400, "Invalid request");
         return response.bodyOutputStream.write(emptyMessage, emptyMessage.length);
@@ -830,17 +831,17 @@ add_task(function* test_client_metrics()
           errno: 111,
         });
       },
     }
   );
 
   let client = new FxAccountsClient(server.baseURI);
 
-  yield rejects(client.signOut(FAKE_SESSION_TOKEN, {
+  yield Assert.rejects(client.signOut(FAKE_SESSION_TOKEN, {
     service: "sync",
   }), function(err) {
     return err.errno == 111;
   });
 
   yield deferredStop(server);
 });
 
--- a/services/sync/modules/rest.js
+++ b/services/sync/modules/rest.js
@@ -37,17 +37,17 @@ SyncStorageRequest.prototype = {
     if (Svc.Prefs.get("sendVersionInfo", true)) {
       this.setHeader("user-agent", Utils.userAgent);
     }
 
     if (this.authenticator) {
       let result = this.authenticator(this, method);
       if (result && result.headers) {
         for (let [k, v] of Object.entries(result.headers)) {
-          setHeader(k, v);
+          this.setHeader(k, v);
         }
       }
     } else {
       this._log.debug("No authenticator found.");
     }
 
     return RESTRequest.prototype.dispatch.apply(this, arguments);
   },
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -1397,9 +1397,9 @@ Sync11Service.prototype = {
   },
 
   recordTelemetryEvent(object, method, value, extra = undefined) {
     Svc.Obs.notify("weave:telemetry:event", { object, method, value, extra });
   },
 };
 
 this.Service = new Sync11Service();
-Service.onStartup();
+this.Service.onStartup();
--- a/services/sync/modules/stages/enginesync.js
+++ b/services/sync/modules/stages/enginesync.js
@@ -255,17 +255,17 @@ EngineSynchronizer.prototype = {
       maxRecords = 2;
     } else {
 
       let collectionCountsURL = this.service.userBaseURL + "info/collection_counts";
       try {
         let infoResp = this.service._fetchInfo(collectionCountsURL);
         if (!infoResp.success) {
           this._log.error("Can't run validation: request to info/collection_counts responded with "
-                          + resp.status);
+                          + infoResp.status);
           return;
         }
         info = infoResp.obj; // might throw because obj is a getter which parses json.
       } catch (e) {
         // Not running validation is totally fine, so we just write an error log and return.
         this._log.error("Can't run validation: Caught error when fetching counts", e);
         return;
       }
deleted file mode 100644
--- a/services/sync/tests/unit/fake_login_manager.js
+++ /dev/null
@@ -1,38 +0,0 @@
-Cu.import("resource://services-sync/util.js");
-
-// ----------------------------------------
-// Fake Sample Data
-// ----------------------------------------
-
-var fakeSampleLogins = [
-  // Fake nsILoginInfo object.
-  {hostname: "www.boogle.com",
-   formSubmitURL: "http://www.boogle.com/search",
-   httpRealm: "",
-   username: "",
-   password: "",
-   usernameField: "test_person",
-   passwordField: "test_password"}
-];
-
-// ----------------------------------------
-// Fake Login Manager
-// ----------------------------------------
-
-function FakeLoginManager(fakeLogins) {
-  this.fakeLogins = fakeLogins;
-
-  let self = this;
-
-  // Use a fake nsILoginManager object.
-  delete Services.logins;
-  Services.logins = {
-      removeAllLogins() { self.fakeLogins = []; },
-      getAllLogins() { return self.fakeLogins; },
-      addLogin(login) {
-        getTestLogger().info("nsILoginManager.addLogin() called " +
-                             "with hostname '" + login.hostname + "'.");
-        self.fakeLogins.push(login);
-      }
-  };
-}
--- a/services/sync/tests/unit/head_errorhandler_common.js
+++ b/services/sync/tests/unit/head_errorhandler_common.js
@@ -1,16 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* import-globals-from head_appinfo.js */
 /* import-globals-from ../../../common/tests/unit/head_helpers.js */
+/* import-globals-from head_helpers.js */
 /* import-globals-from head_http_server.js */
 
+// This file expects Service to be defined in the global scope when EHTestsCommon
+// is used (from service.js).
+/* global Service */
+
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/keys.js");
 
 // Common code for test_errorhandler_{1,2}.js -- pulled out to make it less
 // monolithic and take less time to execute.
 const EHTestsCommon = {
 
   service_unavailable(request, response) {
     let body = "Service Unavailable";
     response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -1,14 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* import-globals-from head_appinfo.js */
 /* import-globals-from ../../../common/tests/unit/head_helpers.js */
 
+// From head_http_server.js (which also imports this file).
+/* global new_timestamp */
+
+// This file expects Service to be defined in the global scope when EHTestsCommon
+// is used (from service.js).
+/* global Service */
+
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://testing-common/services/common/utils.js");
 Cu.import("resource://testing-common/PlacesTestUtils.jsm");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "SyncPingSchema", function() {
   let ns = {};
@@ -70,32 +77,34 @@ function ExtensionsTestPath(path) {
  * This should be called in the global scope of any test file needing to
  * interface with the AddonManager. It should only be called once, or the
  * universe will end.
  */
 function loadAddonTestFunctions() {
   const path = ExtensionsTestPath("/head_addons.js");
   let file = do_get_file(path);
   let uri = Services.io.newFileURI(file);
+  /* import-globals-from ../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js */
   Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 }
 
 function webExtensionsTestPath(path) {
   if (path[0] != "/") {
     throw Error("Path must begin with '/': " + path);
   }
 
   return "../../../../toolkit/components/extensions/test/xpcshell" + path;
 }
 
 /**
  * Loads the WebExtension test functions by importing its test file.
  */
 function loadWebExtensionTestFunctions() {
+  /* import-globals-from ../../../../toolkit/components/extensions/test/xpcshell/head_sync.js */
   const path = webExtensionsTestPath("/head_sync.js");
   let file = do_get_file(path);
   let uri = Services.io.newFileURI(file);
   Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
 }
 
 function getAddonInstall(name) {
   let f = do_get_file(ExtensionsTestPath("/addons/" + name + ".xpi"));
--- a/services/sync/tests/unit/head_http_server.js
+++ b/services/sync/tests/unit/head_http_server.js
@@ -1,10 +1,11 @@
 /* import-globals-from head_appinfo.js */
 /* import-globals-from ../../../common/tests/unit/head_helpers.js */
+/* import-globals-from head_helpers.js */
 
 var Cm = Components.manager;
 
 // Shared logging for all HTTP server functions.
 Cu.import("resource://gre/modules/Log.jsm");
 const SYNC_HTTP_LOGGER = "Sync.Test.Server";
 const SYNC_API_VERSION = "1.1";
 
--- a/services/sync/tests/unit/test_bookmark_livemarks.js
+++ b/services/sync/tests/unit/test_bookmark_livemarks.js
@@ -105,17 +105,17 @@ add_test(function test_livemark_invalid(
 
   _("Parent is unknown. Will be set to unfiled.");
   let lateParentRec = makeLivemark(record631361.payload, true);
   let parentGUID = Utils.makeGUID();
   lateParentRec.parentid = parentGUID;
   do_check_eq(-1, store.idForGUID(parentGUID));
 
   store.create(lateParentRec);
-  recID = store.idForGUID(lateParentRec.id, true);
+  let recID = store.idForGUID(lateParentRec.id, true);
   do_check_true(recID > 0);
   do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(recID),
               PlacesUtils.bookmarks.unfiledBookmarksFolder);
 
   _("No feed URI, which is invalid. Will be skipped.");
   let noFeedURIRec = makeLivemark(record631361.payload, true);
   delete noFeedURIRec.cleartext.feedUri;
   store.create(noFeedURIRec);
--- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
+++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
@@ -39,17 +39,16 @@ function smartBookmarkCount() {
 }
 
 function clearBookmarks() {
   _("Cleaning up existing items.");
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.bookmarksMenuFolder);
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.tagsFolder);
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.toolbarFolder);
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.unfiledBookmarksFolder);
-  startCount = smartBookmarkCount();
 }
 
 function serverForFoo(engineData) {
   return serverForUsers({"foo": "password"}, {
     meta: {global: {engines: {bookmarks: {version: engineData.version,
                                           syncID: engineData.syncID}}}},
     bookmarks: {}
   });
--- a/services/sync/tests/unit/test_bookmark_tracker.js
+++ b/services/sync/tests/unit/test_bookmark_tracker.js
@@ -92,47 +92,16 @@ async function dumpBookmarks() {
                                     }
     ).then(() => {
       dump("All bookmarks:\n");
       dump(JSON.stringify(all, undefined, 2));
     });
   })
 }
 
-var populateTree = async function populate(parentId, ...items) {
-  let guids = {};
-  for (let item of items) {
-    let itemId;
-    switch (item.type) {
-      case PlacesUtils.bookmarks.TYPE_BOOKMARK:
-        itemId = PlacesUtils.bookmarks.insertBookmark(parentId,
-          Utils.makeURI(item.url),
-          PlacesUtils.bookmarks.DEFAULT_INDEX, item.title);
-        break;
-
-      case PlacesUtils.bookmarks.TYPE_FOLDER: {
-        itemId = PlacesUtils.bookmarks.createFolder(parentId,
-          item.title, PlacesUtils.bookmarks.DEFAULT_INDEX);
-        Object.assign(guids, await populate(itemId, ...item.children));
-        break;
-      }
-
-      default:
-        throw new Error(`Unsupported item type: ${item.type}`);
-    }
-    if (item.exclude) {
-      PlacesUtils.annotations.setItemAnnotation(
-        itemId, BookmarkAnnos.EXCLUDEBACKUP_ANNO, "Don't back this up", 0,
-        PlacesUtils.annotations.EXPIRE_NEVER);
-    }
-    guids[item.title] = await PlacesUtils.promiseItemGuid(itemId);
-  }
-  return guids;
-}
-
 async function insertBookmarksToMigrate() {
   await PlacesUtils.bookmarks.insert({
     guid: "0gtWTOgYcoJD",
     parentGuid: PlacesUtils.bookmarks.menuGuid,
     url: "https://mozilla.org",
   });
   let fxBmk = await PlacesUtils.bookmarks.insert({
     guid: "0dbpnMdxKxfg",
--- a/services/sync/tests/unit/test_forms_tracker.js
+++ b/services/sync/tests/unit/test_forms_tracker.js
@@ -15,17 +15,17 @@ function run_test() {
 
   do_check_empty(tracker.changedIDs);
   Log.repository.rootLogger.addAppender(new Log.DumpAppender());
 
   function addEntry(name, value) {
     engine._store.create({name, value});
   }
   function removeEntry(name, value) {
-    guid = engine._findDupe({name, value});
+    let guid = engine._findDupe({name, value});
     engine._store.remove({id: guid});
   }
 
   try {
     _("Create an entry. Won't show because we haven't started tracking yet");
     addEntry("name", "John Doe");
     do_check_empty(tracker.changedIDs);
 
--- a/services/sync/tests/unit/test_fxa_service_cluster.js
+++ b/services/sync/tests/unit/test_fxa_service_cluster.js
@@ -30,17 +30,17 @@ add_task(async function test_findCluster
     headers: {"content-type": "application/json"},
     body: "{}",
   });
 
   await Service.identity.initializeWithCurrentIdentity();
   await Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
                        "should reject due to 401");
 
-  cluster = Service._clusterManager._findCluster();
+  let cluster = Service._clusterManager._findCluster();
   Assert.strictEqual(cluster, null);
 
   _("_findCluster() works with correct tokenserver response.");
   let endpoint = "http://example.com/something";
   initializeIdentityWithTokenServerResponse({
     status: 200,
     headers: {"content-type": "application/json"},
     body:
--- a/services/sync/tests/unit/test_hmac_error.js
+++ b/services/sync/tests/unit/test_hmac_error.js
@@ -193,19 +193,19 @@ add_task(async function hmac_error_durin
 
   _("Make sure that syncing again causes recovery.");
   await new Promise(resolve => {
     onSyncFinished = function() {
       _("== First sync done.");
       _("---------------------------");
       onSyncFinished = function() {
         _("== Second (automatic) sync done.");
-        hasData = rotaryColl.wbo("flying") ||
-                  rotaryColl.wbo("scotsman");
-        hasKeys = keysWBO.modified;
+        let hasData = rotaryColl.wbo("flying") ||
+                      rotaryColl.wbo("scotsman");
+        let hasKeys = keysWBO.modified;
         do_check_true(!hasData == !hasKeys);
 
         // Kick off another sync. Can't just call it, because we're inside the
         // lock...
         Utils.nextTick(function() {
           _("Now a fresh sync will get no HMAC errors.");
           _("Partially resetting client, as if after a restart, and forcing redownload.");
           Service.collectionKeys.clear();
--- a/services/sync/tests/unit/test_resource.js
+++ b/services/sync/tests/unit/test_resource.js
@@ -445,17 +445,17 @@ function run_test() {
               server.baseURI + "/json");
 
   // And this is what happens if JS throws an exception.
   res18 = new Resource(server.baseURI + "/json");
   onProgress = function(rec) {
     throw "BOO!";
   };
   res18._onProgress = onProgress;
-  oldWarn = res18._log.warn;
+  let oldWarn = res18._log.warn;
   warnings = [];
   res18._log.warn = function(msg) { warnings.push(msg) };
   error = undefined;
   try {
     content = res18.get();
   } catch (ex) {
     error = ex;
   }
@@ -463,16 +463,17 @@ function run_test() {
   // It throws and logs.
   do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
   do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
   do_check_eq(warnings.pop(), "${action} request to ${url} failed: ${ex}");
   do_check_eq(warnings.pop(),
               "Got exception calling onProgress handler during fetch of " +
               server.baseURI + "/json");
 
+  res18._log.warn = oldWarn;
 
   _("Ensure channel timeouts are thrown appropriately.");
   let res19 = new Resource(server.baseURI + "/json");
   res19.ABORT_TIMEOUT = 0;
   error = undefined;
   try {
     content = res19.get();
   } catch (ex) {
--- a/services/sync/tests/unit/test_syncscheduler.js
+++ b/services/sync/tests/unit/test_syncscheduler.js
@@ -688,17 +688,17 @@ add_task(async function test_back_deboun
 });
 
 add_task(async function test_no_sync_node() {
   // Test when Status.sync == NO_SYNC_NODE_FOUND
   // it is not overwritten on sync:finish
   let server = sync_httpd_setup();
   await setUp(server);
 
-  oldfc = Service._clusterManager._findCluster;
+  let oldfc = Service._clusterManager._findCluster;
   Service._clusterManager._findCluster = () => null;
   Service.clusterURL = "";
   try {
     Service.sync();
     do_check_eq(Status.sync, NO_SYNC_NODE_FOUND);
     do_check_eq(scheduler.syncTimer.delay, NO_SYNC_NODE_INTERVAL);
 
     await cleanUpAndGo(server);
--- a/services/sync/tests/unit/test_syncstoragerequest.js
+++ b/services/sync/tests/unit/test_syncstoragerequest.js
@@ -168,16 +168,17 @@ add_test(function test_weave_quota_error
     do_check_eq(this.response.status, 400);
     do_check_eq(quotaValue, undefined);
     Svc.Obs.remove("weave:service:quota:remaining", onQuota);
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_abort() {
+  const TIMESTAMP = 1274380462;
   function handler(request, response) {
     response.setHeader("X-Weave-Timestamp", "" + TIMESTAMP, false);
     response.setHeader("X-Weave-Quota-Remaining", "1048576", false);
     response.setHeader("X-Weave-Backoff", "600", false);
     response.setStatusLine(request.httpVersion, 200, "OK");
   }
   let server = httpd_setup({"/resource": handler});
 
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -1,15 +1,14 @@
 [DEFAULT]
 head = head_appinfo.js ../../../common/tests/unit/head_helpers.js head_helpers.js head_http_server.js head_errorhandler_common.js
 firefox-appdir = browser
 support-files =
   addon1-search.xml
   bootstrap1-search.xml
-  fake_login_manager.js
   missing-sourceuri.xml
   missing-xpi-search.xml
   places_v10_from_v11.sqlite
   rewrite-search.xml
   sync_ping_schema.json
   systemaddon-search.xml
   !/services/common/tests/unit/head_helpers.js
   !/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
--- a/services/sync/tps/extensions/tps/components/tps-cmdline.js
+++ b/services/sync/tps/extensions/tps/components/tps-cmdline.js
@@ -109,17 +109,17 @@ var TPSCmdLineModule = {
                             "m-tps",
                             TPS_CMDLINE_CONTRACTID, true, true);
   },
 
   unregisterSelf(compMgr, fileSpec, location) {
     compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
 
     compMgr.unregisterFactoryLocation(TPS_CMDLINE_CLSID, fileSpec);
-    catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
+    let catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
     catman.deleteCategoryEntry("command-line-argument-handlers",
                                "TPS command line handler", true);
     catman.deleteCategoryEntry("command-line-handler",
                                "m-tps", true);
   },
 
   getClassObject(compMgr, cid, iid) {
     if (cid.equals(TPS_CMDLINE_CLSID)) {
--- a/services/sync/tps/extensions/tps/resource/modules/bookmarks.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/bookmarks.jsm
@@ -258,17 +258,17 @@ PlacesItem.prototype = {
    *
    * Locates the specified folder; if not found it is created.
    *
    * @param location The full path of the folder, which must begin
    *        with one of the bookmark root folders
    * @return the folder id if the folder was found or created, otherwise -1
    */
   GetOrCreateFolder(location) {
-    folder_id = this.GetFolder(location);
+    let folder_id = this.GetFolder(location);
     if (folder_id == -1)
       folder_id = this.CreateFolder(location);
     return folder_id;
   },
 
   /**
    * CheckDescription
    *
@@ -779,17 +779,17 @@ Livemark.prototype = {
                        feedURI: Services.io.newURI(this.props.feedUri),
                        index: PlacesUtils.bookmarks.DEFAULT_INDEX};
 
     // Until this can handle asynchronous creation, we need to spin.
     let spinningCb = Async.makeSpinningCallback();
 
     PlacesUtils.livemarks.addLivemark(livemarkObj).then(
       aLivemark => { spinningCb(null, [Components.results.NS_OK, aLivemark]) },
-      () => { spinningCb(null, [Components.results.NS_ERROR_UNEXPECTED, aLivemark]) }
+      () => { spinningCb(null, [Components.results.NS_ERROR_UNEXPECTED, null]) }
     );
 
     let [status, livemark] = spinningCb.wait();
     if (!Components.isSuccessCode(status)) {
       throw new Error(status);
     }
 
     this.props.item_id = livemark.id;
@@ -928,19 +928,19 @@ Separator.prototype = {
     }
     if (this.props.before == null && this.props.last_item_pos == null) {
       Logger.logPotentialError("Separator requires 'before' attribute if it's the" +
         "first item in the list");
       return -1;
     }
     let expected_pos = -1;
     if (this.props.before) {
-      other_id = this.GetPlacesNodeId(this.props.folder_id,
-                                      null,
-                                      this.props.before);
+      let other_id = this.GetPlacesNodeId(this.props.folder_id,
+                                          null,
+                                          this.props.before);
       if (other_id == -1) {
         Logger.logPotentialError("Can't find places item " + this.props.before +
           " for locating separator");
         return -1;
       }
       expected_pos = PlacesUtils.bookmarks.getItemIndex(other_id) - 1;
     } else {
       expected_pos = this.props.last_item_pos + 1;
--- a/services/sync/tps/extensions/tps/resource/tps.jsm
+++ b/services/sync/tps/extensions/tps/resource/tps.jsm
@@ -132,16 +132,18 @@ var TPS = {
     this.delayAutoSync();
 
     OBSERVER_TOPICS.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     // Configure some logging prefs for Sync itself.
     Weave.Svc.Prefs.set("log.appender.dump", "Debug");
+
+    /* global Authentication */
     Cu.import("resource://tps/auth/fxaccounts.jsm", module);
   },
 
   DumpError(msg, exc = null) {
     this._errors++;
     let errInfo;
     if (exc) {
       errInfo = Log.exceptionStr(exc); // includes details and stack-trace.
--- a/testing/mochitest/chrome.eslintrc.js
+++ b/testing/mochitest/chrome.eslintrc.js
@@ -1,27 +1,29 @@
 // Parent config file for all mochitest files.
 module.exports = {
   rules: {
     "mozilla/import-headjs-globals": "warn",
+    "mozilla/import-browserjs-globals": "warn",
     "mozilla/import-test-globals": "warn",
     "mozilla/mark-test-function-used": "warn",
   },
 
   "env": {
     "browser": true,
   },
 
   // All globals made available in the test environment.
   "globals": {
     // `$` is defined in SimpleTest.js
     "$": false,
     "add_task": false,
     "addLoadEvent": false,
     "Assert": false,
+    "BrowserTestUtils": false,
     "EventUtils": false,
     "executeSoon": false,
     "export_assertions": false,
     "finish": false,
     "getRootDirectory": false,
     "getTestFilePath": false,
     "gTestPath": false,
     "info": false,
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -65,16 +65,17 @@
   "engines.js": ["EngineManager", "Engine", "SyncEngine", "Tracker", "Store", "Changeset"],
   "enginesync.js": ["EngineSynchronizer"],
   "errors.js": ["BaseError", "ApplicationQuitError", "AssertionError", "TimeoutError"],
   "evaluate.js": ["evaluate", "sandbox", "Sandboxes"],
   "event-emitter.js": ["EventEmitter"],
   "EventUtils.js": ["disableNonTestMouseEvents", "sendMouseEvent", "sendChar", "sendString", "sendKey", "synthesizeMouse", "synthesizeTouch", "synthesizeMouseAtPoint", "synthesizeTouchAtPoint", "synthesizeMouseAtCenter", "synthesizeTouchAtCenter", "synthesizeWheel", "synthesizeKey", "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent", "synthesizeText", "synthesizeComposition", "synthesizeQuerySelectedText"],
   "Extension.jsm": ["Extension", "ExtensionData"],
   "ExtensionAPI.jsm": ["ExtensionAPI", "ExtensionAPIs"],
+  "extension-storage.js": ["ExtensionStorageEngine", "EncryptionRemoteTransformer", "KeyRingEncryptionRemoteTransformer"],
   "ExtensionXPCShellUtils.jsm": ["ExtensionTestUtils"],
   "fakeservices.js": ["FakeCryptoService", "FakeFilesystemService", "FakeGUIDService", "fakeSHA256HMAC"],
   "file_expandosharing.jsm": ["checkFromJSM"],
   "file_stringencoding.jsm": ["checkFromJSM"],
   "file_url.jsm": ["checkFromJSM"],
   "file_worker_url.jsm": ["checkFromJSM"],
   "Finder.jsm": ["Finder", "GetClipboardSearchString"],
   "FormAutofillContent.jsm": ["FormAutofillHandler"],
@@ -217,17 +218,17 @@
   "Timer.jsm": ["setTimeout", "clearTimeout", "setInterval", "clearInterval"],
   "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
   "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
   "tps.jsm": ["ACTIONS", "TPS"],
   "Translation.jsm": ["Translation", "TranslationTelemetry"],
   "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
   "UpdateTelemetry.jsm": ["AUSTLMY"],
   "util.js": ["getChromeWindow", "XPCOMUtils", "Services", "Utils", "Async", "Svc", "Str"],
-  "utils.js": ["applicationName", "assert", "Copy", "getBrowserObject", "getChromeWindow", "getWindows", "getWindowByTitle", "getWindowByType", "getWindowId", "getMethodInWindows", "getPreference", "saveDataURL", "setPreference", "sleep", "startTimer", "stopTimer", "takeScreenshot", "unwrapNode", "waitFor", "btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils"],
+  "utils.js": ["applicationName", "assert", "Copy", "getBrowserObject", "getChromeWindow", "getWindows", "getWindowByTitle", "getWindowByType", "getWindowId", "getMethodInWindows", "getPreference", "saveDataURL", "setPreference", "sleep", "startTimer", "stopTimer", "takeScreenshot", "unwrapNode", "waitFor", "btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer"],
   "Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache"],
   "VariablesView.jsm": ["VariablesView", "escapeHTML"],
   "VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
   "version.jsm": ["VERSION"],
   "vtt.jsm": ["WebVTT"],
   "WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
   "WindowDraggingUtils.jsm": ["WindowDraggingElement"],
   "windows.js": ["init", "map"],