Bug 1439777 p2 - Remove weave:ui:* related code. r=markh,tcsc
authorEdouard Oger <eoger@fastmail.com>
Thu, 22 Feb 2018 16:30:39 +0800
changeset 461834 157f93b585b18e810942d3fc421b8dc9327b4419
parent 461833 76c0ebfa6f087fec78b2be5f0ac102a59e42e96c
child 461835 60b1f8616b22e378c8b0575d9d62bc1ff6463fc6
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh, tcsc
bugs1439777
milestone60.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 1439777 p2 - Remove weave:ui:* related code. r=markh,tcsc MozReview-Commit-ID: D1H36YeiJCS
browser/base/content/browser-sync.js
browser/components/customizableui/test/browser_remote_tabs_button.js
services/sync/modules/constants.js
services/sync/modules/policies.js
services/sync/services-sync.js
services/sync/tests/unit/test_errorhandler_1.js
services/sync/tests/unit/test_errorhandler_2.js
services/sync/tests/unit/test_errorhandler_filelog.js
services/sync/tests/unit/test_fxa_node_reassignment.js
services/sync/tests/unit/test_node_reassignment.js
services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
tools/lint/eslint/modules.json
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -560,17 +560,17 @@ var gSync = {
   // via the various UI components.
   doSync() {
     if (!UIState.isReady()) {
       return;
     }
     const state = UIState.get();
     if (state.status == UIState.STATUS_SIGNED_IN) {
       this.updateSyncStatus({ syncing: true });
-      setTimeout(() => Weave.Service.errorHandler.syncAndReportErrors(), 0);
+      Services.tm.dispatchToMainThread(() => Weave.Service.sync());
     }
   },
 
   openPrefs(entryPoint = "syncbutton", origin = undefined) {
     window.openPreferences("paneSync", { origin, urlParams: { entrypoint: entryPoint } });
   },
 
   openSyncedTabsPanel() {
--- a/browser/components/customizableui/test/browser_remote_tabs_button.js
+++ b/browser/components/customizableui/test/browser_remote_tabs_button.js
@@ -64,25 +64,24 @@ add_task(async function asyncCleanup() {
 
 function mockFunctions() {
   // mock UIState.get()
   UIState.get = () => ({
     status: UIState.STATUS_SIGNED_IN,
     email: "user@mozilla.com"
   });
 
-  // mock service.errorHandler.syncAndReportErrors()
-  service.errorHandler.syncAndReportErrors = mocked_syncAndReportErrors;
+  service.sync = mocked_syncAndReportErrors;
 }
 
 function mocked_syncAndReportErrors() {
   syncWasCalled = true;
 }
 
 function restoreValues() {
   UIState.get = getState;
-  service.syncAndReportErrors = originalSync;
+  service.sync = originalSync;
 }
 
 function storeInitialValues() {
   getState = UIState.get;
-  originalSync = service.syncAndReportErrors;
+  originalSync = service.sync;
 }
--- a/services/sync/modules/constants.js
+++ b/services/sync/modules/constants.js
@@ -101,17 +101,16 @@ LOGIN_FAILED_LOGIN_REJECTED:           "
 
 // sync failure status codes
 METARECORD_DOWNLOAD_FAIL:              "error.sync.reason.metarecord_download_fail",
 VERSION_OUT_OF_DATE:                   "error.sync.reason.version_out_of_date",
 CREDENTIALS_CHANGED:                   "error.sync.reason.credentials_changed",
 ABORT_SYNC_COMMAND:                    "aborting sync, process commands said so",
 NO_SYNC_NODE_FOUND:                    "error.sync.reason.no_node_found",
 OVER_QUOTA:                            "error.sync.reason.over_quota",
-PROLONGED_SYNC_FAILURE:                "error.sync.prolonged_failure",
 SERVER_MAINTENANCE:                    "error.sync.reason.serverMaintenance",
 
 RESPONSE_OVER_QUOTA:                   "14",
 
 // engine failure status codes
 ENGINE_UPLOAD_FAIL:                    "error.engine.reason.record_upload_fail",
 ENGINE_DOWNLOAD_FAIL:                  "error.engine.reason.record_download_fail",
 ENGINE_UNKNOWN_FAIL:                   "error.engine.reason.unknown_fail",
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -649,29 +649,17 @@ SyncScheduler.prototype = {
 
 };
 
 function ErrorHandler(service) {
   this.service = service;
   this.init();
 }
 ErrorHandler.prototype = {
-  /**
-   * Flag that turns on error reporting for all errors, incl. network errors.
-   */
-  dontIgnoreErrors: false,
-
-  /**
-   * Flag that indicates if we have already reported a prolonged failure.
-   * Once set, we don't report it again, meaning this error is only reported
-   * one per run.
-   */
-  didReportProlongedError: false,
-
-  init: function init() {
+  init() {
     Svc.Obs.add("weave:engine:sync:applied", this);
     Svc.Obs.add("weave:engine:sync:error", this);
     Svc.Obs.add("weave:service:login:error", this);
     Svc.Obs.add("weave:service:sync:error", this);
     Svc.Obs.add("weave:service:sync:finish", this);
     Svc.Obs.add("weave:service:start-over:finish", this);
 
     this.initLogs();
@@ -687,17 +675,17 @@ ErrorHandler.prototype = {
 
     let logs = ["Sync", "Services.Common", "FirefoxAccounts", "Hawk",
                 "browserwindow.syncui", "BookmarkSyncUtils", "addons.xpi",
                ];
 
     this._logManager = new LogManager(Svc.Prefs, logs, "sync");
   },
 
-  observe: function observe(subject, topic, data) {
+  observe(subject, topic, data) {
     this._log.trace("Handling " + topic);
     switch (topic) {
       case "weave:engine:sync:applied":
         if (subject.newFailed) {
           // An engine isn't able to apply one or more incoming records.
           // We don't fail hard on this, but it usually indicates a bug,
           // so for now treat it as sync error (c.f. Service._syncEngine())
           Status.engines = [data, ENGINE_APPLY_FAIL];
@@ -718,24 +706,16 @@ ErrorHandler.prototype = {
           Services.telemetry.getKeyedHistogramById("WEAVE_ENGINE_SYNC_ERRORS")
                             .add(engine_name);
         }
         break;
       }
       case "weave:service:login:error":
         this._log.error("Sync encountered a login error");
         this.resetFileLog();
-
-        if (this.shouldReportError()) {
-          this.notifyOnNextTick("weave:ui:login:error");
-        } else {
-          this.notifyOnNextTick("weave:ui:clear-error");
-        }
-
-        this.dontIgnoreErrors = false;
         break;
       case "weave:service:sync:error": {
         if (Status.sync == CREDENTIALS_CHANGED) {
           this.service.logout();
         }
 
         let exception = subject;
         if (Async.isShutdownException(exception)) {
@@ -744,24 +724,16 @@ ErrorHandler.prototype = {
           this._log.error("Sync was interrupted due to the application shutting down");
           this.resetFileLog();
           break;
         }
 
         // Not a shutdown related exception...
         this._log.error("Sync encountered an error", exception);
         this.resetFileLog();
-
-        if (this.shouldReportError()) {
-          this.notifyOnNextTick("weave:ui:sync:error");
-        } else {
-          this.notifyOnNextTick("weave:ui:sync:finish");
-        }
-
-        this.dontIgnoreErrors = false;
         break;
       }
       case "weave:service:sync:finish":
         this._log.trace("Status.service is " + Status.service);
 
         // Check both of these status codes: in the event of a failure in one
         // engine, Status.service will be SYNC_FAILED_PARTIAL despite
         // Status.sync being SYNC_SUCCEEDED.
@@ -770,57 +742,26 @@ ErrorHandler.prototype = {
             Status.service == STATUS_OK) {
           // Great. Let's clear our mid-sync 401 note.
           this._log.trace("Clearing lastSyncReassigned.");
           Svc.Prefs.reset("lastSyncReassigned");
         }
 
         if (Status.service == SYNC_FAILED_PARTIAL) {
           this._log.error("Some engines did not sync correctly.");
-          this.resetFileLog();
-
-          if (this.shouldReportError()) {
-            this.dontIgnoreErrors = false;
-            this.notifyOnNextTick("weave:ui:sync:error");
-            break;
-          }
-        } else {
-          this.resetFileLog();
         }
-        this.dontIgnoreErrors = false;
-        this.notifyOnNextTick("weave:ui:sync:finish");
+        this.resetFileLog();
         break;
       case "weave:service:start-over:finish":
         // ensure we capture any logs between the last sync and the reset completing.
         this.resetFileLog();
         break;
     }
   },
 
-  notifyOnNextTick: function notifyOnNextTick(topic) {
-    CommonUtils.nextTick(function() {
-      this._log.trace("Notifying " + topic +
-                      ". Status.login is " + Status.login +
-                      ". Status.sync is " + Status.sync);
-      Svc.Obs.notify(topic);
-    }, this);
-  },
-
-  /**
-   * Trigger a sync and don't muffle any errors, particularly network errors.
-   */
-  syncAndReportErrors: function syncAndReportErrors() {
-    this._log.debug("Beginning user-triggered sync.");
-
-    this.dontIgnoreErrors = true;
-    CommonUtils.nextTick(() => {
-      this.service.sync({why: "user"});
-    }, this);
-  },
-
   async _dumpAddons() {
     // Just dump the items that sync may be concerned with. Specifically,
     // active extensions that are not hidden.
     let addons = [];
     try {
       addons = await AddonManager.getAddonsByTypes(["extension"]);
     } catch (e) {
       this._log.warn("Failed to dump addons", e);
@@ -832,118 +773,26 @@ ErrorHandler.prototype = {
       this._log.debug(" - ${name}, version ${version}, id ${id}", addon);
     }
   },
 
   /**
    * Generate a log file for the sync that just completed
    * and refresh the input & output streams.
    */
-  resetFileLog: function resetFileLog() {
-    let onComplete = logType => {
-      Svc.Obs.notify("weave:service:reset-file-log");
-      this._log.trace("Notified: " + Date.now());
-      if (logType == this._logManager.ERROR_LOG_WRITTEN) {
-        Cu.reportError("Sync encountered an error - see about:sync-log for the log file.");
-      }
-    };
-
+  async resetFileLog() {
     // If we're writing an error log, dump extensions that may be causing problems.
-    let beforeResetLog;
     if (this._logManager.sawError) {
-      beforeResetLog = this._dumpAddons();
-    } else {
-      beforeResetLog = Promise.resolve();
+      await this._dumpAddons();
     }
-    // Note we do not return the promise here - the caller doesn't need to wait
-    // for this to complete.
-    beforeResetLog
-      .then(() => this._logManager.resetFileLog())
-      .then(onComplete, onComplete);
-  },
-
-  /**
-   * Translates server error codes to meaningful strings.
-   *
-   * @param code
-   *        server error code as an integer
-   */
-  errorStr: function errorStr(code) {
-    switch (code.toString()) {
-    case "1":
-      return "illegal-method";
-    case "2":
-      return "invalid-captcha";
-    case "3":
-      return "invalid-username";
-    case "4":
-      return "cannot-overwrite-resource";
-    case "5":
-      return "userid-mismatch";
-    case "6":
-      return "json-parse-failure";
-    case "7":
-      return "invalid-password";
-    case "8":
-      return "invalid-record";
-    case "9":
-      return "weak-password";
-    default:
-      return "generic-server-error";
+    const logType = await this._logManager.resetFileLog();
+    if (logType == this._logManager.ERROR_LOG_WRITTEN) {
+      Cu.reportError("Sync encountered an error - see about:sync-log for the log file.");
     }
-  },
-
-  // A function to indicate if Sync errors should be "reported" - which in this
-  // context really means "should be notify observers of an error" - but note
-  // that since bug 1180587, no one is going to surface an error to the user.
-  shouldReportError: function shouldReportError() {
-    if (Status.login == MASTER_PASSWORD_LOCKED) {
-      this._log.trace("shouldReportError: false (master password locked).");
-      return false;
-    }
-
-    if (this.dontIgnoreErrors) {
-      return true;
-    }
-
-    if (Status.login == LOGIN_FAILED_LOGIN_REJECTED) {
-      // An explicit LOGIN_REJECTED state is always reported (bug 1081158)
-      this._log.trace("shouldReportError: true (login was rejected)");
-      return true;
-    }
-
-    let lastSync = Svc.Prefs.get("lastSync");
-    if (lastSync && ((Date.now() - Date.parse(lastSync)) >
-        Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) {
-      Status.sync = PROLONGED_SYNC_FAILURE;
-      if (this.didReportProlongedError) {
-        this._log.trace("shouldReportError: false (prolonged sync failure, but" +
-                        " we've already reported it).");
-        return false;
-      }
-      this._log.trace("shouldReportError: true (first prolonged sync failure).");
-      this.didReportProlongedError = true;
-      return true;
-    }
-
-    // We got a 401 mid-sync. Wait for the next sync before actually handling
-    // an error. This assumes that we'll get a 401 again on a login fetch in
-    // order to report the error.
-    if (!this.service.clusterURL) {
-      this._log.trace("shouldReportError: false (no cluster URL; " +
-                      "possible node reassignment).");
-      return false;
-    }
-
-
-    let result = (![Status.login, Status.sync].includes(SERVER_MAINTENANCE) &&
-                  ![Status.login, Status.sync].includes(LOGIN_FAILED_NETWORK_ERROR));
-    this._log.trace("shouldReportError: ${result} due to login=${login}, sync=${sync}",
-                    {result, login: Status.login, sync: Status.sync});
-    return result;
+    Svc.Obs.notify("weave:service:reset-file-log");
   },
 
   /**
    * Handle HTTP response results or exceptions and set the appropriate
    * Status.* bits.
    *
    * This method also looks for "side-channel" warnings.
    */
--- a/services/sync/services-sync.js
+++ b/services/sync/services-sync.js
@@ -9,18 +9,16 @@ pref("services.sync.sendVersionInfo", tr
 
 pref("services.sync.scheduler.idleInterval", 3600);  // 1 hour
 pref("services.sync.scheduler.activeInterval", 600);   // 10 minutes
 pref("services.sync.scheduler.immediateInterval", 90);    // 1.5 minutes
 pref("services.sync.scheduler.idleTime", 300);   // 5 minutes
 
 pref("services.sync.scheduler.fxa.singleDeviceInterval", 3600); // 1 hour
 
-pref("services.sync.errorhandler.networkFailureReportTimeout", 1209600); // 2 weeks
-
 // Note that new engines are typically added with a default of disabled, so
 // when an existing sync user gets the Firefox upgrade that supports the engine
 // it starts as disabled until the user has explicitly opted in.
 // The sync "create account" process typically *will* offer these engines, so
 // they may be flipped to enabled at that time.
 pref("services.sync.engine.addons", true);
 pref("services.sync.engine.addresses", false);
 pref("services.sync.engine.bookmarks", true);
--- a/services/sync/tests/unit/test_errorhandler_1.js
+++ b/services/sync/tests/unit/test_errorhandler_1.js
@@ -7,57 +7,39 @@ ChromeUtils.import("resource://services-
 ChromeUtils.import("resource://services-sync/keys.js");
 ChromeUtils.import("resource://services-sync/policies.js");
 ChromeUtils.import("resource://services-sync/service.js");
 ChromeUtils.import("resource://services-sync/status.js");
 ChromeUtils.import("resource://services-sync/util.js");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 
-var fakeServer = new SyncServer();
+const fakeServer = new SyncServer();
 fakeServer.start();
+const fakeServerUrl = "http://localhost:" + fakeServer.port;
 
 registerCleanupFunction(function() {
   return promiseStopServer(fakeServer).finally(() => {
     Svc.Prefs.resetBranch("");
   });
 });
 
-var fakeServerUrl = "http://localhost:" + fakeServer.port;
-
-const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
-
-const PROLONGED_ERROR_DURATION =
-  (Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 2) * 1000;
-
-const NON_PROLONGED_ERROR_DURATION =
-  (Svc.Prefs.get("errorhandler.networkFailureReportTimeout") / 2) * 1000;
-
-function setLastSync(lastSyncValue) {
-  Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
-}
-
-// This relies on Service/ErrorHandler being a singleton. Fixing this will take
-// a lot of work.
-let errorHandler = Service.errorHandler;
 let engine;
-
 add_task(async function setup() {
   await Service.engineManager.clear();
   await Service.engineManager.register(EHTestsCommon.CatapultEngine);
   engine = Service.engineManager.get("catapult");
 });
 
 async function clean() {
   let promiseLogReset = promiseOneObserver("weave:service:reset-file-log");
   await Service.startOver();
   await promiseLogReset;
   Status.resetSync();
   Status.resetBackoff();
-  errorHandler.didReportProlongedError = false;
   // Move log levels back to trace (startOver will have reversed this), sicne
   syncTestLogging();
 }
 
 add_task(async function test_401_logout() {
   enableValidationPrefs();
 
   let server = await EHTestsCommon.sync_httpd_setup();
@@ -125,525 +107,98 @@ add_task(async function test_credentials
   Assert.equal(Status.sync, CREDENTIALS_CHANGED);
   Assert.ok(!Service.isLoggedIn);
 
   // Clean up.
   await Service.startOver();
   await promiseStopServer(server);
 });
 
-add_task(function test_no_lastSync_pref() {
-  syncTestLogging();
-  // Test reported error.
-  Status.resetSync();
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = CREDENTIALS_CHANGED;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test unreported error.
-  Status.resetSync();
-  errorHandler.dontIgnoreErrors = true;
-  Status.login = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-
-});
-
-add_task(function test_shouldReportError() {
-  Status.login = MASTER_PASSWORD_LOCKED;
-  Assert.ok(!errorHandler.shouldReportError());
-
-  // Give ourselves a clusterURL so that the temporary 401 no-error situation
-  // doesn't come into play.
-  Service.clusterURL = fakeServerUrl;
-
-  // Test dontIgnoreErrors, non-network, non-prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = CREDENTIALS_CHANGED;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test dontIgnoreErrors, non-network, prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = CREDENTIALS_CHANGED;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test dontIgnoreErrors, network, non-prolonged, login error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.login = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test dontIgnoreErrors, network, non-prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test dontIgnoreErrors, network, prolonged, login error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.login = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test dontIgnoreErrors, network, prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-
-  // Test non-network, prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  errorHandler.didReportProlongedError = false;
-  Status.sync = CREDENTIALS_CHANGED;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(errorHandler.didReportProlongedError);
-  errorHandler.didReportProlongedError = false;
-
-  // Test network, prolonged, login error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.login = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(errorHandler.didReportProlongedError);
-  errorHandler.didReportProlongedError = false;
-
-  // Test network, prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.sync = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(errorHandler.didReportProlongedError);
-  errorHandler.didReportProlongedError = false;
-
-  // Test non-network, non-prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.sync = CREDENTIALS_CHANGED;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test network, non-prolonged, login error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.login = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(!errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test network, non-prolonged, sync error reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.sync = LOGIN_FAILED_NETWORK_ERROR;
-  Assert.ok(!errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test server maintenance, sync errors are not reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.sync = SERVER_MAINTENANCE;
-  Assert.ok(!errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test server maintenance, login errors are not reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.login = SERVER_MAINTENANCE;
-  Assert.ok(!errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test prolonged, server maintenance, sync errors are reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.sync = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(errorHandler.didReportProlongedError);
-  errorHandler.didReportProlongedError = false;
-
-  // Test prolonged, server maintenance, login errors are reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = false;
-  Status.login = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(errorHandler.didReportProlongedError);
-  errorHandler.didReportProlongedError = false;
-
-  // Test dontIgnoreErrors, server maintenance, sync errors are reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  // dontIgnoreErrors means we don't set didReportProlongedError
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test dontIgnoreErrors, server maintenance, login errors are reported
-  Status.resetSync();
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.login = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test dontIgnoreErrors, prolonged, server maintenance,
-  // sync errors are reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.sync = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  // Test dontIgnoreErrors, prolonged, server maintenance,
-  // login errors are reported
-  Status.resetSync();
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.dontIgnoreErrors = true;
-  Status.login = SERVER_MAINTENANCE;
-  Assert.ok(errorHandler.shouldReportError());
-  Assert.ok(!errorHandler.didReportProlongedError);
-});
-
-add_task(async function test_shouldReportError_master_password() {
-  _("Test error ignored due to locked master password");
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  // Monkey patch Service.verifyLogin to imitate
-  // master password being locked.
-  Service._verifyLogin = Service.verifyLogin;
-  Service.verifyLogin = async function() {
-    Status.login = MASTER_PASSWORD_LOCKED;
-    return false;
-  };
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await Service.sync();
-  Assert.ok(!errorHandler.shouldReportError());
-
-  // Clean up.
-  Service.verifyLogin = Service._verifyLogin;
-  await clean();
-  await promiseStopServer(server);
-});
-
-// Test that even if we don't have a cluster URL, a login failure due to
-// authentication errors is always reported.
-add_task(function test_shouldReportLoginFailureWithNoCluster() {
-  // Ensure no clusterURL - any error not specific to login should not be reported.
-  Service.clusterURL = "";
-
-  // Test explicit "login rejected" state.
-  Status.resetSync();
-  // If we have a LOGIN_REJECTED state, we always report the error.
-  Status.login = LOGIN_FAILED_LOGIN_REJECTED;
-  Assert.ok(errorHandler.shouldReportError());
-  // But any other status with a missing clusterURL is treated as a mid-sync
-  // 401 (ie, should be treated as a node reassignment)
-  Status.login = LOGIN_SUCCEEDED;
-  Assert.ok(!errorHandler.shouldReportError());
-});
-
-add_task(async function test_login_syncAndReportErrors_non_network_error() {
+add_task(async function test_login_non_network_error() {
   enableValidationPrefs();
 
   // Test non-network errors are reported
-  // when calling syncAndReportErrors
+  // when calling sync
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
   Service.identity._syncKeyBundle = null;
 
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseObserved;
+  await Service.sync();
   Assert.equal(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
 
   await clean();
   await promiseStopServer(server);
 });
 
-add_task(async function test_sync_syncAndReportErrors_non_network_error() {
+add_task(async function test_sync_non_network_error() {
   enableValidationPrefs();
 
   // Test non-network errors are reported
-  // when calling syncAndReportErrors
+  // when calling sync
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
 
   // By calling sync, we ensure we're logged in.
   await Service.sync();
   Assert.equal(Status.sync, SYNC_SUCCEEDED);
   Assert.ok(Service.isLoggedIn);
 
   await EHTestsCommon.generateCredentialsChangedFailure();
 
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  let ping = await wait_for_ping(() => errorHandler.syncAndReportErrors(), true);
+  let ping = await sync_and_validate_telem(true);
   equal(ping.status.sync, CREDENTIALS_CHANGED);
   deepEqual(ping.failureReason, {
     name: "unexpectederror",
     error: "Error: Aborting sync, remote setup failed"
   });
-  await promiseObserved;
-
-  Assert.equal(Status.sync, CREDENTIALS_CHANGED);
-  // If we clean this tick, telemetry won't get the right error
-  await Async.promiseYield();
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_login_syncAndReportErrors_prolonged_non_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, non-network errors are
-  // reported when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-  Service.identity._syncKeyBundle = null;
-
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseObserved;
-  Assert.equal(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_sync_syncAndReportErrors_prolonged_non_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, non-network errors are
-  // reported when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  // By calling sync, we ensure we're logged in.
-  await Service.sync();
-  Assert.equal(Status.sync, SYNC_SUCCEEDED);
-  Assert.ok(Service.isLoggedIn);
-
-  await EHTestsCommon.generateCredentialsChangedFailure();
-
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  let ping = await wait_for_ping(() => errorHandler.syncAndReportErrors(), true);
-  equal(ping.status.sync, CREDENTIALS_CHANGED);
-  deepEqual(ping.failureReason, {
-    name: "unexpectederror",
-    error: "Error: Aborting sync, remote setup failed"
-  });
-  await promiseObserved;
 
   Assert.equal(Status.sync, CREDENTIALS_CHANGED);
   // If we clean this tick, telemetry won't get the right error
   await Async.promiseYield();
   await clean();
   await promiseStopServer(server);
 });
 
-add_task(async function test_login_syncAndReportErrors_network_error() {
+add_task(async function test_login_sync_network_error() {
   enableValidationPrefs();
 
-  // Test network errors are reported when calling syncAndReportErrors.
+  // Test network errors are reported when calling sync.
   await configureIdentity({username: "broken.wipe"});
   Service.clusterURL = fakeServerUrl;
 
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseObserved;
-
+  await Service.sync();
   Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
   await clean();
 });
 
 
-add_task(async function test_sync_syncAndReportErrors_network_error() {
-  enableValidationPrefs();
-
-  // Test network errors are reported when calling syncAndReportErrors.
-  Services.io.offline = true;
-
-  let promiseUISyncError = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseUISyncError;
-  Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
-
-  Services.io.offline = false;
-  await clean();
-});
-
-add_task(async function test_login_syncAndReportErrors_prolonged_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, network errors are reported
-  // when calling syncAndReportErrors.
-  await configureIdentity({username: "johndoe"});
-
-  Service.clusterURL = fakeServerUrl;
-
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseObserved;
-  Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR);
-
-  await clean();
-});
-
-add_task(async function test_sync_syncAndReportErrors_prolonged_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, network errors are reported
-  // when calling syncAndReportErrors.
-  Services.io.offline = true;
-
-  let promiseUISyncError = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  errorHandler.syncAndReportErrors();
-  await promiseUISyncError;
-  Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
-
-  Services.io.offline = false;
-  await clean();
-});
-
-add_task(async function test_login_prolonged_non_network_error() {
+add_task(async function test_sync_network_error() {
   enableValidationPrefs();
 
-  // Test prolonged, non-network errors are reported
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-  Service.identity._syncKeyBundle = null;
-
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await Service.sync();
-  await promiseObserved;
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_sync_prolonged_non_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, non-network errors are reported
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  // By calling sync, we ensure we're logged in.
-  await Service.sync();
-  Assert.equal(Status.sync, SYNC_SUCCEEDED);
-  Assert.ok(Service.isLoggedIn);
-
-  await EHTestsCommon.generateCredentialsChangedFailure();
-
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-
-  let ping = await sync_and_validate_telem(true);
-  equal(ping.status.sync, PROLONGED_SYNC_FAILURE);
-  deepEqual(ping.failureReason, {
-    name: "unexpectederror",
-    error: "Error: Aborting sync, remote setup failed"
-  });
-  await promiseObserved;
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_login_prolonged_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, network errors are reported
-  await configureIdentity({username: "johndoe"});
-  Service.clusterURL = fakeServerUrl;
-
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await Service.sync();
-  await promiseObserved;
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-});
-
-add_task(async function test_sync_prolonged_network_error() {
-  enableValidationPrefs();
-
-  // Test prolonged, network errors are reported
+  // Test network errors are reported when calling sync.
   Services.io.offline = true;
 
-  let promiseUISyncError = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseUISyncError;
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
+  Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
 
   Services.io.offline = false;
   await clean();
 });
 
 add_task(async function test_login_non_network_error() {
   enableValidationPrefs();
 
   // Test non-network errors are reported
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
   Service.identity._syncKeyBundle = null;
 
-  let promiseObserved = promiseOneObserver("weave:ui:login:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseObserved;
   Assert.equal(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_sync_non_network_error() {
   enableValidationPrefs();
 
@@ -653,60 +208,46 @@ add_task(async function test_sync_non_ne
 
   // By calling sync, we ensure we're logged in.
   await Service.sync();
   Assert.equal(Status.sync, SYNC_SUCCEEDED);
   Assert.ok(Service.isLoggedIn);
 
   await EHTestsCommon.generateCredentialsChangedFailure();
 
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseObserved;
   Assert.equal(Status.sync, CREDENTIALS_CHANGED);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_login_network_error() {
   enableValidationPrefs();
 
   await configureIdentity({username: "johndoe"});
   Service.clusterURL = fakeServerUrl;
 
-  let promiseObserved = promiseOneObserver("weave:ui:clear-error");
   // Test network errors are not reported.
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseObserved;
   Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   Services.io.offline = false;
   await clean();
 });
 
 add_task(async function test_sync_network_error() {
   enableValidationPrefs();
 
   // Test network errors are not reported.
   Services.io.offline = true;
 
-  let promiseSyncFinished = promiseOneObserver("weave:ui:sync:finish");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseSyncFinished;
   Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   Services.io.offline = false;
   await clean();
 });
 
 add_task(async function test_sync_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -714,34 +255,24 @@ add_task(async function test_sync_server
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
 
   const BACKOFF = 42;
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
-  function onSyncError() {
-    do_throw("Shouldn't get here!");
-  }
-  Svc.Obs.add("weave:ui:sync:error", onSyncError);
-
   Assert.equal(Status.service, STATUS_OK);
 
-  let promiseObserved = promiseOneObserver("weave:ui:sync:finish");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   let ping = await sync_and_validate_telem(true);
   equal(ping.status.sync, SERVER_MAINTENANCE);
   deepEqual(ping.engines.find(e => e.failureReason).failureReason, { name: "httperror", code: 503 });
 
-  await promiseObserved;
   Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
   Assert.equal(Status.sync, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_info_collections_login_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -752,37 +283,26 @@ add_task(async function test_info_collec
   await configureIdentity({username: "broken.info"}, server);
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
-  function onUIUpdate() {
-    do_throw("Shouldn't experience UI update!");
-  }
-  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
-
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  let promiseObserved = promiseOneObserver("weave:ui:clear-error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
-  Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_meta_global_login_server_maintenance_error() {
   enableValidationPrefs();
 
   // Test meta/global server maintenance errors are not reported.
@@ -792,32 +312,21 @@ add_task(async function test_meta_global
   await configureIdentity({username: "broken.meta"}, server);
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
-  function onUIUpdate() {
-    do_throw("Shouldn't get here!");
-  }
-  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
-
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  let promiseObserved = promiseOneObserver("weave:ui:clear-error");
-
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
   await Service.sync();
-  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
-  Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
   await clean();
   await promiseStopServer(server);
 });
--- a/services/sync/tests/unit/test_errorhandler_2.js
+++ b/services/sync/tests/unit/test_errorhandler_2.js
@@ -7,25 +7,26 @@ ChromeUtils.import("resource://services-
 ChromeUtils.import("resource://services-sync/keys.js");
 ChromeUtils.import("resource://services-sync/policies.js");
 ChromeUtils.import("resource://services-sync/service.js");
 ChromeUtils.import("resource://services-sync/status.js");
 ChromeUtils.import("resource://services-sync/util.js");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 
-var fakeServer = new SyncServer();
+const fakeServer = new SyncServer();
 fakeServer.start();
+const fakeServerUrl = "http://localhost:" + fakeServer.port;
 
 registerCleanupFunction(function() {
-  return promiseStopServer(fakeServer);
+  return promiseStopServer(fakeServer).finally(() => {
+    Svc.Prefs.resetBranch("");
+  });
 });
 
-var fakeServerUrl = "http://localhost:" + fakeServer.port;
-
 const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
 
 function removeLogFiles() {
   let entries = logsdir.directoryEntries;
   while (entries.hasMoreElements()) {
     let logfile = entries.getNext().QueryInterface(Ci.nsIFile);
     logfile.remove(false);
   }
@@ -35,61 +36,29 @@ function getLogFiles() {
   let result = [];
   let entries = logsdir.directoryEntries;
   while (entries.hasMoreElements()) {
     result.push(entries.getNext().QueryInterface(Ci.nsIFile));
   }
   return result;
 }
 
-const PROLONGED_ERROR_DURATION =
-  (Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 2) * 1000;
-
-const NON_PROLONGED_ERROR_DURATION =
-  (Svc.Prefs.get("errorhandler.networkFailureReportTimeout") / 2) * 1000;
-
-function setLastSync(lastSyncValue) {
-  Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
-}
-
-// This relies on Service/ErrorHandler being a singleton. Fixing this will take
-// a lot of work.
-var errorHandler = Service.errorHandler;
 let engine;
-
-async function syncAndWait(topic) {
-  let promise1 = promiseOneObserver(topic);
-  // also wait for the log file to be written
-  let promise2 = promiseOneObserver("weave:service:reset-file-log");
-  await Service.sync();
-  await promise1;
-  await promise2;
-}
-
-async function syncAndReportErrorsAndWait(topic) {
-  let promise1 = promiseOneObserver(topic);
-  // also wait for the log file to be written
-  let promise2 = promiseOneObserver("weave:service:reset-file-log");
-  errorHandler.syncAndReportErrors();
-  await promise1;
-  await promise2;
-}
 add_task(async function setup() {
   await Service.engineManager.clear();
   await Service.engineManager.register(EHTestsCommon.CatapultEngine);
   engine = Service.engineManager.get("catapult");
 });
 
 async function clean() {
   let promiseLogReset = promiseOneObserver("weave:service:reset-file-log");
   await Service.startOver();
   await promiseLogReset;
   Status.resetSync();
   Status.resetBackoff();
-  errorHandler.didReportProlongedError = false;
   removeLogFiles();
   // Move log levels back to trace (startOver will have reversed this), sicne
   syncTestLogging();
 }
 
 add_task(async function test_crypto_keys_login_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -104,292 +73,92 @@ add_task(async function test_crypto_keys
   Service.collectionKeys.clear();
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
-  function onUIUpdate() {
-    do_throw("Shouldn't get here!");
-  }
-  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
-
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:clear-error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
-  Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_lastSync_not_updated_on_complete_failure() {
   enableValidationPrefs();
 
   // Test info/collections prolonged server maintenance errors are reported.
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
 
   await configureIdentity({username: "johndoe"}, server);
 
   // Do an initial sync that we expect to be successful.
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
   await sync_and_validate_telem(false);
+  await promiseObserved;
 
   Assert.equal(Status.service, STATUS_OK);
   Assert.equal(Status.sync, SYNC_SUCCEEDED);
 
   let lastSync = Svc.Prefs.get("lastSync");
 
   Assert.ok(lastSync);
 
   // Report server maintenance on info/collections requests
   server.registerPathHandler("/1.1/johndoe/info/collections",
                              EHTestsCommon.service_unavailable);
 
+  promiseObserved = promiseOneObserver("weave:service:reset-file-log");
   await sync_and_validate_telem(true);
+  await promiseObserved;
 
   Assert.equal(Status.sync, SERVER_MAINTENANCE);
   Assert.equal(Status.service, SYNC_FAILED);
 
   // We shouldn't update lastSync on complete failure.
   Assert.equal(lastSync, Svc.Prefs.get("lastSync"));
 
   await clean();
   await promiseStopServer(server);
 });
 
-add_task(async function test_info_collections_login_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test info/collections prolonged server maintenance errors are reported.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.info"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_meta_global_login_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test meta/global prolonged server maintenance errors are reported.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.meta"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_download_crypto_keys_login_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test crypto/keys prolonged server maintenance errors are reported.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.keys"}, server);
-  // Force re-download of keys
-  Service.collectionKeys.clear();
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:login:error");
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_upload_crypto_keys_login_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test crypto/keys prolonged server maintenance errors are reported.
-  let server = await EHTestsCommon.sync_httpd_setup();
-
-  // Start off with an empty account, do not upload a key.
-  await configureIdentity({username: "broken.keys"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_wipeServer_login_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test that we report prolonged server maintenance errors that occur whilst
-  // wiping the server.
-  let server = await EHTestsCommon.sync_httpd_setup();
-
-  // Start off with an empty account, do not upload a key.
-  await configureIdentity({username: "broken.wipe"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.ok(errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_wipeRemote_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test that we report prolonged server maintenance errors that occur whilst
-  // wiping all remote devices.
-  let server = await EHTestsCommon.sync_httpd_setup();
-
-  server.registerPathHandler("/1.1/broken.wipe/storage/catapult", EHTestsCommon.service_unavailable);
-  await configureIdentity({username: "broken.wipe"}, server);
-  await EHTestsCommon.generateAndUploadKeys();
-
-  engine.exception = null;
-  engine.enabled = true;
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  Svc.Prefs.set("firstSync", "wipeRemote");
-  setLastSync(PROLONGED_ERROR_DURATION);
-  let ping = await sync_and_validate_telem(true);
-  deepEqual(ping.failureReason, { name: "httperror", code: 503 });
-  await promiseObserved;
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, SYNC_FAILED);
-  Assert.equal(Status.sync, PROLONGED_SYNC_FAILURE);
-  Assert.equal(Svc.Prefs.get("firstSync"), "wipeRemote");
-  Assert.ok(errorHandler.didReportProlongedError);
-  await promiseStopServer(server);
-  await clean();
-});
-
 add_task(async function test_sync_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
   // Test server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
 
   const BACKOFF = 42;
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:sync:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
   Assert.equal(Status.sync, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_info_collections_login_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -404,24 +173,24 @@ add_task(async function test_info_collec
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_meta_global_login_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -436,24 +205,24 @@ add_task(async function test_meta_global
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_download_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -470,24 +239,24 @@ add_task(async function test_download_cr
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_upload_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -502,24 +271,24 @@ add_task(async function test_upload_cryp
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_wipeServer_login_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -534,24 +303,24 @@ add_task(async function test_wipeServer_
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, LOGIN_FAILED);
   Assert.equal(Status.login, SERVER_MAINTENANCE);
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_wipeRemote_syncAndReportErrors_server_maintenance_error() {
   enableValidationPrefs();
 
@@ -570,225 +339,26 @@ add_task(async function test_wipeRemote_
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Assert.ok(!Status.enforceBackoff);
   Assert.equal(Status.service, STATUS_OK);
 
   Svc.Prefs.set("firstSync", "wipeRemote");
-  setLastSync(NON_PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:sync:error");
+
+  let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
+  await Service.sync();
+  await promiseObserved;
 
   Assert.ok(Status.enforceBackoff);
   Assert.equal(backoffInterval, 42);
   Assert.equal(Status.service, SYNC_FAILED);
   Assert.equal(Status.sync, SERVER_MAINTENANCE);
   Assert.equal(Svc.Prefs.get("firstSync"), "wipeRemote");
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test prolonged server maintenance errors are
-  // reported when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  const BACKOFF = 42;
-  engine.enabled = true;
-  engine.exception = {status: 503,
-                      headers: {"retry-after": BACKOFF}};
-
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:sync:error");
-
-  Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
-  Assert.equal(Status.sync, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_info_collections_login_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test info/collections server maintenance errors are reported
-  // when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.info"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, LOGIN_FAILED);
-  Assert.equal(Status.login, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_meta_global_login_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test meta/global server maintenance errors are reported
-  // when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.meta"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, LOGIN_FAILED);
-  Assert.equal(Status.login, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_download_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test crypto/keys server maintenance errors are reported
-  // when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-  await EHTestsCommon.setUp(server);
-
-  await configureIdentity({username: "broken.keys"}, server);
-  // Force re-download of keys
-  Service.collectionKeys.clear();
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, LOGIN_FAILED);
-  Assert.equal(Status.login, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test crypto/keys server maintenance errors are reported
-  // when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-
-  // Start off with an empty account, do not upload a key.
-  await configureIdentity({username: "broken.keys"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, LOGIN_FAILED);
-  Assert.equal(Status.login, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
-
-  await clean();
-  await promiseStopServer(server);
-});
-
-add_task(async function test_wipeServer_login_syncAndReportErrors_prolonged_server_maintenance_error() {
-  enableValidationPrefs();
-
-  // Test crypto/keys server maintenance errors are reported
-  // when calling syncAndReportErrors.
-  let server = await EHTestsCommon.sync_httpd_setup();
-
-  // Start off with an empty account, do not upload a key.
-  await configureIdentity({username: "broken.wipe"}, server);
-
-  let backoffInterval;
-  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
-    Svc.Obs.remove("weave:service:backoff:interval", observe);
-    backoffInterval = subject;
-  });
-
-  Assert.ok(!Status.enforceBackoff);
-  Assert.equal(Status.service, STATUS_OK);
-
-  setLastSync(PROLONGED_ERROR_DURATION);
-  await syncAndReportErrorsAndWait("weave:ui:login:error");
-
-  Assert.ok(Status.enforceBackoff);
-  Assert.equal(backoffInterval, 42);
-  Assert.equal(Status.service, LOGIN_FAILED);
-  Assert.equal(Status.login, SERVER_MAINTENANCE);
-  // syncAndReportErrors means dontIgnoreErrors, which means
-  // didReportProlongedError not touched.
-  Assert.ok(!errorHandler.didReportProlongedError);
 
   await clean();
   await promiseStopServer(server);
 });
 
 add_task(async function test_sync_engine_generic_fail() {
   enableValidationPrefs();
 
@@ -840,55 +410,53 @@ add_task(async function test_sync_engine
 
   let syncErrors = sumHistogram("WEAVE_ENGINE_SYNC_ERRORS", { key: "catapult" });
   Assert.ok(syncErrors, 1);
 
   await clean();
   await promiseStopServer(server);
 });
 
-add_task(async function test_logs_on_sync_error_despite_shouldReportError() {
+add_task(async function test_logs_on_sync_error() {
   enableValidationPrefs();
 
   _("Ensure that an error is still logged when weave:service:sync:error " +
     "is notified, despite shouldReportError returning false.");
 
   let log = Log.repository.getLogger("Sync.ErrorHandler");
   Svc.Prefs.set("log.appender.file.logOnError", true);
   log.info("TESTING");
 
   // Ensure that we report no error.
   Status.login = MASTER_PASSWORD_LOCKED;
-  Assert.ok(!errorHandler.shouldReportError());
 
   let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
   Svc.Obs.notify("weave:service:sync:error", {});
   await promiseObserved;
 
   // Test that error log was written.
   let logFiles = getLogFiles();
   equal(logFiles.length, 1);
   Assert.ok(logFiles[0].leafName.startsWith("error-sync-"), logFiles[0].leafName);
 
   await clean();
 });
 
-add_task(async function test_logs_on_login_error_despite_shouldReportError() {
+add_task(async function test_logs_on_login_error() {
   enableValidationPrefs();
 
   _("Ensure that an error is still logged when weave:service:login:error " +
     "is notified, despite shouldReportError returning false.");
 
   let log = Log.repository.getLogger("Sync.ErrorHandler");
   Svc.Prefs.set("log.appender.file.logOnError", true);
   log.info("TESTING");
 
   // Ensure that we report no error.
   Status.login = MASTER_PASSWORD_LOCKED;
-  Assert.ok(!errorHandler.shouldReportError());
 
   let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
   Svc.Obs.notify("weave:service:login:error", {});
   await promiseObserved;
 
   // Test that error log was written.
   let logFiles = getLogFiles();
   equal(logFiles.length, 1);
--- a/services/sync/tests/unit/test_errorhandler_filelog.js
+++ b/services/sync/tests/unit/test_errorhandler_filelog.js
@@ -13,25 +13,18 @@ const logsdir            = FileUtils.get
 
 // Delay to wait before cleanup, to allow files to age.
 // This is so large because the file timestamp granularity is per-second, and
 // so otherwise we can end up with all of our files -- the ones we want to
 // keep, and the ones we want to clean up -- having the same modified time.
 const CLEANUP_DELAY      = 2000;
 const DELAY_BUFFER       = 500; // Buffer for timers on different OS platforms.
 
-const PROLONGED_ERROR_DURATION =
-  (Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 2) * 1000;
-
 var errorHandler = Service.errorHandler;
 
-function setLastSync(lastSyncValue) {
-  Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
-}
-
 function run_test() {
   validate_all_future_pings();
   run_next_test();
 }
 
 add_test(function test_noOutput() {
   // Ensure that the log appender won't print anything.
   errorHandler._logManager._fileAppender.level = Log.Level.Fatal + 1;
@@ -137,18 +130,17 @@ add_test(function test_sync_error_logOnE
     Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
     // No log file was written.
     Assert.ok(!logsdir.directoryEntries.hasMoreElements());
 
     Svc.Prefs.resetBranch("");
     run_next_test();
   });
 
-  // Fake an unsuccessful sync due to prolonged failure.
-  setLastSync(PROLONGED_ERROR_DURATION);
+  // Fake an unsuccessful sync.
   Svc.Obs.notify("weave:service:sync:error");
 });
 
 add_test(function test_sync_error_logOnError_true() {
   Svc.Prefs.set("log.appender.file.logOnError", true);
 
   let log = Log.repository.getLogger("Sync.Test.FileLog");
   const MESSAGE = "this WILL show up";
@@ -178,18 +170,17 @@ add_test(function test_sync_error_logOnE
         // Stupid Windows box.
       }
 
       Svc.Prefs.resetBranch("");
       run_next_test();
     });
   });
 
-  // Fake an unsuccessful sync due to prolonged failure.
-  setLastSync(PROLONGED_ERROR_DURATION);
+  // Fake an unsuccessful sync.
   Svc.Obs.notify("weave:service:sync:error");
 });
 
 add_test(function test_login_error_logOnError_false() {
   Svc.Prefs.set("log.appender.file.logOnError", false);
 
   let log = Log.repository.getLogger("Sync.Test.FileLog");
   log.info("this won't show up");
@@ -198,18 +189,17 @@ add_test(function test_login_error_logOn
     Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
     // No log file was written.
     Assert.ok(!logsdir.directoryEntries.hasMoreElements());
 
     Svc.Prefs.resetBranch("");
     run_next_test();
   });
 
-  // Fake an unsuccessful login due to prolonged failure.
-  setLastSync(PROLONGED_ERROR_DURATION);
+  // Fake an unsuccessful login.
   Svc.Obs.notify("weave:service:login:error");
 });
 
 add_test(function test_login_error_logOnError_true() {
   Svc.Prefs.set("log.appender.file.logOnError", true);
 
   let log = Log.repository.getLogger("Sync.Test.FileLog");
   const MESSAGE = "this WILL show up";
@@ -239,18 +229,17 @@ add_test(function test_login_error_logOn
         // Stupid Windows box.
       }
 
       Svc.Prefs.resetBranch("");
       run_next_test();
     });
   });
 
-  // Fake an unsuccessful login due to prolonged failure.
-  setLastSync(PROLONGED_ERROR_DURATION);
+  // Fake an unsuccessful login.
   Svc.Obs.notify("weave:service:login:error");
 });
 
 add_test(function test_noNewFailed_noErrorLog() {
   Svc.Prefs.set("log.appender.file.logOnError", true);
   Svc.Prefs.set("log.appender.file.logOnSuccess", false);
 
   Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
@@ -348,18 +337,17 @@ add_test(function test_errorLog_dumpAddo
         // Stupid Windows box.
       }
 
       Svc.Prefs.resetBranch("");
       run_next_test();
     });
   });
 
-  // Fake an unsuccessful sync due to prolonged failure.
-  setLastSync(PROLONGED_ERROR_DURATION);
+  // Fake an unsuccessful sync.
   Svc.Obs.notify("weave:service:sync:error");
 });
 
 // Check that error log files are deleted above an age threshold.
 add_test(function test_logErrorCleanup_age() {
   _("Beginning test_logErrorCleanup_age.");
   let maxAge = CLEANUP_DELAY / 1000;
   let oldLogs = [];
--- a/services/sync/tests/unit/test_fxa_node_reassignment.js
+++ b/services/sync/tests/unit/test_fxa_node_reassignment.js
@@ -20,23 +20,16 @@ ChromeUtils.import("resource://gre/modul
 
 add_task(async function setup() {
   // Disables all built-in engines. Important for avoiding errors thrown by the
   // add-ons engine.
   await Service.engineManager.clear();
 
   // Setup the FxA identity manager and cluster manager.
   Status.__authManager = Service.identity = new BrowserIDManager();
-
-  // None of the failures in this file should result in a UI error.
-  function onUIError() {
-    do_throw("Errors should not be presented in the UI.");
-  }
-  Svc.Obs.add("weave:ui:login:error", onUIError);
-  Svc.Obs.add("weave:ui:sync:error", onUIError);
 });
 
 
 // API-compatible with SyncServer handler. Bind `handler` to something to use
 // as a ServerCollection handler.
 function handleReassign(handler, req, resp) {
   resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
   resp.setHeader("Content-Type", "application/json");
--- a/services/sync/tests/unit/test_node_reassignment.js
+++ b/services/sync/tests/unit/test_node_reassignment.js
@@ -11,23 +11,16 @@ ChromeUtils.import("resource://services-
 ChromeUtils.import("resource://services-sync/status.js");
 ChromeUtils.import("resource://services-sync/util.js");
 ChromeUtils.import("resource://testing-common/services/sync/rotaryengine.js");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 
 
 add_task(async function setup() {
   validate_all_future_pings();
-
-  // None of the failures in this file should result in a UI error.
-  function onUIError() {
-    do_throw("Errors should not be presented in the UI.");
-  }
-  Svc.Obs.add("weave:ui:login:error", onUIError);
-  Svc.Obs.add("weave:ui:sync:error", onUIError);
 });
 
 /**
  * Emulate the following Zeus config:
  * $draining = data.get($prefix . $host . " draining");
  * if ($draining == "drain.") {
  *   log.warn($log_host_db_status . " migrating=1 (node-reassignment)" .
  *            $log_suffix);
--- a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
+++ b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
@@ -215,21 +215,18 @@ add_task(async function test_disabledLoc
   await setUp(server);
 
   _("Disable engine locally.");
   Service._ignorePrefObserver = true;
   engine.enabled = true;
   Service._ignorePrefObserver = false;
   engine.enabled = false;
 
-  let promiseObserved = promiseOneObserver("weave:ui:sync:error");
-
   _("Sync.");
-  Service.errorHandler.syncAndReportErrors();
-  await promiseObserved;
+  await Service.sync();
   Assert.equal(Service.status.sync, SERVER_MAINTENANCE);
 
   await Service.startOver();
   await promiseStopServer(server);
 });
 
 add_task(async function test_enabledRemotely() {
   enableValidationPrefs();
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -24,17 +24,17 @@
   "BootstrapMonitor.jsm": ["monitor"],
   "browser-loader.js": ["BrowserLoader"],
   "browserid_identity.js": ["BrowserIDManager", "AuthenticationError"],
   "CertUtils.jsm": ["BadCertHandler", "checkCert", "readCertPrefs", "validateCert"],
   "clients.js": ["ClientEngine", "ClientsRec"],
   "collection_repair.js": ["getRepairRequestor", "getAllRepairRequestors", "CollectionRepairRequestor", "getRepairResponder", "CollectionRepairResponder"],
   "collection_validator.js": ["CollectionValidator", "CollectionProblemData"],
   "Console.jsm": ["console", "ConsoleAPI"],
-  "constants.js": ["WEAVE_VERSION", "SYNC_API_VERSION", "STORAGE_VERSION", "PREFS_BRANCH", "DEFAULT_KEYBUNDLE_NAME", "SYNC_KEY_ENCODED_LENGTH", "SYNC_KEY_DECODED_LENGTH", "NO_SYNC_NODE_INTERVAL", "MAX_ERROR_COUNT_BEFORE_BACKOFF", "MINIMUM_BACKOFF_INTERVAL", "MAXIMUM_BACKOFF_INTERVAL", "HMAC_EVENT_INTERVAL", "MASTER_PASSWORD_LOCKED_RETRY_INTERVAL", "DEFAULT_GUID_FETCH_BATCH_SIZE", "DEFAULT_DOWNLOAD_BATCH_SIZE", "SINGLE_USER_THRESHOLD", "MULTI_DEVICE_THRESHOLD", "SCORE_INCREMENT_SMALL", "SCORE_INCREMENT_MEDIUM", "SCORE_INCREMENT_XLARGE", "SCORE_UPDATE_DELAY", "IDLE_OBSERVER_BACK_DELAY", "URI_LENGTH_MAX", "MAX_HISTORY_UPLOAD", "MAX_HISTORY_DOWNLOAD", "STATUS_OK", "SYNC_FAILED", "LOGIN_FAILED", "SYNC_FAILED_PARTIAL", "CLIENT_NOT_CONFIGURED", "STATUS_DISABLED", "MASTER_PASSWORD_LOCKED", "LOGIN_SUCCEEDED", "SYNC_SUCCEEDED", "ENGINE_SUCCEEDED", "LOGIN_FAILED_NO_USERNAME", "LOGIN_FAILED_NO_PASSPHRASE", "LOGIN_FAILED_NETWORK_ERROR", "LOGIN_FAILED_SERVER_ERROR", "LOGIN_FAILED_INVALID_PASSPHRASE", "LOGIN_FAILED_LOGIN_REJECTED", "METARECORD_DOWNLOAD_FAIL", "VERSION_OUT_OF_DATE", "CREDENTIALS_CHANGED", "ABORT_SYNC_COMMAND", "NO_SYNC_NODE_FOUND", "OVER_QUOTA", "PROLONGED_SYNC_FAILURE", "SERVER_MAINTENANCE", "RESPONSE_OVER_QUOTA", "ENGINE_UPLOAD_FAIL", "ENGINE_DOWNLOAD_FAIL", "ENGINE_UNKNOWN_FAIL", "ENGINE_APPLY_FAIL", "ENGINE_BATCH_INTERRUPTED", "kSyncMasterPasswordLocked", "kSyncWeaveDisabled", "kSyncNetworkOffline", "kSyncBackoffNotMet", "kFirstSyncChoiceNotMade", "kSyncNotConfigured", "kFirefoxShuttingDown", "DEVICE_TYPE_DESKTOP", "DEVICE_TYPE_MOBILE", "SQLITE_MAX_VARIABLE_NUMBER"],
+  "constants.js": ["WEAVE_VERSION", "SYNC_API_VERSION", "STORAGE_VERSION", "PREFS_BRANCH", "DEFAULT_KEYBUNDLE_NAME", "SYNC_KEY_ENCODED_LENGTH", "SYNC_KEY_DECODED_LENGTH", "NO_SYNC_NODE_INTERVAL", "MAX_ERROR_COUNT_BEFORE_BACKOFF", "MINIMUM_BACKOFF_INTERVAL", "MAXIMUM_BACKOFF_INTERVAL", "HMAC_EVENT_INTERVAL", "MASTER_PASSWORD_LOCKED_RETRY_INTERVAL", "DEFAULT_GUID_FETCH_BATCH_SIZE", "DEFAULT_DOWNLOAD_BATCH_SIZE", "SINGLE_USER_THRESHOLD", "MULTI_DEVICE_THRESHOLD", "SCORE_INCREMENT_SMALL", "SCORE_INCREMENT_MEDIUM", "SCORE_INCREMENT_XLARGE", "SCORE_UPDATE_DELAY", "IDLE_OBSERVER_BACK_DELAY", "URI_LENGTH_MAX", "MAX_HISTORY_UPLOAD", "MAX_HISTORY_DOWNLOAD", "STATUS_OK", "SYNC_FAILED", "LOGIN_FAILED", "SYNC_FAILED_PARTIAL", "CLIENT_NOT_CONFIGURED", "STATUS_DISABLED", "MASTER_PASSWORD_LOCKED", "LOGIN_SUCCEEDED", "SYNC_SUCCEEDED", "ENGINE_SUCCEEDED", "LOGIN_FAILED_NO_USERNAME", "LOGIN_FAILED_NO_PASSPHRASE", "LOGIN_FAILED_NETWORK_ERROR", "LOGIN_FAILED_SERVER_ERROR", "LOGIN_FAILED_INVALID_PASSPHRASE", "LOGIN_FAILED_LOGIN_REJECTED", "METARECORD_DOWNLOAD_FAIL", "VERSION_OUT_OF_DATE", "CREDENTIALS_CHANGED", "ABORT_SYNC_COMMAND", "NO_SYNC_NODE_FOUND", "OVER_QUOTA", "SERVER_MAINTENANCE", "RESPONSE_OVER_QUOTA", "ENGINE_UPLOAD_FAIL", "ENGINE_DOWNLOAD_FAIL", "ENGINE_UNKNOWN_FAIL", "ENGINE_APPLY_FAIL", "ENGINE_BATCH_INTERRUPTED", "kSyncMasterPasswordLocked", "kSyncWeaveDisabled", "kSyncNetworkOffline", "kSyncBackoffNotMet", "kFirstSyncChoiceNotMade", "kSyncNotConfigured", "kFirefoxShuttingDown", "DEVICE_TYPE_DESKTOP", "DEVICE_TYPE_MOBILE", "SQLITE_MAX_VARIABLE_NUMBER"],
   "Constants.jsm": ["Roles", "Events", "Relations", "Filters", "States", "Prefilters"],
   "ContactDB.jsm": ["ContactDB", "DB_NAME", "STORE_NAME", "SAVED_GETALL_STORE_NAME", "REVISION_STORE", "DB_VERSION"],
   "content-server.jsm": ["init"],
   "content.jsm": ["registerContentFrame"],
   "ContentCrashHandlers.jsm": ["TabCrashHandler", "PluginCrashReporter", "UnsubmittedCrashHandler"],
   "ContentObservers.js": [],
   "ContentPrefUtils.jsm": ["ContentPref", "cbHandleResult", "cbHandleError", "cbHandleCompletion", "safeCallback", "_methodsCallableFromChild"],
   "cookies.js": ["Cookies"],