Bug 785225 - Part 9: Refactor engines to not use singletons; r=rnewman
authorGregory Szorc <gps@mozilla.com>
Wed, 29 Aug 2012 14:43:41 -0700
changeset 111056 9eeef1bbadeeee885c858bd56f46ac285a6cb250
parent 111055 6d61599eebd30b5e55a1fc60b893b52a9eb8edc4
child 111057 cf4c39d50fd26b16f25e68cb370655ae620c0da0
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersrnewman
bugs785225
milestone18.0a1
Bug 785225 - Part 9: Refactor engines to not use singletons; r=rnewman Engines now maintain a reference to the service they belong to. This allows them to obtain references to other engine instances belonging to that service and that service only. Stores and trackers now maintain a reference to the engine they belong to. Engine managers now maintain a reference back to a service. The clients singleton has been removed. It now exists as an instance variable on Service. Parts of ClientsEngine do behave as singletons (e.g. commands). This will be addressed in future refactoring.
services/sync/modules/engines.js
services/sync/modules/engines/addons.js
services/sync/modules/engines/apps.js
services/sync/modules/engines/bookmarks.js
services/sync/modules/engines/clients.js
services/sync/modules/engines/forms.js
services/sync/modules/engines/history.js
services/sync/modules/engines/passwords.js
services/sync/modules/engines/prefs.js
services/sync/modules/engines/tabs.js
services/sync/modules/policies.js
services/sync/modules/service.js
services/sync/tests/unit/head_helpers.js
services/sync/tests/unit/test_addons_engine.js
services/sync/tests/unit/test_addons_reconciler.js
services/sync/tests/unit/test_addons_store.js
services/sync/tests/unit/test_addons_tracker.js
services/sync/tests/unit/test_bookmark_batch_fail.js
services/sync/tests/unit/test_bookmark_engine.js
services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js
services/sync/tests/unit/test_bookmark_livemarks.js
services/sync/tests/unit/test_bookmark_order.js
services/sync/tests/unit/test_bookmark_places_query_rewriting.js
services/sync/tests/unit/test_bookmark_smart_bookmarks.js
services/sync/tests/unit/test_bookmark_store.js
services/sync/tests/unit/test_bookmark_tracker.js
services/sync/tests/unit/test_clients_engine.js
services/sync/tests/unit/test_clients_escape.js
services/sync/tests/unit/test_corrupt_keys.js
services/sync/tests/unit/test_engine.js
services/sync/tests/unit/test_engine_abort.js
services/sync/tests/unit/test_enginemanager.js
services/sync/tests/unit/test_errorhandler.js
services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
services/sync/tests/unit/test_forms_store.js
services/sync/tests/unit/test_forms_tracker.js
services/sync/tests/unit/test_history_engine.js
services/sync/tests/unit/test_history_store.js
services/sync/tests/unit/test_history_tracker.js
services/sync/tests/unit/test_hmac_error.js
services/sync/tests/unit/test_interval_triggers.js
services/sync/tests/unit/test_node_reassignment.js
services/sync/tests/unit/test_password_store.js
services/sync/tests/unit/test_password_tracker.js
services/sync/tests/unit/test_places_guid_downgrade.js
services/sync/tests/unit/test_prefs_store.js
services/sync/tests/unit/test_prefs_tracker.js
services/sync/tests/unit/test_score_triggers.js
services/sync/tests/unit/test_service_detect_upgrade.js
services/sync/tests/unit/test_service_startOver.js
services/sync/tests/unit/test_service_startup.js
services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
services/sync/tests/unit/test_service_wipeClient.js
services/sync/tests/unit/test_syncengine.js
services/sync/tests/unit/test_syncengine_sync.js
services/sync/tests/unit/test_syncscheduler.js
services/sync/tests/unit/test_tab_engine.js
services/sync/tests/unit/test_tab_store.js
services/sync/tests/unit/test_tab_tracker.js
services/sync/tests/unit/test_tracker_addChanged.js
--- a/services/sync/modules/engines.js
+++ b/services/sync/modules/engines.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const EXPORTED_SYMBOLS = [
-  "Engines",
+  "EngineManager",
   "Engine",
   "SyncEngine",
   "Tracker",
   "Store"
 ];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
@@ -30,19 +30,24 @@ Cu.import("resource://services-sync/main
  *
  * There are two things they keep track of:
  * 1) A score, indicating how urgently the engine wants to sync
  * 2) A list of IDs for all the changed items that need to be synced
  * and updating their 'score', indicating how urgently they
  * want to sync.
  *
  */
-function Tracker(name) {
+function Tracker(name, engine) {
+  if (!engine) {
+    throw new Error("Tracker must be associated with an Engine instance.");
+  }
+
   name = name || "Unnamed";
   this.name = this.file = name.toLowerCase();
+  this.engine = engine;
 
   this._log = Log4Moz.repository.getLogger("Sync.Tracker." + name);
   let level = Svc.Prefs.get("log.logger.engine." + this.name, "Debug");
   this._log.level = Log4Moz.Level[level];
 
   this._score = 0;
   this._ignored = [];
   this.ignoreAll = false;
@@ -163,19 +168,24 @@ Tracker.prototype = {
  * Store implementations require a number of functions to be implemented. These
  * are all documented below.
  *
  * For stores that deal with many records or which have expensive store access
  * routines, it is highly recommended to implement a custom applyIncomingBatch
  * and/or applyIncoming function on top of the basic APIs.
  */
 
-function Store(name) {
+function Store(name, engine) {
+  if (!engine) {
+    throw new Error("Store must be associated with an Engine instance.");
+  }
+
   name = name || "Unnamed";
   this.name = name.toLowerCase();
+  this.engine = engine;
 
   this._log = Log4Moz.repository.getLogger("Sync.Store." + name);
   let level = Svc.Prefs.get("log.logger.engine." + this.name, "Debug");
   this._log.level = Log4Moz.Level[level];
 
   XPCOMUtils.defineLazyGetter(this, "_timer", function() {
     return Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   });
@@ -349,22 +359,19 @@ Store.prototype = {
    * can be thought of as clearing out all state and restoring the "new
    * browser" state.
    */
   wipe: function Store_wipe() {
     throw "override wipe in a subclass";
   }
 };
 
-// TODO remove singleton (bug 785225).
-XPCOMUtils.defineLazyGetter(this, "Engines", function() {
-  return new EngineManager();
-});
+function EngineManager(service) {
+  this.service = service;
 
-function EngineManager() {
   this._engines = {};
   this._log = Log4Moz.repository.getLogger("Sync.EngineManager");
   this._log.level = Log4Moz.Level[Svc.Prefs.get(
     "log.logger.service.engines", "Debug")];
 }
 EngineManager.prototype = {
   get: function get(name) {
     // Return an array of engines if we have an array of names
@@ -383,17 +390,17 @@ EngineManager.prototype = {
       this._log.debug("Could not get engine: " + name);
       if (Object.keys)
         this._log.debug("Engines are: " + JSON.stringify(Object.keys(this._engines)));
     }
     return engine;
   },
 
   getAll: function getAll() {
-    return [engine for ([name, engine] in Iterator(Engines._engines))];
+    return [engine for ([name, engine] in Iterator(this._engines))];
   },
 
   getEnabled: function getEnabled() {
     return this.getAll().filter(function(engine) engine.enabled);
   },
 
   /**
    * Register an Engine to the service. Alternatively, give an array of engine
@@ -403,17 +410,17 @@ EngineManager.prototype = {
    *        Engine object used to get an instance of the engine
    * @return The engine object if anything failed
    */
   register: function register(engineObject) {
     if (Array.isArray(engineObject))
       return engineObject.map(this.register, this);
 
     try {
-      let engine = new engineObject();
+      let engine = new engineObject(this.service);
       let name = engine.name;
       if (name in this._engines)
         this._log.error("Engine '" + name + "' is already registered!");
       else
         this._engines[name] = engine;
     }
     catch(ex) {
       this._log.error(CommonUtils.exceptionStr(ex));
@@ -431,21 +438,32 @@ EngineManager.prototype = {
   },
 
   unregister: function unregister(val) {
     let name = val;
     if (val instanceof Engine)
       name = val.name;
     delete this._engines[name];
   },
+
+  clear: function clear() {
+    for (let name in this._engines) {
+      delete this._engines[name];
+    }
+  },
 };
 
-function Engine(name) {
+function Engine(name, service) {
+  if (!service) {
+    throw new Error("Engine must be associated with a Service instance.");
+  }
+
   this.Name = name || "Unnamed";
   this.name = name.toLowerCase();
+  this.service = service;
 
   this._notify = Utils.notify("weave:engine:");
   this._log = Log4Moz.repository.getLogger("Sync.Engine." + this.Name);
   let level = Svc.Prefs.get("log.logger.engine." + this.name, "Debug");
   this._log.level = Log4Moz.Level[level];
 
   this._tracker; // initialize tracker to load previously changed IDs
   this._log.debug("Engine initialized");
@@ -461,23 +479,23 @@ Engine.prototype = {
 
   get prefName() this.name,
   get enabled() Svc.Prefs.get("engine." + this.prefName, false),
   set enabled(val) Svc.Prefs.set("engine." + this.prefName, !!val),
 
   get score() this._tracker.score,
 
   get _store() {
-    let store = new this._storeObj(this.Name);
+    let store = new this._storeObj(this.Name, this);
     this.__defineGetter__("_store", function() store);
     return store;
   },
 
   get _tracker() {
-    let tracker = new this._trackerObj(this.Name);
+    let tracker = new this._trackerObj(this.Name, this);
     this.__defineGetter__("_tracker", function() tracker);
     return tracker;
   },
 
   sync: function Engine_sync() {
     if (!this.enabled)
       return;
 
@@ -506,18 +524,19 @@ Engine.prototype = {
     this._tracker.clearChangedIDs();
   },
 
   wipeClient: function Engine_wipeClient() {
     this._notify("wipe-client", this.name, this._wipeClient)();
   }
 };
 
-function SyncEngine(name) {
-  Engine.call(this, name || "SyncEngine");
+function SyncEngine(name, service) {
+  Engine.call(this, name || "SyncEngine", service);
+
   this.loadToFetch();
   this.loadPreviousFailed();
 }
 
 // Enumeration to define approaches to handling bad records.
 // Attached to the constructor to allow use as a kind of static enumeration.
 SyncEngine.kRecoveryStrategy = {
   ignore: "ignore",
@@ -1354,13 +1373,13 @@ SyncEngine.prototype = {
    *
    * Subclasses of SyncEngine can override this method to allow for different
    * behavior -- e.g., to delete and ignore erroneous entries.
    *
    * All return values will be part of the kRecoveryStrategy enumeration.
    */
   handleHMACMismatch: function handleHMACMismatch(item, mayRetry) {
     // By default we either try again, or bail out noisily.
-    return (Weave.Service.handleHMACEvent() && mayRetry) ?
+    return (this.service.handleHMACEvent() && mayRetry) ?
            SyncEngine.kRecoveryStrategy.retry :
            SyncEngine.kRecoveryStrategy.error;
   }
 };
--- a/services/sync/modules/engines/addons.js
+++ b/services/sync/modules/engines/addons.js
@@ -102,18 +102,18 @@ Utils.deferGetSet(AddonRecord, "cleartex
  *
  * The engine maintains an instance of an AddonsReconciler, which is the entity
  * maintaining state for add-ons. It provides the history and tracking APIs
  * that AddonManager doesn't.
  *
  * The engine instance overrides a handful of functions on the base class. The
  * rationale for each is documented by that function.
  */
-function AddonsEngine() {
-  SyncEngine.call(this, "Addons");
+function AddonsEngine(service) {
+  SyncEngine.call(this, "Addons", service);
 
   this._reconciler = new AddonsReconciler();
 }
 AddonsEngine.prototype = {
   __proto__:              SyncEngine.prototype,
   _storeObj:              AddonsStore,
   _trackerObj:            AddonsTracker,
   _recordObj:             AddonRecord,
@@ -233,38 +233,31 @@ AddonsEngine.prototype = {
 };
 
 /**
  * This is the primary interface between Sync and the Addons Manager.
  *
  * In addition to the core store APIs, we provide convenience functions to wrap
  * Add-on Manager APIs with Sync-specific semantics.
  */
-function AddonsStore(name) {
-  Store.call(this, name);
+function AddonsStore(name, engine) {
+  Store.call(this, name, engine);
 }
 AddonsStore.prototype = {
   __proto__: Store.prototype,
 
   // Define the add-on types (.type) that we support.
   _syncableTypes: ["extension", "theme"],
 
   _extensionsPrefs: new Preferences("extensions."),
 
   get reconciler() {
     return this.engine._reconciler;
   },
 
-  get engine() {
-    // Ideally we'd link to a specific object, but the API doesn't provide an
-    // easy way to faciliate this. When the async API lands, this hackiness can
-    // go away.
-    return Engines.get("addons");
-  },
-
   /**
    * Override applyIncoming to filter out records we can't handle.
    */
   applyIncoming: function applyIncoming(record) {
     // The fields we look at aren't present when the record is deleted.
     if (!record.deleted) {
       // Ignore records not belonging to our application ID because that is the
       // current policy.
@@ -643,35 +636,31 @@ AddonsStore.prototype = {
   },
 };
 
 /**
  * The add-ons tracker keeps track of real-time changes to add-ons.
  *
  * It hooks up to the reconciler and receives notifications directly from it.
  */
-function AddonsTracker(name) {
-  Tracker.call(this, name);
+function AddonsTracker(name, engine) {
+  Tracker.call(this, name, engine);
 
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 AddonsTracker.prototype = {
   __proto__: Tracker.prototype,
 
-  get engine() {
-    return Engines.get("addons");
-  },
-
   get reconciler() {
-    return Engines.get("addons")._reconciler;
+    return this.engine._reconciler;
   },
 
   get store() {
-    return Engines.get("addons")._store;
+    return this.engine._store;
   },
 
   /**
    * This callback is executed whenever the AddonsReconciler sends out a change
    * notification. See AddonsReconciler.addChangeListener().
    */
   changeListener: function changeHandler(date, change, addon) {
     this._log.debug("changeListener invoked: " + change + " " + addon.id);
--- a/services/sync/modules/engines/apps.js
+++ b/services/sync/modules/engines/apps.js
@@ -21,18 +21,18 @@ function AppRec(collection, id) {
 
 AppRec.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.App"
 }
 
 Utils.deferGetSet(AppRec, "cleartext", ["value"]);
 
-function AppStore(name) {
-  Store.call(this, name);
+function AppStore(name, engine) {
+  Store.call(this, name, engine);
 }
 
 AppStore.prototype = {
   __proto__: Store.prototype,
 
   getAllIDs: function getAllIDs() {
     let apps = DOMApplicationRegistry.getAllIDs();
     return apps;
@@ -76,18 +76,18 @@ AppStore.prototype = {
   wipe: function wipe(record) {
     let callback = Async.makeSyncCallback();
     DOMApplicationRegistry.wipe(callback);
     Async.waitForSyncCallback(callback);
   }
 }
 
 
-function AppTracker(name) {
-  Tracker.call(this, name);
+function AppTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 
 AppTracker.prototype = {
   __proto__: Tracker.prototype,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   
@@ -118,18 +118,18 @@ AppTracker.prototype = {
         this._enabled = false;
         Svc.Obs.remove("webapps-sync-install", this);
         Svc.Obs.remove("webapps-sync-uninstall", this);
         break;
     }
   }
 }
 
-function AppsEngine() {
-  SyncEngine.call(this, "Apps");
+function AppsEngine(service) {
+  SyncEngine.call(this, "Apps", service);
 }
 
 AppsEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: AppStore,
   _trackerObj: AppTracker,
   _recordObj: AppRec,
   applyIncomingBatchSize: APPS_STORE_BATCH_SIZE
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -188,18 +188,18 @@ let kSpecialIds = {
   get menu()    PlacesUtils.bookmarksMenuFolderId,
   get places()  PlacesUtils.placesRootId,
   get tags()    PlacesUtils.tagsFolderId,
   get toolbar() PlacesUtils.toolbarFolderId,
   get unfiled() PlacesUtils.unfiledBookmarksFolderId,
   get mobile()  this.findMobileRoot(true),
 };
 
-function BookmarksEngine() {
-  SyncEngine.call(this, "Bookmarks");
+function BookmarksEngine(service) {
+  SyncEngine.call(this, "Bookmarks", service);
 }
 BookmarksEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _recordObj: PlacesItem,
   _storeObj: BookmarksStore,
   _trackerObj: BookmarksTracker,
   version: 2,
 
@@ -405,18 +405,18 @@ BookmarksEngine.prototype = {
   _findDupe: function _findDupe(item) {
     // Don't bother finding a dupe if the incoming item has duplicates
     if (item.hasDupe)
       return;
     return this._mapDupe(item);
   }
 };
 
-function BookmarksStore(name) {
-  Store.call(this, name);
+function BookmarksStore(name, engine) {
+  Store.call(this, name, engine);
 
   // Explicitly nullify our references to our cached services so we don't leak
   Svc.Obs.add("places-shutdown", function() {
     for each ([query, stmt] in Iterator(this._stmts)) {
       stmt.finalize();
     }
     this._stmts = {};
   }, this);
@@ -1250,18 +1250,18 @@ BookmarksStore.prototype = {
       if (guid != "places") {
         let id = kSpecialIds.specialIdForGUID(guid);
         if (id)
           PlacesUtils.bookmarks.removeFolderChildren(id);
       }
   }
 };
 
-function BookmarksTracker(name) {
-  Tracker.call(this, name);
+function BookmarksTracker(name, engine) {
+  Tracker.call(this, name, engine);
 
   Svc.Obs.add("places-shutdown", this);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 BookmarksTracker.prototype = {
   __proto__: Tracker.prototype,
 
@@ -1293,17 +1293,17 @@ BookmarksTracker.prototype = {
         break;
       case "bookmarks-restore-success":
         this._log.debug("Tracking all items on successful import.");
         this.ignoreAll = false;
         
         this._log.debug("Restore succeeded: wiping server and other clients.");
         Weave.Service.resetClient([this.name]);
         Weave.Service.wipeServer([this.name]);
-        Clients.sendCommand("wipeEngine", [this.name]);
+        this.engine.service.clientsEngine.sendCommand("wipeEngine", [this.name]);
         break;
       case "bookmarks-restore-failed":
         this._log.debug("Tracking all items on failed import.");
         this.ignoreAll = false;
         break;
     }
   },
 
--- a/services/sync/modules/engines/clients.js
+++ b/services/sync/modules/engines/clients.js
@@ -1,47 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const EXPORTED_SYMBOLS = ["Clients", "ClientsRec"];
+const EXPORTED_SYMBOLS = [
+  "ClientEngine",
+  "ClientsRec"
+];
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://services-common/stringbundle.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-sync/main.js");
 
 const CLIENTS_TTL = 1814400; // 21 days
 const CLIENTS_TTL_REFRESH = 604800; // 7 days
 
 function ClientsRec(collection, id) {
   CryptoWrapper.call(this, collection, id);
 }
 ClientsRec.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.Clients",
   ttl: CLIENTS_TTL
 };
 
 Utils.deferGetSet(ClientsRec, "cleartext", ["name", "type", "commands"]);
 
 
-XPCOMUtils.defineLazyGetter(this, "Clients", function () {
-  return new ClientEngine();
-});
-
-function ClientEngine() {
-  SyncEngine.call(this, "Clients");
+function ClientEngine(service) {
+  SyncEngine.call(this, "Clients", service);
 
   // Reset the client on every startup so that we fetch recent clients
   this._resetClient();
 }
 ClientEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: ClientStore,
   _recordObj: ClientsRec,
@@ -235,26 +231,26 @@ ClientEngine.prototype = {
         this._log.debug("Processing command: " + command + "(" + args + ")");
 
         let engines = [args[0]];
         switch (command) {
           case "resetAll":
             engines = null;
             // Fallthrough
           case "resetEngine":
-            Weave.Service.resetClient(engines);
+            this.service.resetClient(engines);
             break;
           case "wipeAll":
             engines = null;
             // Fallthrough
           case "wipeEngine":
-            Weave.Service.wipeClient(engines);
+            this.service.wipeClient(engines);
             break;
           case "logout":
-            Weave.Service.logout();
+            this.service.logout();
             return false;
           case "displayURI":
             this._handleDisplayURI.apply(this, args);
             break;
           default:
             this._log.debug("Received an unknown command: " + command);
             break;
         }
@@ -319,17 +315,17 @@ ClientEngine.prototype = {
    * @param title
    *        Title of the page being sent.
    */
   sendURIToClientForDisplay: function sendURIToClientForDisplay(uri, clientId, title) {
     this._log.info("Sending URI to client: " + uri + " -> " +
                    clientId + " (" + title + ")");
     this.sendCommand("displayURI", [uri, this.localID, title], clientId);
 
-    Clients._tracker.score += SCORE_INCREMENT_XLARGE;
+    this._tracker.score += SCORE_INCREMENT_XLARGE;
   },
 
   /**
    * Handle a single received 'displayURI' command.
    *
    * Interested parties should observe the "weave:engine:clients:display-uri"
    * topic. The callback will receive an object as the subject parameter with
    * the following keys:
@@ -352,64 +348,64 @@ ClientEngine.prototype = {
     this._log.info("Received a URI for display: " + uri + " (" + title +
                    ") from " + clientId);
 
     let subject = {uri: uri, client: clientId, title: title};
     Svc.Obs.notify("weave:engine:clients:display-uri", subject);
   }
 };
 
-function ClientStore(name) {
-  Store.call(this, name);
+function ClientStore(name, engine) {
+  Store.call(this, name, engine);
 }
 ClientStore.prototype = {
   __proto__: Store.prototype,
 
   create: function create(record) this.update(record),
 
   update: function update(record) {
     // Only grab commands from the server; local name/type always wins
-    if (record.id == Clients.localID)
-      Clients.localCommands = record.commands;
+    if (record.id == this.engine.localID)
+      this.engine.localCommands = record.commands;
     else
       this._remoteClients[record.id] = record.cleartext;
   },
 
   createRecord: function createRecord(id, collection) {
     let record = new ClientsRec(collection, id);
 
     // Package the individual components into a record for the local client
-    if (id == Clients.localID) {
-      record.name = Clients.localName;
-      record.type = Clients.localType;
-      record.commands = Clients.localCommands;
+    if (id == this.engine.localID) {
+      record.name = this.engine.localName;
+      record.type = this.engine.localType;
+      record.commands = this.engine.localCommands;
     }
     else
       record.cleartext = this._remoteClients[id];
 
     return record;
   },
 
   itemExists: function itemExists(id) id in this.getAllIDs(),
 
   getAllIDs: function getAllIDs() {
     let ids = {};
-    ids[Clients.localID] = true;
+    ids[this.engine.localID] = true;
     for (let id in this._remoteClients)
       ids[id] = true;
     return ids;
   },
 
   wipe: function wipe() {
     this._remoteClients = {};
   },
 };
 
-function ClientsTracker(name) {
-  Tracker.call(this, name);
+function ClientsTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 ClientsTracker.prototype = {
   __proto__: Tracker.prototype,
 
   _enabled: false,
 
--- a/services/sync/modules/engines/forms.js
+++ b/services/sync/modules/engines/forms.js
@@ -146,18 +146,18 @@ let FormWrapper = {
     let stmt = this._replaceGUIDStmt;
     stmt.params.oldGUID = oldGUID;
     stmt.params.newGUID = newGUID;
     Async.querySpinningly(stmt);
   }
 
 };
 
-function FormEngine() {
-  SyncEngine.call(this, "Forms");
+function FormEngine(service) {
+  SyncEngine.call(this, "Forms", service);
 }
 FormEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: FormStore,
   _trackerObj: FormTracker,
   _recordObj: FormRec,
   applyIncomingBatchSize: FORMS_STORE_BATCH_SIZE,
 
@@ -165,18 +165,18 @@ FormEngine.prototype = {
 
   _findDupe: function _findDupe(item) {
     if (Svc.Form.entryExists(item.name, item.value)) {
       return FormWrapper.getGUID(item.name, item.value);
     }
   }
 };
 
-function FormStore(name) {
-  Store.call(this, name);
+function FormStore(name, engine) {
+  Store.call(this, name, engine);
 }
 FormStore.prototype = {
   __proto__: Store.prototype,
 
   applyIncomingBatch: function applyIncomingBatch(records) {
     return Utils.runInTransaction(Svc.Form.DBConnection, function() {
       return Store.prototype.applyIncomingBatch.call(this, records);
     }, this);
@@ -236,18 +236,18 @@ FormStore.prototype = {
     this._log.trace("Ignoring form record update request!");
   },
 
   wipe: function FormStore_wipe() {
     Svc.Form.removeAllEntries();
   }
 };
 
-function FormTracker(name) {
-  Tracker.call(this, name);
+function FormTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
   Svc.Obs.add("profile-change-teardown", this);
 }
 FormTracker.prototype = {
   __proto__: Tracker.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -7,49 +7,49 @@ const EXPORTED_SYMBOLS = ['HistoryEngine
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const HISTORY_TTL = 5184000; // 60 days
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://services-common/async.js");
+Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-common/log4moz.js");
 
 function HistoryRec(collection, id) {
   CryptoWrapper.call(this, collection, id);
 }
 HistoryRec.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.History",
   ttl: HISTORY_TTL
 };
 
 Utils.deferGetSet(HistoryRec, "cleartext", ["histUri", "title", "visits"]);
 
 
-function HistoryEngine() {
-  SyncEngine.call(this, "History");
+function HistoryEngine(service) {
+  SyncEngine.call(this, "History", service);
 }
 HistoryEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _recordObj: HistoryRec,
   _storeObj: HistoryStore,
   _trackerObj: HistoryTracker,
   downloadLimit: MAX_HISTORY_DOWNLOAD,
   applyIncomingBatchSize: HISTORY_STORE_BATCH_SIZE
 };
 
-function HistoryStore(name) {
-  Store.call(this, name);
+function HistoryStore(name, engine) {
+  Store.call(this, name, engine);
 
   // Explicitly nullify our references to our cached services so we don't leak
   Svc.Obs.add("places-shutdown", function() {
     for each ([query, stmt] in Iterator(this._stmts)) {
       stmt.finalize();
     }
     this._stmts = {};
   }, this);
@@ -349,18 +349,18 @@ HistoryStore.prototype = {
     return record;
   },
 
   wipe: function HistStore_wipe() {
     PlacesUtils.history.removeAllPages();
   }
 };
 
-function HistoryTracker(name) {
-  Tracker.call(this, name);
+function HistoryTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 HistoryTracker.prototype = {
   __proto__: Tracker.prototype,
 
   _enabled: false,
   observe: function observe(subject, topic, data) {
--- a/services/sync/modules/engines/passwords.js
+++ b/services/sync/modules/engines/passwords.js
@@ -21,18 +21,18 @@ LoginRec.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.Login",
 };
 
 Utils.deferGetSet(LoginRec, "cleartext", ["hostname", "formSubmitURL",
   "httpRealm", "username", "password", "usernameField", "passwordField"]);
 
 
-function PasswordEngine() {
-  SyncEngine.call(this, "Passwords");
+function PasswordEngine(service) {
+  SyncEngine.call(this, "Passwords", service);
 }
 PasswordEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: PasswordStore,
   _trackerObj: PasswordTracker,
   _recordObj: LoginRec,
   applyIncomingBatchSize: PASSWORDS_STORE_BATCH_SIZE,
 
@@ -70,18 +70,18 @@ PasswordEngine.prototype = {
 
     // Look for existing logins that match the hostname but ignore the password
     for each (let local in logins)
       if (login.matches(local, true) && local instanceof Ci.nsILoginMetaInfo)
         return local.guid;
   }
 };
 
-function PasswordStore(name) {
-  Store.call(this, name);
+function PasswordStore(name, engine) {
+  Store.call(this, name, engine);
   this._nsLoginInfo = new Components.Constructor(
     "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
 
   XPCOMUtils.defineLazyGetter(this, "DBConnection", function() {
     return Services.logins.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.mozIStorageConnection);
   });
 }
@@ -250,18 +250,18 @@ PasswordStore.prototype = {
     }
   },
 
   wipe: function PasswordStore_wipe() {
     Services.logins.removeAllLogins();
   }
 };
 
-function PasswordTracker(name) {
-  Tracker.call(this, name);
+function PasswordTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 PasswordTracker.prototype = {
   __proto__: Tracker.prototype,
 
   _enabled: false,
   observe: function PasswordTracker_observe(aSubject, aTopic, aData) {
--- a/services/sync/modules/engines/prefs.js
+++ b/services/sync/modules/engines/prefs.js
@@ -26,18 +26,18 @@ function PrefRec(collection, id) {
 PrefRec.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.Pref",
 };
 
 Utils.deferGetSet(PrefRec, "cleartext", ["value"]);
 
 
-function PrefsEngine() {
-  SyncEngine.call(this, "Prefs");
+function PrefsEngine(service) {
+  SyncEngine.call(this, "Prefs", service);
 }
 PrefsEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: PrefStore,
   _trackerObj: PrefTracker,
   _recordObj: PrefRec,
   version: 2,
 
@@ -60,18 +60,18 @@ PrefsEngine.prototype = {
       this.justWiped = false;
       return true;
     }
     return SyncEngine.prototype._reconcile.call(this, item);
   }
 };
 
 
-function PrefStore(name) {
-  Store.call(this, name);
+function PrefStore(name, engine) {
+  Store.call(this, name, engine);
   Svc.Obs.add("profile-before-change", function() {
     this.__prefs = null;
   }, this);
 }
 PrefStore.prototype = {
   __proto__: Store.prototype,
 
  __prefs: null,
@@ -183,18 +183,18 @@ PrefStore.prototype = {
     this._setAllPrefs(record.value);
   },
 
   wipe: function PrefStore_wipe() {
     this._log.trace("Ignoring wipe request");
   }
 };
 
-function PrefTracker(name) {
-  Tracker.call(this, name);
+function PrefTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("profile-before-change", this);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 }
 PrefTracker.prototype = {
   __proto__: Tracker.prototype,
 
   get modified() {
--- a/services/sync/modules/engines/tabs.js
+++ b/services/sync/modules/engines/tabs.js
@@ -34,33 +34,33 @@ TabSetRecord.prototype = {
   __proto__: CryptoWrapper.prototype,
   _logName: "Sync.Record.Tabs",
   ttl: TABS_TTL
 };
 
 Utils.deferGetSet(TabSetRecord, "cleartext", ["clientName", "tabs"]);
 
 
-function TabEngine() {
-  SyncEngine.call(this, "Tabs");
+function TabEngine(service) {
+  SyncEngine.call(this, "Tabs", service);
 
   // Reset the client on every startup so that we fetch recent tabs
   this._resetClient();
 }
 TabEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: TabStore,
   _trackerObj: TabTracker,
   _recordObj: TabSetRecord,
 
   getChangedIDs: function getChangedIDs() {
     // No need for a proper timestamp (no conflict resolution needed).
     let changedIDs = {};
     if (this._tracker.modified)
-      changedIDs[Clients.localID] = 0;
+      changedIDs[this.service.clientsEngine.localID] = 0;
     return changedIDs;
   },
 
   // API for use by Weave UI code to give user choices of tabs to open:
   getAllClients: function TabEngine_getAllClients() {
     return this._store._remoteClients;
   },
 
@@ -70,17 +70,17 @@ TabEngine.prototype = {
 
   _resetClient: function TabEngine__resetClient() {
     SyncEngine.prototype._resetClient.call(this);
     this._store.wipe();
     this._tracker.modified = true;
   },
 
   removeClientData: function removeClientData() {
-    new Resource(this.engineURL + "/" + Clients.localID).delete();
+    new Resource(this.engineURL + "/" + this.service.clientsEngine.localID).delete();
   },
 
   /* The intent is not to show tabs in the menu if they're already
    * open locally.  There are a couple ways to interpret this: for
    * instance, we could do it by removing a tab from the list when
    * you open it -- but then if you close it, you can't get back to
    * it.  So the way I'm doing it here is to not show a tab in the menu
    * if you have a tab open to the same URL, even though this means
@@ -90,24 +90,24 @@ TabEngine.prototype = {
   locallyOpenTabMatchesURL: function TabEngine_localTabMatches(url) {
     return this._store.getAllTabs().some(function(tab) {
       return tab.urlHistory[0] == url;
     });
   }
 };
 
 
-function TabStore(name) {
-  Store.call(this, name);
+function TabStore(name, engine) {
+  Store.call(this, name, engine);
 }
 TabStore.prototype = {
   __proto__: Store.prototype,
 
   itemExists: function TabStore_itemExists(id) {
-    return id == Clients.localID;
+    return id == this.engine.service.clientsEngine.localID;
   },
 
   /**
    * Return the recorded last used time of the provided tab, or
    * 0 if none is present.
    * The result will always be an integer value.
    */
   tabLastUsed: function tabLastUsed(tab) {
@@ -152,17 +152,17 @@ TabStore.prototype = {
       });
     });
 
     return allTabs;
   },
 
   createRecord: function createRecord(id, collection) {
     let record = new TabSetRecord(collection, id);
-    record.clientName = Clients.localName;
+    record.clientName = this.engine.service.clientsEngine.localName;
 
     // Don't provide any tabs to compare against and ignore the update later.
     if (Svc.Private && Svc.Private.privateBrowsingEnabled && !PBPrefs.get("autostart")) {
       record.tabs = [];
       return record;
     }
 
     // Sort tabs in descending-used order to grab the most recently used
@@ -195,17 +195,17 @@ TabStore.prototype = {
   },
 
   getAllIDs: function TabStore_getAllIds() {
     // Don't report any tabs if we're in private browsing for first syncs.
     let ids = {};
     if (Svc.Private && Svc.Private.privateBrowsingEnabled && !PBPrefs.get("autostart"))
       return ids;
 
-    ids[Clients.localID] = true;
+    ids[this.engine.service.clientsEngine.localID] = true;
     return ids;
   },
 
   wipe: function TabStore_wipe() {
     this._remoteClients = {};
   },
 
   create: function TabStore_create(record) {
@@ -227,18 +227,18 @@ TabStore.prototype = {
   },
 
   update: function update(record) {
     this._log.trace("Ignoring tab updates as local ones win");
   }
 };
 
 
-function TabTracker(name) {
-  Tracker.call(this, name);
+function TabTracker(name, engine) {
+  Tracker.call(this, name, engine);
   Svc.Obs.add("weave:engine:start-tracking", this);
   Svc.Obs.add("weave:engine:stop-tracking", this);
 
   // Make sure "this" pointer is always set correctly for event listeners
   this.onTab = Utils.bind2(this, this.onTab);
   this._unregisterListeners = Utils.bind2(this, this._unregisterListeners);
 }
 TabTracker.prototype = {
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -7,17 +7,16 @@ const EXPORTED_SYMBOLS = [
   "SyncScheduler",
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 
 function SyncScheduler(service) {
   this.service = service;
   this.init();
 }
 SyncScheduler.prototype = {
@@ -256,33 +255,33 @@ SyncScheduler.prototype = {
       this.syncInterval = this.immediateInterval;
     } else {
       this._log.trace("Adjusting syncInterval to activeInterval.");
       this.syncInterval = this.activeInterval;
     }
   },
 
   calculateScore: function calculateScore() {
-    let engines = [Clients].concat(Engines.getEnabled());
+    let engines = [this.service.clientsEngine].concat(this.service.engineManager.getEnabled());
     for (let i = 0;i < engines.length;i++) {
       this._log.trace(engines[i].name + ": score: " + engines[i].score);
       this.globalScore += engines[i].score;
       engines[i]._tracker.resetScore();
     }
 
     this._log.trace("Global score updated: " + this.globalScore);
     this.checkSyncStatus();
   },
 
   /**
    * Process the locally stored clients list to figure out what mode to be in
    */
   updateClientMode: function updateClientMode() {
     // Nothing to do if it's the same amount
-    let numClients = Clients.stats.numClients;
+    let numClients = this.service.clientsEngine.stats.numClients;
     if (this.numClients == numClients)
       return;
 
     this._log.debug("Client count: " + this.numClients + " -> " + numClients);
     this.numClients = numClients;
 
     if (numClients <= 1) {
       this._log.trace("Adjusting syncThreshold to SINGLE_USER_THRESHOLD");
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -49,26 +49,16 @@ const ENGINE_MODULES = {
   Tab: "tabs.js",
 };
 
 const STORAGE_INFO_TYPES = [INFO_COLLECTIONS,
                             INFO_COLLECTION_USAGE,
                             INFO_COLLECTION_COUNTS,
                             INFO_QUOTA];
 
-/*
- * Service singleton
- * Main entry point into Weave's sync framework
- */
-XPCOMUtils.defineLazyGetter(this, "Service", function onService() {
-  let service = new WeaveSvc();
-  service.onStartup();
-
-  return service;
-});
 
 function WeaveSvc() {
   this._notify = Utils.notify("weave:service:");
 }
 WeaveSvc.prototype = {
 
   _lock: Utils.lock,
   _locked: false,
@@ -407,29 +397,27 @@ WeaveSvc.prototype = {
     oldPref.resetBranch("");
     Svc.Prefs.set("migrated", true);
   },
 
   /**
    * Register the built-in engines for certain applications
    */
   _registerEngines: function _registerEngines() {
-    // TODO Singleton (bug 785225).
-    this.engineManager = Engines;
+    this.engineManager = new EngineManager(this);
 
     let engines = [];
     // Applications can provide this preference (comma-separated list)
     // to specify which engines should be registered on startup.
     let pref = Svc.Prefs.get("registerEngines");
     if (pref) {
       engines = pref.split(",");
     }
 
-    // TODO Singleton (bug 785225).
-    this.clientsEngine = Clients;
+    this.clientsEngine = new ClientEngine(this);
 
     for (let name of engines) {
       if (!name in ENGINE_MODULES) {
         this._log.info("Do not know about engine: " + name);
         continue;
       }
 
       let ns = {};
@@ -437,17 +425,17 @@ WeaveSvc.prototype = {
         Cu.import("resource://services-sync/engines/" + ENGINE_MODULES[name], ns);
 
         let engineName = name + "Engine";
         if (!(engineName in ns)) {
           this._log.warn("Could not find exported engine instance: " + engineName);
           continue;
         }
 
-        this.engineManager.register(ns[engineName]);
+        this.engineManager.register(ns[engineName], this);
       } catch (ex) {
         this._log.warn("Could not register engine " + name + ": " +
                        CommonUtils.exceptionStr(ex));
       }
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
@@ -1453,8 +1441,11 @@ WeaveSvc.prototype = {
                         "': " + this.response.body);
         return callback(ex);
       }
       this._log.trace("Successfully retrieved '" + info_type + "'.");
       return callback(null, result);
     });
   },
 };
+
+let Service = new WeaveSvc();
+Service.onStartup();
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -285,18 +285,18 @@ function generateNewKeys(collections) {
 function RotaryRecord(collection, id) {
   CryptoWrapper.call(this, collection, id);
 }
 RotaryRecord.prototype = {
   __proto__: CryptoWrapper.prototype
 };
 Utils.deferGetSet(RotaryRecord, "cleartext", ["denomination"]);
 
-function RotaryStore() {
-  Store.call(this, "Rotary");
+function RotaryStore(engine) {
+  Store.call(this, "Rotary", engine);
   this.items = {};
 }
 RotaryStore.prototype = {
   __proto__: Store.prototype,
 
   create: function Store_create(record) {
     this.items[record.id] = record.denomination;
   },
@@ -341,26 +341,26 @@ RotaryStore.prototype = {
     return ids;
   },
 
   wipe: function() {
     this.items = {};
   }
 };
 
-function RotaryTracker() {
-  Tracker.call(this, "Rotary");
+function RotaryTracker(engine) {
+  Tracker.call(this, "Rotary", engine);
 }
 RotaryTracker.prototype = {
   __proto__: Tracker.prototype
 };
 
 
-function RotaryEngine() {
-  SyncEngine.call(this, "Rotary");
+function RotaryEngine(service) {
+  SyncEngine.call(this, "Rotary", service);
   // Ensure that the engine starts with a clean slate.
   this.toFetch        = [];
   this.previousFailed = [];
 }
 RotaryEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: RotaryStore,
   _trackerObj: RotaryTracker,
--- a/services/sync/tests/unit/test_addons_engine.js
+++ b/services/sync/tests/unit/test_addons_engine.js
@@ -1,30 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://services-common/async.js");
+Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-sync/addonsreconciler.js");
-Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/engines/addons.js");
-Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-sync/service.js");
 
 let prefs = new Preferences();
 prefs.set("extensions.getAddons.get.url",
           "http://localhost:8888/search/guid:%IDS%");
 
 loadAddonTestFunctions();
 startupManager();
 
-Engines.register(AddonsEngine);
-let engine = Engines.get("addons");
+let engineManager = Service.engineManager;
+
+engineManager.register(AddonsEngine);
+let engine = engineManager.get("addons");
 let reconciler = engine._reconciler;
 let tracker = engine._tracker;
 
 function advance_test() {
   reconciler._addons = {};
   reconciler._changes = [];
 
   let cb = Async.makeSpinningCallback();
--- a/services/sync/tests/unit/test_addons_reconciler.js
+++ b/services/sync/tests/unit/test_addons_reconciler.js
@@ -1,28 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://services-sync/addonsreconciler.js");
 Cu.import("resource://services-sync/engines/addons.js");
-Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://services-sync/service.js");
 
 loadAddonTestFunctions();
 startupManager();
 
 function run_test() {
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.AddonsReconciler").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.AddonsReconciler").level =
     Log4Moz.Level.Trace;
 
   Svc.Prefs.set("engine.addons", true);
-  Engines.register(AddonsEngine);
+  Service.engineManager.register(AddonsEngine);
 
   run_next_test();
 }
 
 add_test(function test_defaults() {
   _("Ensure new objects have reasonable defaults.");
 
   let reconciler = new AddonsReconciler();
--- a/services/sync/tests/unit/test_addons_store.js
+++ b/services/sync/tests/unit/test_addons_store.js
@@ -1,28 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-sync/addonutils.js");
 Cu.import("resource://services-sync/engines/addons.js");
+Cu.import("resource://services-sync/service.js");
 
 const HTTP_PORT = 8888;
 
 let prefs = new Preferences();
 
 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
 prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
 loadAddonTestFunctions();
 startupManager();
 
-Engines.register(AddonsEngine);
-let engine     = Engines.get("addons");
+Service.engineManager.register(AddonsEngine);
+let engine     = Service.engineManager.get("addons");
 let tracker    = engine._tracker;
 let store      = engine._store;
 let reconciler = engine._reconciler;
 
 /**
  * Create a AddonsRec for this application with the fields specified.
  *
  * @param  id       Sync GUID of record
--- a/services/sync/tests/unit/test_addons_tracker.js
+++ b/services/sync/tests/unit/test_addons_tracker.js
@@ -1,25 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://services-sync/engines/addons.js");
 Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://gre/modules/AddonManager.jsm");
 
 loadAddonTestFunctions();
 startupManager();
 Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
 Svc.Prefs.set("engine.addons", true);
 
-Engines.register(AddonsEngine);
-let engine     = Engines.get("addons");
+Service.engineManager.register(AddonsEngine);
+let engine     = Service.engineManager.get("addons");
 let reconciler = engine._reconciler;
 let store      = engine._store;
 let tracker    = engine._tracker;
 
 const addon1ID = "addon1@tests.mozilla.org";
 
 function cleanup_and_advance() {
   Svc.Obs.notify("weave:engine:stop-tracking");
--- a/services/sync/tests/unit/test_bookmark_batch_fail.js
+++ b/services/sync/tests/unit/test_bookmark_batch_fail.js
@@ -1,13 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 _("Making sure a failing sync reports a useful error");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/service.js");
 
 function run_test() {
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   engine._syncStartup = function() {
     throw "FAIL!";
   };
 
   try {
     _("Try calling the sync that should throw right away");
     engine._sync();
     do_throw("Should have failed sync!");
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -1,26 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://services-common/async.js");
+Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-common/log4moz.js");
-Cu.import("resource://services-common/async.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
-Cu.import("resource://services-sync/service.js");
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-
-Engines.register(BookmarksEngine);
+Service.engineManager.register(BookmarksEngine);
 var syncTesting = new SyncTestingInfrastructure();
 
 add_test(function bad_record_allIDs() {
   let syncTesting = new SyncTestingInfrastructure();
 
   _("Ensure that bad Places queries don't cause an error in getAllIDs.");
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
   let badRecordID = PlacesUtils.bookmarks.insertBookmark(
       PlacesUtils.bookmarks.toolbarFolder,
       Utils.makeURI("place:folder=1138"),
       PlacesUtils.bookmarks.DEFAULT_INDEX,
       null);
 
   do_check_true(badRecordID > 0);
@@ -41,17 +43,17 @@ add_test(function bad_record_allIDs() {
   PlacesUtils.bookmarks.removeItem(badRecordID);
   run_next_test();
 });
 
 add_test(function test_ID_caching() {
   let syncTesting = new SyncTestingInfrastructure();
 
   _("Ensure that Places IDs are not cached.");
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
   _("All IDs: " + JSON.stringify(store.getAllIDs()));
 
   let mobileID = store.idForGUID("mobile");
   _("Change the GUID for that item, and drop the mobile anno.");
   store._setGUID(mobileID, "abcdefghijkl");
   PlacesUtils.annotations.removeItemAnnotation(mobileID, "mobile/bookmarksRoot");
 
@@ -89,17 +91,17 @@ function serverForFoo(engine) {
     bookmarks: {}
   });
 }
 
 add_test(function test_processIncoming_error_orderChildren() {
   _("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
   new SyncTestingInfrastructure();
 
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
 
   let collection = server.user("foo").collection("bookmarks");
 
   try {
 
     let folder1_id = PlacesUtils.bookmarks.createFolder(
@@ -159,17 +161,17 @@ add_test(function test_processIncoming_e
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_restorePromptsReupload() {
   _("Ensure that restoring from a backup will reupload all records.");
   new SyncTestingInfrastructure();
 
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
 
   let collection = server.user("foo").collection("bookmarks");
 
   Svc.Obs.notify("weave:engine:start-tracking");   // We skip usual startup...
 
   try {
@@ -324,17 +326,17 @@ add_test(function test_mismatched_types(
       ["HCRq40Rnxhrd", "YeyWCV1RVsYw", "GCceVZMhvMbP", "sYi2hevdArlF",
        "vjbZlPlSyGY8", "UtjUhVyrpeG6", "rVq8WMG2wfZI", "Lx0tcy43ZKhZ",
        "oT74WwV8_j4P", "IztsItWVSo3-"],
     "parentid": "toolbar"
   };
 
   new SyncTestingInfrastructure();
 
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
 
   _("GUID: " + store.GUIDForId(6, true));
 
   try {
     let bms = PlacesUtils.bookmarks;
     let oldR = new FakeRecord(BookmarkFolder, oldRecord);
@@ -367,17 +369,17 @@ add_test(function test_mismatched_types(
   }
 });
 
 add_test(function test_bookmark_guidMap_fail() {
   _("Ensure that failures building the GUID map cause early death.");
 
   new SyncTestingInfrastructure();
 
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
 
   let store  = engine._store;
   let server = serverForFoo(engine);
   let coll   = server.user("foo").collection("bookmarks");
 
   // Add one item to the server.
   let itemID = PlacesUtils.bookmarks.createFolder(
@@ -414,17 +416,17 @@ add_test(function test_bookmark_guidMap_
     err = ex;
   }
   do_check_eq(err, "Nooo");
 
   server.stop(run_next_test);
 });
 
 add_test(function test_bookmark_is_taggable() {
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
 
   do_check_true(store.isTaggable("bookmark"));
   do_check_true(store.isTaggable("microsummary"));
   do_check_true(store.isTaggable("query"));
   do_check_false(store.isTaggable("folder"));
   do_check_false(store.isTaggable("livemark"));
   do_check_false(store.isTaggable(null));
@@ -432,17 +434,17 @@ add_test(function test_bookmark_is_tagga
   do_check_false(store.isTaggable(""));
 
   run_next_test();
 });
 
 add_test(function test_bookmark_tag_but_no_uri() {
   _("Ensure that a bookmark record with tags, but no URI, doesn't throw an exception.");
 
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
 
   // We're simply checking that no exception is thrown, so
   // no actual checks in this test.
  
   store._tagURI(null, ["foo"]);
   store._tagURI(null, null);
   store._tagURI(Utils.makeURI("about:fake"), null);
--- a/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js
+++ b/services/sync/tests/unit/test_bookmark_legacy_microsummaries_support.js
@@ -1,23 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that Sync can correctly handle a legacy microsummary record
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
 
+Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-common/log4moz.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-
 const GENERATORURI_ANNO = "microsummary/generatorURI";
 const STATICTITLE_ANNO = "bookmarks/staticTitle";
 
 const TEST_URL = "http://micsum.mozilla.org/";
 const TEST_TITLE = "A microsummarized bookmark"
 const GENERATOR_URL = "http://generate.micsum/"
 const STATIC_TITLE = "Static title"
 
@@ -32,18 +32,18 @@ function newMicrosummary(url, title) {
   PlacesUtils.annotations.setItemAnnotation(id, STATICTITLE_ANNO,
                                             "Static title", 0,
                                             PlacesUtils.annotations.EXPIRE_NEVER);
   return id;
 }
 
 function run_test() {
 
-  Engines.register(BookmarksEngine);
-  let engine = Engines.get("bookmarks");
+  Service.engineManager.register(BookmarksEngine);
+  let engine = Service.engineManager.get("bookmarks");
   let store = engine._store;
 
   // Clean up.
   store.wipe();
 
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.Engine.Bookmarks").level = Log4Moz.Level.Trace;
 
--- a/services/sync/tests/unit/test_bookmark_livemarks.js
+++ b/services/sync/tests/unit/test_bookmark_livemarks.js
@@ -7,17 +7,17 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://testing-common/services-common/utils.js");
 
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 
-let engine = Engines.get("bookmarks");
+let engine = Service.engineManager.get("bookmarks");
 let store = engine._store;
 
 // Record borrowed from Bug 631361.
 let record631361 = {
   id: "M5bwUKK8hPyF",
   index: 150,
   modified: 1296768176.49,
   payload:
--- a/services/sync/tests/unit/test_bookmark_order.js
+++ b/services/sync/tests/unit/test_bookmark_order.js
@@ -1,10 +1,14 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 _("Making sure after processing incoming bookmarks, they show up in the right order");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function getBookmarks(folderId) {
   let bookmarks = [];
 
   let pos = 0;
   while (true) {
     let itemId = PlacesUtils.bookmarks.getIdForItemAt(folderId, pos);
@@ -33,17 +37,17 @@ function check(expected) {
   let bookmarks = getBookmarks(PlacesUtils.bookmarks.unfiledBookmarksFolder);
 
   _("Checking if the bookmark structure is", JSON.stringify(expected));
   _("Got bookmarks:", JSON.stringify(bookmarks));
   do_check_true(Utils.deepEquals(bookmarks, expected));
 }
 
 function run_test() {
-  let store = new BookmarksEngine()._store;
+  let store = new BookmarksEngine(Service)._store;
   initTestLogging("Trace");
 
   _("Starting with a clean slate of no bookmarks");
   store.wipe();
   check([]);
 
   function bookmark(name, parent) {
     let bookmark = new Bookmark("http://weave.server/my-bookmark");
--- a/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
+++ b/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
@@ -1,13 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 _("Rewrite place: URIs.");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
-let engine = new BookmarksEngine();
+let engine = new BookmarksEngine(Service);
 let store = engine._store;
 
 function run_test() {
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.Engine.Bookmarks").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Store.Bookmarks").level = Log4Moz.Level.Trace;
 
   let tagRecord = new BookmarkQuery("bookmarks", "abcdefabcdef");
@@ -16,31 +20,31 @@ function run_test() {
   tagRecord.parentName = "Bookmarks Toolbar";
   tagRecord.bmkUri = uri;
   tagRecord.title = "tagtag";
   tagRecord.folderName = "bar";
 
   _("Type: " + tagRecord.type);
   _("Folder name: " + tagRecord.folderName);
   store.preprocessTagQuery(tagRecord);
-  
+
   _("Verify that the URI has been rewritten.");
   do_check_neq(tagRecord.bmkUri, uri);
-  
+
   let tags = store._getNode(PlacesUtils.tagsFolderId);
   tags.containerOpen = true;
   let tagID;
   for (let i = 0; i < tags.childCount; ++i) {
     let child = tags.getChild(i);
     if (child.title == "bar")
       tagID = child.itemId;
   }
   tags.containerOpen = false;
 
   _("Tag ID: " + tagID);
   do_check_eq(tagRecord.bmkUri, uri.replace("499", tagID));
-  
+
   _("... but not if the type is wrong.");
   let wrongTypeURI = "place:folder=499&type=2&queryType=1";
   tagRecord.bmkUri = wrongTypeURI;
   store.preprocessTagQuery(tagRecord);
   do_check_eq(tagRecord.bmkUri, wrongTypeURI);
 }
--- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
+++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
@@ -8,18 +8,18 @@ Cu.import("resource://services-sync/serv
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 
 const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
 var IOService = Cc["@mozilla.org/network/io-service;1"]
                 .getService(Ci.nsIIOService);
 ("http://www.mozilla.com", null, null);
 
 
-Engines.register(BookmarksEngine);
-let engine = Engines.get("bookmarks");
+Service.engineManager.register(BookmarksEngine);
+let engine = Service.engineManager.get("bookmarks");
 let store = engine._store;
 
 // Clean up after other tests. Only necessary in XULRunner.
 store.wipe();
 
 var syncTesting = new SyncTestingInfrastructure();
 
 function newSmartBookmark(parent, uri, position, title, queryID) {
--- a/services/sync/tests/unit/test_bookmark_store.js
+++ b/services/sync/tests/unit/test_bookmark_store.js
@@ -1,16 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 const PARENT_ANNO = "sync/parent";
 
-Engines.register(BookmarksEngine);
-let engine = Engines.get("bookmarks");
+Service.engineManager.register(BookmarksEngine);
+let engine = Service.engineManager.get("bookmarks");
 let store = engine._store;
 let fxuri = Utils.makeURI("http://getfirefox.com/");
 let tburi = Utils.makeURI("http://getthunderbird.com/");
 
 
 function test_bookmark_create() {
   try {
     _("Ensure the record isn't present yet.");
--- a/services/sync/tests/unit/test_bookmark_tracker.js
+++ b/services/sync/tests/unit/test_bookmark_tracker.js
@@ -1,16 +1,20 @@
-Cu.import("resource://services-sync/engines/bookmarks.js");
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
 
-Engines.register(BookmarksEngine);
-let engine = Engines.get("bookmarks");
+Service.engineManager.register(BookmarksEngine);
+let engine = Service.engineManager.get("bookmarks");
 let store  = engine._store;
 store.wipe();
 
 function test_tracking() {
   _("Verify we've got an empty tracker to work with.");
   let tracker = engine._tracker;
   do_check_empty(tracker.changedIDs);
 
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -1,27 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/identity.js");
-Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
 
 const MORE_THAN_CLIENTS_TTL_REFRESH = 691200; // 8 days
 const LESS_THAN_CLIENTS_TTL_REFRESH = 86400;  // 1 day
 
+let engine = Service.clientsEngine;
+
 add_test(function test_bad_hmac() {
   _("Ensure that Clients engine deletes corrupt records.");
   let contents = {
-    meta: {global: {engines: {clients: {version: Clients.version,
-                                        syncID: Clients.syncID}}}},
+    meta: {global: {engines: {clients: {version: engine.version,
+                                        syncID: engine.syncID}}}},
     clients: {},
     crypto: {}
   };
   let deletedCollections = [];
   let deletedItems       = [];
   let callback = {
     __proto__: SyncServerCallback,
     onItemDeleted: function (username, coll, wboID) {
@@ -59,89 +61,89 @@ add_test(function test_bad_hmac() {
     let passphrase     = "abcdeabcdeabcdeabcdeabcdea";
     Service.serverURL  = TEST_SERVER_URL;
     Service.clusterURL = TEST_CLUSTER_URL;
     Service.login("foo", "ilovejane", passphrase);
 
     generateNewKeys();
 
     _("First sync, client record is uploaded");
-    do_check_eq(Clients.lastRecordUpload, 0);
+    do_check_eq(engine.lastRecordUpload, 0);
     check_clients_count(0);
-    Clients._sync();
+    engine._sync();
     check_clients_count(1);
-    do_check_true(Clients.lastRecordUpload > 0);
+    do_check_true(engine.lastRecordUpload > 0);
 
     // Initial setup can wipe the server, so clean up.
     deletedCollections = [];
     deletedItems       = [];
 
     _("Change our keys and our client ID, reupload keys.");
-    let oldLocalID  = Clients.localID;     // Preserve to test for deletion!
-    Clients.localID = Utils.makeGUID();
-    Clients.resetClient();
+    let oldLocalID  = engine.localID;     // Preserve to test for deletion!
+    engine.localID = Utils.makeGUID();
+    engine.resetClient();
     generateNewKeys();
     let serverKeys = CollectionKeys.asWBO("crypto", "keys");
     serverKeys.encrypt(Weave.Identity.syncKeyBundle);
     do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
 
     _("Sync.");
-    Clients._sync();
+    engine._sync();
 
     _("Old record " + oldLocalID + " was deleted, new one uploaded.");
     check_clients_count(1);
     check_client_deleted(oldLocalID);
 
     _("Now change our keys but don't upload them. " +
       "That means we get an HMAC error but redownload keys.");
     Service.lastHMACEvent = 0;
-    Clients.localID = Utils.makeGUID();
-    Clients.resetClient();
+    engine.localID = Utils.makeGUID();
+    engine.resetClient();
     generateNewKeys();
     deletedCollections = [];
     deletedItems       = [];
     check_clients_count(1);
-    Clients._sync();
+    engine._sync();
 
     _("Old record was not deleted, new one uploaded.");
     do_check_eq(deletedCollections.length, 0);
     do_check_eq(deletedItems.length, 0);
     check_clients_count(2);
 
     _("Now try the scenario where our keys are wrong *and* there's a bad record.");
     // Clean up and start fresh.
     user.collection("clients")._wbos = {};
     Service.lastHMACEvent = 0;
-    Clients.localID = Utils.makeGUID();
-    Clients.resetClient();
+    engine.localID = Utils.makeGUID();
+    engine.resetClient();
     deletedCollections = [];
     deletedItems       = [];
     check_clients_count(0);
 
     uploadNewKeys();
 
     // Sync once to upload a record.
-    Clients._sync();
+    engine._sync();
     check_clients_count(1);
 
     // Generate and upload new keys, so the old client record is wrong.
     uploadNewKeys();
 
     // Create a new client record and new keys. Now our keys are wrong, as well
     // as the object on the server. We'll download the new keys and also delete
     // the bad client record.
-    oldLocalID  = Clients.localID;         // Preserve to test for deletion!
-    Clients.localID = Utils.makeGUID();
-    Clients.resetClient();
+    oldLocalID  = engine.localID;         // Preserve to test for deletion!
+    engine.localID = Utils.makeGUID();
+    engine.resetClient();
     generateNewKeys();
     let oldKey = CollectionKeys.keyForCollection();
 
     do_check_eq(deletedCollections.length, 0);
     do_check_eq(deletedItems.length, 0);
-    Clients._sync();
+    engine._sync();
     do_check_eq(deletedItems.length, 1);
     check_client_deleted(oldLocalID);
     check_clients_count(1);
     let newKey = CollectionKeys.keyForCollection();
     do_check_false(oldKey.equals(newKey));
 
   } finally {
     Svc.Prefs.resetBranch("");
@@ -149,128 +151,128 @@ add_test(function test_bad_hmac() {
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_properties() {
   _("Test lastRecordUpload property");
   try {
     do_check_eq(Svc.Prefs.get("clients.lastRecordUpload"), undefined);
-    do_check_eq(Clients.lastRecordUpload, 0);
+    do_check_eq(engine.lastRecordUpload, 0);
 
     let now = Date.now();
-    Clients.lastRecordUpload = now / 1000;
-    do_check_eq(Clients.lastRecordUpload, Math.floor(now / 1000));
+    engine.lastRecordUpload = now / 1000;
+    do_check_eq(engine.lastRecordUpload, Math.floor(now / 1000));
   } finally {
     Svc.Prefs.resetBranch("");
     run_next_test();
   }
 });
 
 add_test(function test_sync() {
   _("Ensure that Clients engine uploads a new client record once a week.");
 
   new SyncTestingInfrastructure();
   generateNewKeys();
 
   let contents = {
-    meta: {global: {engines: {clients: {version: Clients.version,
-                                        syncID: Clients.syncID}}}},
+    meta: {global: {engines: {clients: {version: engine.version,
+                                        syncID: engine.syncID}}}},
     clients: {},
     crypto: {}
   };
   let server = serverForUsers({"foo": "password"}, contents);
   let user   = server.user("foo");
 
   function clientWBO() {
-    return user.collection("clients").wbo(Clients.localID);
+    return user.collection("clients").wbo(engine.localID);
   }
 
   try {
 
     _("First sync. Client record is uploaded.");
     do_check_eq(clientWBO(), undefined);
-    do_check_eq(Clients.lastRecordUpload, 0);
-    Clients._sync();
+    do_check_eq(engine.lastRecordUpload, 0);
+    engine._sync();
     do_check_true(!!clientWBO().payload);
-    do_check_true(Clients.lastRecordUpload > 0);
+    do_check_true(engine.lastRecordUpload > 0);
 
     _("Let's time travel more than a week back, new record should've been uploaded.");
-    Clients.lastRecordUpload -= MORE_THAN_CLIENTS_TTL_REFRESH;
-    let lastweek = Clients.lastRecordUpload;
+    engine.lastRecordUpload -= MORE_THAN_CLIENTS_TTL_REFRESH;
+    let lastweek = engine.lastRecordUpload;
     clientWBO().payload = undefined;
-    Clients._sync();
+    engine._sync();
     do_check_true(!!clientWBO().payload);
-    do_check_true(Clients.lastRecordUpload > lastweek);
+    do_check_true(engine.lastRecordUpload > lastweek);
 
     _("Remove client record.");
-    Clients.removeClientData();
+    engine.removeClientData();
     do_check_eq(clientWBO().payload, undefined);
 
     _("Time travel one day back, no record uploaded.");
-    Clients.lastRecordUpload -= LESS_THAN_CLIENTS_TTL_REFRESH;
-    let yesterday = Clients.lastRecordUpload;
-    Clients._sync();
+    engine.lastRecordUpload -= LESS_THAN_CLIENTS_TTL_REFRESH;
+    let yesterday = engine.lastRecordUpload;
+    engine._sync();
     do_check_eq(clientWBO().payload, undefined);
-    do_check_eq(Clients.lastRecordUpload, yesterday);
+    do_check_eq(engine.lastRecordUpload, yesterday);
 
   } finally {
     Svc.Prefs.resetBranch("");
     Records.clearCache();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_client_name_change() {
   _("Ensure client name change incurs a client record update.");
 
-  let tracker = Clients._tracker;
+  let tracker = engine._tracker;
 
-  let localID = Clients.localID;
-  let initialName = Clients.localName;
+  let localID = engine.localID;
+  let initialName = engine.localName;
 
   Svc.Obs.notify("weave:engine:start-tracking");
   _("initial name: " + initialName);
 
   // Tracker already has data, so clear it.
   tracker.clearChangedIDs();
 
   let initialScore = tracker.score;
 
   do_check_eq(Object.keys(tracker.changedIDs).length, 0);
 
   Svc.Prefs.set("client.name", "new name");
 
-  _("new name: " + Clients.localName);
-  do_check_neq(initialName, Clients.localName);
+  _("new name: " + engine.localName);
+  do_check_neq(initialName, engine.localName);
   do_check_eq(Object.keys(tracker.changedIDs).length, 1);
-  do_check_true(Clients.localID in tracker.changedIDs);
+  do_check_true(engine.localID in tracker.changedIDs);
   do_check_true(tracker.score > initialScore);
   do_check_true(tracker.score >= SCORE_INCREMENT_XLARGE);
 
   Svc.Obs.notify("weave:engine:stop-tracking");
 
   run_next_test();
 });
 
 add_test(function test_send_command() {
   _("Verifies _sendCommandToClient puts commands in the outbound queue.");
 
-  let store = Clients._store;
-  let tracker = Clients._tracker;
+  let store = engine._store;
+  let tracker = engine._tracker;
   let remoteId = Utils.makeGUID();
   let rec = new ClientsRec("clients", remoteId);
 
   store.create(rec);
   let remoteRecord = store.createRecord(remoteId, "clients");
 
   let action = "testCommand";
   let args = ["foo", "bar"];
 
-  Clients._sendCommandToClient(action, args, remoteId);
+  engine._sendCommandToClient(action, args, remoteId);
 
   let newRecord = store._remoteClients[remoteId];
   do_check_neq(newRecord, undefined);
   do_check_eq(newRecord.commands.length, 1);
 
   let command = newRecord.commands[0];
   do_check_eq(command.command, action);
   do_check_eq(command.args.length, 2);
@@ -279,17 +281,17 @@ add_test(function test_send_command() {
   do_check_neq(tracker.changedIDs[remoteId], undefined);
 
   run_next_test();
 });
 
 add_test(function test_command_validation() {
   _("Verifies that command validation works properly.");
 
-  let store = Clients._store;
+  let store = engine._store;
 
   let testCommands = [
     ["resetAll",    [],       true ],
     ["resetAll",    ["foo"],  false],
     ["resetEngine", ["tabs"], true ],
     ["resetEngine", [],       false],
     ["wipeAll",     [],       true ],
     ["wipeAll",     ["foo"],  false],
@@ -302,212 +304,212 @@ add_test(function test_command_validatio
 
   for each (let [action, args, expectedResult] in testCommands) {
     let remoteId = Utils.makeGUID();
     let rec = new ClientsRec("clients", remoteId);
 
     store.create(rec);
     store.createRecord(remoteId, "clients");
 
-    Clients.sendCommand(action, args, remoteId);
+    engine.sendCommand(action, args, remoteId);
 
     let newRecord = store._remoteClients[remoteId];
     do_check_neq(newRecord, undefined);
 
     if (expectedResult) {
       _("Ensuring command is sent: " + action);
       do_check_eq(newRecord.commands.length, 1);
 
       let command = newRecord.commands[0];
       do_check_eq(command.command, action);
       do_check_eq(command.args, args);
 
-      do_check_neq(Clients._tracker, undefined);
-      do_check_neq(Clients._tracker.changedIDs[remoteId], undefined);
+      do_check_neq(engine._tracker, undefined);
+      do_check_neq(engine._tracker.changedIDs[remoteId], undefined);
     } else {
       _("Ensuring command is scrubbed: " + action);
       do_check_eq(newRecord.commands, undefined);
 
       if (store._tracker) {
-        do_check_eq(Clients._tracker[remoteId], undefined);
+        do_check_eq(engine._tracker[remoteId], undefined);
       }
     }
 
   }
   run_next_test();
 });
 
 add_test(function test_command_duplication() {
   _("Ensures duplicate commands are detected and not added");
 
-  let store = Clients._store;
+  let store = engine._store;
   let remoteId = Utils.makeGUID();
   let rec = new ClientsRec("clients", remoteId);
   store.create(rec);
   store.createRecord(remoteId, "clients");
 
   let action = "resetAll";
   let args = [];
 
-  Clients.sendCommand(action, args, remoteId);
-  Clients.sendCommand(action, args, remoteId);
+  engine.sendCommand(action, args, remoteId);
+  engine.sendCommand(action, args, remoteId);
 
   let newRecord = store._remoteClients[remoteId];
   do_check_eq(newRecord.commands.length, 1);
 
   _("Check variant args length");
   newRecord.commands = [];
 
   action = "resetEngine";
-  Clients.sendCommand(action, [{ x: "foo" }], remoteId);
-  Clients.sendCommand(action, [{ x: "bar" }], remoteId);
+  engine.sendCommand(action, [{ x: "foo" }], remoteId);
+  engine.sendCommand(action, [{ x: "bar" }], remoteId);
 
   _("Make sure we spot a real dupe argument.");
-  Clients.sendCommand(action, [{ x: "bar" }], remoteId);
+  engine.sendCommand(action, [{ x: "bar" }], remoteId);
 
   do_check_eq(newRecord.commands.length, 2);
 
   run_next_test();
 });
 
 add_test(function test_command_invalid_client() {
   _("Ensures invalid client IDs are caught");
 
   let id = Utils.makeGUID();
   let error;
 
   try {
-    Clients.sendCommand("wipeAll", [], id);
+    engine.sendCommand("wipeAll", [], id);
   } catch (ex) {
     error = ex;
   }
 
   do_check_eq(error.message.indexOf("Unknown remote client ID: "), 0);
 
   run_next_test();
 });
 
 add_test(function test_process_incoming_commands() {
   _("Ensures local commands are executed");
 
-  Clients.localCommands = [{ command: "logout", args: [] }];
+  engine.localCommands = [{ command: "logout", args: [] }];
 
   let ev = "weave:service:logout:finish";
 
   var handler = function() {
     Svc.Obs.remove(ev, handler);
     run_next_test();
   };
 
   Svc.Obs.add(ev, handler);
 
   // logout command causes processIncomingCommands to return explicit false.
-  do_check_false(Clients.processIncomingCommands());
+  do_check_false(engine.processIncomingCommands());
 });
 
 add_test(function test_command_sync() {
   _("Ensure that commands are synced across clients.");
 
   new SyncTestingInfrastructure();
 
-  Clients._store.wipe();
+  engine._store.wipe();
   generateNewKeys();
 
   let contents = {
-    meta: {global: {engines: {clients: {version: Clients.version,
-                                        syncID: Clients.syncID}}}},
+    meta: {global: {engines: {clients: {version: engine.version,
+                                        syncID: engine.syncID}}}},
     clients: {},
     crypto: {}
   };
   let server   = serverForUsers({"foo": "password"}, contents);
   let user     = server.user("foo");
   let remoteId = Utils.makeGUID();
 
   function clientWBO(id) {
     return user.collection("clients").wbo(id);
   }
 
   _("Create remote client record");
   let rec = new ClientsRec("clients", remoteId);
-  Clients._store.create(rec);
-  let remoteRecord = Clients._store.createRecord(remoteId, "clients");
-  Clients.sendCommand("wipeAll", []);
+  engine._store.create(rec);
+  let remoteRecord = engine._store.createRecord(remoteId, "clients");
+  engine.sendCommand("wipeAll", []);
 
-  let clientRecord = Clients._store._remoteClients[remoteId];
+  let clientRecord = engine._store._remoteClients[remoteId];
   do_check_neq(clientRecord, undefined);
   do_check_eq(clientRecord.commands.length, 1);
 
   try {
     _("Syncing.");
-    Clients._sync();
+    engine._sync();
     _("Checking record was uploaded.");
-    do_check_neq(clientWBO(Clients.localID).payload, undefined);
-    do_check_true(Clients.lastRecordUpload > 0);
+    do_check_neq(clientWBO(engine.localID).payload, undefined);
+    do_check_true(engine.lastRecordUpload > 0);
 
     do_check_neq(clientWBO(remoteId).payload, undefined);
 
     Svc.Prefs.set("client.GUID", remoteId);
-    Clients._resetClient();
-    do_check_eq(Clients.localID, remoteId);
+    engine._resetClient();
+    do_check_eq(engine.localID, remoteId);
     _("Performing sync on resetted client.");
-    Clients._sync();
-    do_check_neq(Clients.localCommands, undefined);
-    do_check_eq(Clients.localCommands.length, 1);
+    engine._sync();
+    do_check_neq(engine.localCommands, undefined);
+    do_check_eq(engine.localCommands.length, 1);
 
-    let command = Clients.localCommands[0];
+    let command = engine.localCommands[0];
     do_check_eq(command.command, "wipeAll");
     do_check_eq(command.args.length, 0);
 
   } finally {
     Svc.Prefs.resetBranch("");
     Records.clearCache();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_send_uri_to_client_for_display() {
   _("Ensure sendURIToClientForDisplay() sends command properly.");
 
-  let tracker = Clients._tracker;
-  let store = Clients._store;
+  let tracker = engine._tracker;
+  let store = engine._store;
 
   let remoteId = Utils.makeGUID();
   let rec = new ClientsRec("clients", remoteId);
   rec.name = "remote";
   store.create(rec);
   let remoteRecord = store.createRecord(remoteId, "clients");
 
   tracker.clearChangedIDs();
   let initialScore = tracker.score;
 
   let uri = "http://www.mozilla.org/";
   let title = "Title of the Page";
-  Clients.sendURIToClientForDisplay(uri, remoteId, title);
+  engine.sendURIToClientForDisplay(uri, remoteId, title);
 
   let newRecord = store._remoteClients[remoteId];
 
   do_check_neq(newRecord, undefined);
   do_check_eq(newRecord.commands.length, 1);
 
   let command = newRecord.commands[0];
   do_check_eq(command.command, "displayURI");
   do_check_eq(command.args.length, 3);
   do_check_eq(command.args[0], uri);
-  do_check_eq(command.args[1], Clients.localID);
+  do_check_eq(command.args[1], engine.localID);
   do_check_eq(command.args[2], title);
 
   do_check_true(tracker.score > initialScore);
   do_check_true(tracker.score - initialScore >= SCORE_INCREMENT_XLARGE);
 
   _("Ensure unknown client IDs result in exception.");
   let unknownId = Utils.makeGUID();
   let error;
 
   try {
-    Clients.sendURIToClientForDisplay(uri, unknownId);
+    engine.sendURIToClientForDisplay(uri, unknownId);
   } catch (ex) {
     error = ex;
   }
 
   do_check_eq(error.message.indexOf("Unknown remote client ID: "), 0);
 
   run_next_test();
 });
@@ -522,17 +524,17 @@ add_test(function test_receive_display_u
   let remoteId = Utils.makeGUID();
   let title = "Page Title!";
 
   let command = {
     command: "displayURI",
     args: [uri, remoteId, title],
   };
 
-  Clients.localCommands = [command];
+  engine.localCommands = [command];
 
   // Received 'displayURI' command should result in the topic defined below
   // being called.
   let ev = "weave:engine:clients:display-uri";
 
   let handler = function(subject, data) {
     Svc.Obs.remove(ev, handler);
 
@@ -541,16 +543,16 @@ add_test(function test_receive_display_u
     do_check_eq(subject.title, title);
     do_check_eq(data, null);
 
     run_next_test();
   };
 
   Svc.Obs.add(ev, handler);
 
-  do_check_true(Clients.processIncomingCommands());
+  do_check_true(engine.processIncomingCommands());
 });
 
 function run_test() {
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.Engine.Clients").level = Log4Moz.Level.Trace;
   run_next_test();
 }
--- a/services/sync/tests/unit/test_clients_escape.js
+++ b/services/sync/tests/unit/test_clients_escape.js
@@ -1,43 +1,45 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function run_test() {
   _("Set up test fixtures.");
 
   Identity.username = "john@example.com";
   Svc.Prefs.set("clusterURL", "http://fakebase/");
   let baseUri = "http://fakebase/1.1/foo/storage/";
   let pubUri = baseUri + "keys/pubkey";
   let privUri = baseUri + "keys/privkey";
 
   Identity.syncKey = "abcdeabcdeabcdeabcdeabcdea";
   let keyBundle = Identity.syncKeyBundle;
 
+  let engine = Service.clientsEngine;
+
   try {
     _("Test that serializing client records results in uploadable ascii");
-    Clients.localID = "ascii";
-    Clients.localName = "wéävê";
+    engine.localID = "ascii";
+    engine.localName = "wéävê";
 
     _("Make sure we have the expected record");
-    let record = Clients._createRecord("ascii");
+    let record = engine._createRecord("ascii");
     do_check_eq(record.id, "ascii");
     do_check_eq(record.name, "wéävê");
 
     _("Encrypting record...");
     record.encrypt(keyBundle);
     _("Encrypted.");
-    
+
     let serialized = JSON.stringify(record);
     let checkCount = 0;
     _("Checking for all ASCII:", serialized);
     Array.forEach(serialized, function(ch) {
       let code = ch.charCodeAt(0);
       _("Checking asciiness of '", ch, "'=", code);
       do_check_true(code < 128);
       checkCount++;
@@ -47,15 +49,15 @@ function run_test() {
     do_check_eq(checkCount, serialized.length);
 
     _("Making sure the record still looks like it did before");
     record.decrypt(keyBundle);
     do_check_eq(record.id, "ascii");
     do_check_eq(record.name, "wéävê");
 
     _("Sanity check that creating the record also gives the same");
-    record = Clients._createRecord("ascii");
+    record = engine._createRecord("ascii");
     do_check_eq(record.id, "ascii");
     do_check_eq(record.name, "wéävê");
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
--- a/services/sync/tests/unit/test_corrupt_keys.js
+++ b/services/sync/tests/unit/test_corrupt_keys.js
@@ -1,31 +1,33 @@
-Cu.import("resource://services-sync/main.js");
-Cu.import("resource://services-sync/service.js");
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-common/log4moz.js");
+Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/engines/tabs.js");
 Cu.import("resource://services-sync/engines/history.js");
-Cu.import("resource://services-common/log4moz.js");
-  
+Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/status.js");
+Cu.import("resource://services-sync/util.js");
+
 add_test(function test_locally_changed_keys() {
   let passphrase = "abcdeabcdeabcdeabcdeabcdea";
 
   let hmacErrorCount = 0;
   function counting(f) {
     return function() {
       hmacErrorCount++;
       return f.call(this);
     };
   }
   
-  Weave.Service.handleHMACEvent = counting(Weave.Service.handleHMACEvent);
+  Service.handleHMACEvent = counting(Service.handleHMACEvent);
   
   let server  = new SyncServer();
   let johndoe = server.registerUser("johndoe", "password");
   johndoe.createContents({
     meta: {},
     crypto: {},
     clients: {}
   });
@@ -50,46 +52,45 @@ add_test(function test_locally_changed_k
     Svc.Session = {
       getBrowserState: function () JSON.stringify(myTabs)
     };
 
     setBasicCredentials("johndoe", "password", passphrase);
     Service.serverURL = TEST_SERVER_URL;
     Service.clusterURL = TEST_CLUSTER_URL;
 
-    Engines.register(HistoryEngine);
-    Weave.Service._registerEngines();
-    
+    Service.engineManager.register(HistoryEngine);
+
     function corrupt_local_keys() {
       CollectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
                                          Svc.Crypto.generateRandomKey()];
     }
     
     _("Setting meta.");
     
     // Bump version on the server.
     let m = new WBORecord("meta", "global");
     m.payload = {"syncID": "foooooooooooooooooooooooooo",
                  "storageVersion": STORAGE_VERSION};
-    m.upload(Weave.Service.metaURL);
+    m.upload(Service.metaURL);
     
     _("New meta/global: " + JSON.stringify(johndoe.collection("meta").wbo("global")));
     
     // Upload keys.
     generateNewKeys();
     let serverKeys = CollectionKeys.asWBO("crypto", "keys");
-    serverKeys.encrypt(Weave.Identity.syncKeyBundle);
-    do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
+    serverKeys.encrypt(Identity.syncKeyBundle);
+    do_check_true(serverKeys.upload(Service.cryptoKeysURL).success);
     
     // Check that login works.
-    do_check_true(Weave.Service.login("johndoe", "ilovejane", passphrase));
-    do_check_true(Weave.Service.isLoggedIn);
+    do_check_true(Service.login("johndoe", "ilovejane", passphrase));
+    do_check_true(Service.isLoggedIn);
     
     // Sync should upload records.
-    Weave.Service.sync();
+    Service.sync();
     
     // Tabs exist.
     _("Tabs modified: " + johndoe.modified("tabs"));
     do_check_true(johndoe.modified("tabs") > 0);
     
     let coll_modified = CollectionKeys.lastModified;
     
     // Let's create some server side history records.
@@ -118,36 +119,36 @@ add_test(function test_locally_changed_k
     }
     
     history.timestamp = Date.now() / 1000;
     let old_key_time = johndoe.modified("crypto");
     _("Old key time: " + old_key_time);
     
     // Check that we can decrypt one.
     let rec = new CryptoWrapper("history", "record-no--0");
-    rec.fetch(Weave.Service.storageURL + "history/record-no--0");
+    rec.fetch(Service.storageURL + "history/record-no--0");
     _(JSON.stringify(rec));
     do_check_true(!!rec.decrypt());
     
     do_check_eq(hmacErrorCount, 0);
     
     // Fill local key cache with bad data.
     corrupt_local_keys();
     _("Keys now: " + CollectionKeys.keyForCollection("history").keyPair);
     
     do_check_eq(hmacErrorCount, 0);
     
     _("HMAC error count: " + hmacErrorCount);
     // Now syncing should succeed, after one HMAC error.
-    Weave.Service.sync();
+    Service.sync();
     do_check_eq(hmacErrorCount, 1);
     _("Keys now: " + CollectionKeys.keyForCollection("history").keyPair);
     
     // And look! We downloaded history!
-    let store = Engines.get("history")._store;
+    let store = Service.engineManager.get("history")._store;
     do_check_true(store.urlExists("http://foo/bar?record-no--0"));
     do_check_true(store.urlExists("http://foo/bar?record-no--1"));
     do_check_true(store.urlExists("http://foo/bar?record-no--2"));
     do_check_true(store.urlExists("http://foo/bar?record-no--3"));
     do_check_true(store.urlExists("http://foo/bar?record-no--4"));
     do_check_eq(hmacErrorCount, 1);
     
     _("Busting some new server values.");
@@ -173,32 +174,32 @@ add_test(function test_locally_changed_k
       history.insert(id, payload, modified);
     }
     history.timestamp = Date.now() / 1000;
     
     _("Server key time hasn't changed.");
     do_check_eq(johndoe.modified("crypto"), old_key_time);
     
     _("Resetting HMAC error timer.");
-    Weave.Service.lastHMACEvent = 0;
+    Service.lastHMACEvent = 0;
     
     _("Syncing...");
-    Weave.Service.sync();
+    Service.sync();
     _("Keys now: " + CollectionKeys.keyForCollection("history").keyPair);
     _("Server keys have been updated, and we skipped over 5 more HMAC errors without adjusting history.");
     do_check_true(johndoe.modified("crypto") > old_key_time);
     do_check_eq(hmacErrorCount, 6);
     do_check_false(store.urlExists("http://foo/bar?record-no--5"));
     do_check_false(store.urlExists("http://foo/bar?record-no--6"));
     do_check_false(store.urlExists("http://foo/bar?record-no--7"));
     do_check_false(store.urlExists("http://foo/bar?record-no--8"));
     do_check_false(store.urlExists("http://foo/bar?record-no--9"));
     
   } finally {
-    Weave.Svc.Prefs.resetBranch("");
+    Svc.Prefs.resetBranch("");
     server.stop(run_next_test);
   }
 });
 
 function run_test() {
   let logger = Log4Moz.repository.rootLogger;
   Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
   
--- a/services/sync/tests/unit/test_engine.js
+++ b/services/sync/tests/unit/test_engine.js
@@ -1,34 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-common/observers.js");
 Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-common/observers.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 
-function SteamStore() {
-  Store.call(this, "Steam");
+function SteamStore(engine) {
+  Store.call(this, "Steam", engine);
   this.wasWiped = false;
 }
 SteamStore.prototype = {
   __proto__: Store.prototype,
 
   wipe: function() {
     this.wasWiped = true;
   }
 };
 
-function SteamTracker() {
-  Tracker.call(this, "Steam");
+function SteamTracker(engine) {
+  Tracker.call(this, "Steam", engine);
 }
 SteamTracker.prototype = {
   __proto__: Tracker.prototype
 };
 
 function SteamEngine() {
-  Engine.call(this, "Steam");
+  Engine.call(this, "Steam", Service);
   this.wasReset = false;
   this.wasSynced = false;
 }
 SteamEngine.prototype = {
   __proto__: Engine.prototype,
   _storeObj: SteamStore,
   _trackerObj: SteamTracker,
 
@@ -58,57 +62,57 @@ Observers.add("weave:engine:reset-client
 Observers.add("weave:engine:wipe-client:start", engineObserver);
 Observers.add("weave:engine:wipe-client:finish", engineObserver);
 Observers.add("weave:engine:sync:start", engineObserver);
 Observers.add("weave:engine:sync:finish", engineObserver);
 
 
 function test_members() {
   _("Engine object members");
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   do_check_eq(engine.Name, "Steam");
   do_check_eq(engine.prefName, "steam");
   do_check_true(engine._store instanceof SteamStore);
   do_check_true(engine._tracker instanceof SteamTracker);
 }
 
 function test_score() {
   _("Engine.score corresponds to tracker.score and is readonly");
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   do_check_eq(engine.score, 0);
   engine._tracker.score += 5;
   do_check_eq(engine.score, 5);
 
   try {
     engine.score = 10;
   } catch(ex) {
     // Setting an attribute that has a getter produces an error in
     // Firefox <= 3.6 and is ignored in later versions.  Either way,
     // the attribute's value won't change.
   }
   do_check_eq(engine.score, 5);
 }
 
 function test_resetClient() {
   _("Engine.resetClient calls _resetClient");
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   do_check_false(engine.wasReset);
 
   engine.resetClient();
   do_check_true(engine.wasReset);
   do_check_eq(engineObserver.topics[0], "weave:engine:reset-client:start");
   do_check_eq(engineObserver.topics[1], "weave:engine:reset-client:finish");
 
   engine.wasReset = false;
   engineObserver.reset();
 }
 
 function test_wipeClient() {
   _("Engine.wipeClient calls resetClient, wipes store, clears changed IDs");
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   do_check_false(engine.wasReset);
   do_check_false(engine._store.wasWiped);
   do_check_true(engine._tracker.addChangedID("a-changed-id"));
   do_check_true("a-changed-id" in engine._tracker.changedIDs);
 
   engine.wipeClient();
   do_check_true(engine.wasReset);
   do_check_true(engine._store.wasWiped);
@@ -120,31 +124,31 @@ function test_wipeClient() {
 
   engine.wasReset = false;
   engine._store.wasWiped = false;
   engineObserver.reset();
 }
 
 function test_enabled() {
   _("Engine.enabled corresponds to preference");
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   try {
     do_check_false(engine.enabled);
     Svc.Prefs.set("engine.steam", true);
     do_check_true(engine.enabled);
 
     engine.enabled = false;
     do_check_false(Svc.Prefs.get("engine.steam"));
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_sync() {
-  let engine = new SteamEngine();
+  let engine = new SteamEngine(Service);
   try {
     _("Engine.sync doesn't call _sync if it's not enabled");
     do_check_false(engine.enabled);
     do_check_false(engine.wasSynced);
     engine.sync();
     do_check_false(engine.wasSynced);
 
     _("Engine.sync calls _sync if it's enabled");
--- a/services/sync/tests/unit/test_engine_abort.js
+++ b/services/sync/tests/unit/test_engine_abort.js
@@ -1,17 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 add_test(function test_processIncoming_abort() {
   _("An abort exception, raised in applyIncoming, will abort _processIncoming.");
   new SyncTestingInfrastructure();
   generateNewKeys();
 
-  let engine = new RotaryEngine();
+  let engine = new RotaryEngine(Service);
 
   _("Create some server data.");
   let meta_global = Records.set(engine.metaURL, new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                           syncID: engine.syncID}};
 
   let collection = new ServerCollection();
   let id = Utils.makeGUID();
--- a/services/sync/tests/unit/test_enginemanager.js
+++ b/services/sync/tests/unit/test_enginemanager.js
@@ -1,80 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 
 function run_test() {
   _("We start out with a clean slate");
-  let engines = Engines.getAll();
+
+  let manager = new EngineManager(Service);
+
+  let engines = manager.getAll();
   do_check_eq(engines.length, 0);
-  do_check_eq(Engines.get('dummy'), undefined);
+  do_check_eq(manager.get('dummy'), undefined);
 
   _("Register an engine");
   function DummyEngine() {}
   DummyEngine.prototype.name = "dummy";
-  Engines.register(DummyEngine);
-  let dummy = Engines.get('dummy');
+  manager.register(DummyEngine);
+  let dummy = manager.get('dummy');
   do_check_true(dummy instanceof DummyEngine);
 
-  engines = Engines.getAll();
+  engines = manager.getAll();
   do_check_eq(engines.length, 1);
   do_check_eq(engines[0], dummy);
 
   _("Register an already registered engine is ignored");
-  Engines.register(DummyEngine);
-  do_check_eq(Engines.get('dummy'), dummy);
+  manager.register(DummyEngine);
+  do_check_eq(manager.get('dummy'), dummy);
 
   _("Register multiple engines in one go");
   function PetrolEngine() {}
   PetrolEngine.prototype.name = "petrol";
   function DieselEngine() {}
   DieselEngine.prototype.name = "diesel";
 
-  Engines.register([PetrolEngine, DieselEngine]);
-  let petrol = Engines.get('petrol');
-  let diesel = Engines.get('diesel');
+  manager.register([PetrolEngine, DieselEngine]);
+  let petrol = manager.get('petrol');
+  let diesel = manager.get('diesel');
   do_check_true(petrol instanceof PetrolEngine);
   do_check_true(diesel instanceof DieselEngine);
 
-  engines = Engines.getAll();
+  engines = manager.getAll();
   do_check_eq(engines.length, 3);
   do_check_neq(engines.indexOf(petrol), -1);
   do_check_neq(engines.indexOf(diesel), -1);
 
   _("Retrieve multiple engines in one go");
-  engines = Engines.get(["dummy", "diesel"]);
+  engines = manager.get(["dummy", "diesel"]);
   do_check_eq(engines.length, 2);
   do_check_neq(engines.indexOf(dummy), -1);
   do_check_neq(engines.indexOf(diesel), -1);
 
   _("getEnabled() only returns enabled engines");
-  engines = Engines.getEnabled();
+  engines = manager.getEnabled();
   do_check_eq(engines.length, 0);
 
   petrol.enabled = true;
-  engines = Engines.getEnabled();
+  engines = manager.getEnabled();
   do_check_eq(engines.length, 1);
   do_check_eq(engines[0], petrol);
 
   dummy.enabled = true;
   diesel.enabled = true;
-  engines = Engines.getEnabled();
+  engines = manager.getEnabled();
   do_check_eq(engines.length, 3);
 
   _("Unregister an engine by name");
-  Engines.unregister('dummy');
-  do_check_eq(Engines.get('dummy'), undefined);
-  engines = Engines.getAll();
+  manager.unregister('dummy');
+  do_check_eq(manager.get('dummy'), undefined);
+  engines = manager.getAll();
   do_check_eq(engines.length, 2);
   do_check_eq(engines.indexOf(dummy), -1);
 
   _("Unregister an engine by value");
-  // Engines.unregister() checks for instanceof Engine, so let's make one:
+  // manager.unregister() checks for instanceof Engine, so let's make one:
   function ActualEngine() {}
   ActualEngine.prototype = {__proto__: Engine.prototype,
                             name: 'actual'};
-  Engines.register(ActualEngine);
-  let actual = Engines.get('actual');
+  manager.register(ActualEngine);
+  let actual = manager.get('actual');
   do_check_true(actual instanceof ActualEngine);
   do_check_true(actual instanceof Engine);
 
-  Engines.unregister(actual);
-  do_check_eq(Engines.get('actual'), undefined);
+  manager.unregister(actual);
+  do_check_eq(manager.get('actual'), undefined);
 }
--- a/services/sync/tests/unit/test_errorhandler.js
+++ b/services/sync/tests/unit/test_errorhandler.js
@@ -1,49 +1,50 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/policies.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 
-Svc.DefaultPrefs.set("registerEngines", "");
-Cu.import("resource://services-sync/service.js");
-
 const TEST_MAINTENANCE_URL = "http://localhost:8080/maintenance/";
 const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
 const LOG_PREFIX_SUCCESS = "success-";
 const LOG_PREFIX_ERROR   = "error-";
 
 const PROLONGED_ERROR_DURATION =
   (Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
 
 const NON_PROLONGED_ERROR_DURATION =
   (Svc.Prefs.get('errorhandler.networkFailureReportTimeout') / 2) * 1000;
 
+Service.engineManager.clear();
+
 function setLastSync(lastSyncValue) {
   Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
 }
 
 function CatapultEngine() {
-  SyncEngine.call(this, "Catapult");
+  SyncEngine.call(this, "Catapult", Service);
 }
 CatapultEngine.prototype = {
   __proto__: SyncEngine.prototype,
   exception: null, // tests fill this in
   _sync: function _sync() {
     if (this.exception) {
       throw this.exception;
     }
   }
 };
 
-Engines.register(CatapultEngine);
+let engineManager = Service.engineManager;
+engineManager.register(CatapultEngine);
 
 // This relies on Service/ErrorHandler being a singleton. Fixing this will take
 // a lot of work.
 let errorHandler = Service.errorHandler;
 
 function run_test() {
   initTestLogging("Trace");
 
@@ -69,20 +70,20 @@ function service_unavailable(request, re
   response.setHeader("Retry-After", "42");
   response.bodyOutputStream.write(body, body.length);
 }
 
 function sync_httpd_setup() {
   let global = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
-    engines: {clients: {version: Clients.version,
-                        syncID: Clients.syncID},
-              catapult: {version: Engines.get("catapult").version,
-                         syncID: Engines.get("catapult").syncID}}
+    engines: {clients: {version: Service.clientsEngine.version,
+                        syncID: Service.clientsEngine.syncID},
+              catapult: {version: engineManager.get("catapult").version,
+                         syncID: engineManager.get("catapult").syncID}}
   });
   let clientsColl = new ServerCollection({}, true);
 
   // Tracking info/collections.
   let collectionsHelper = track_collections_helper();
   let upd = collectionsHelper.with_updated_collection;
 
   let handler_401 = httpd_handler(401, "Unauthorized");
@@ -741,17 +742,17 @@ add_test(function test_sync_network_erro
 });
 
 add_test(function test_sync_server_maintenance_error() {
   // Test server maintenance errors are not reported.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   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);
@@ -898,17 +899,17 @@ add_test(function test_crypto_keys_login
 });
 
 add_test(function test_sync_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
@@ -1097,17 +1098,17 @@ add_test(function test_wipeRemote_prolon
   let server = sync_httpd_setup();
 
   server.registerPathHandler("/1.1/broken.wipe/storage/catapult", service_unavailable);
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
   Service.serverURL = TEST_MAINTENANCE_URL;
   Service.clusterURL = TEST_MAINTENANCE_URL;
   generateAndUploadKeys();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   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;
   });
@@ -1134,17 +1135,17 @@ add_test(function test_wipeRemote_prolon
 
 add_test(function test_sync_syncAndReportErrors_server_maintenance_error() {
   // Test server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
@@ -1336,17 +1337,17 @@ add_test(function test_wipeRemote_syncAn
   // wiping all remote devices.
   let server = sync_httpd_setup();
 
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
   Service.serverURL = TEST_MAINTENANCE_URL;
   Service.clusterURL = TEST_MAINTENANCE_URL;
   generateAndUploadKeys();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   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;
   });
@@ -1373,17 +1374,17 @@ add_test(function test_wipeRemote_syncAn
 
 add_test(function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are
   // reported when calling syncAndReportErrors.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
@@ -1568,17 +1569,17 @@ add_test(function test_wipeServer_login_
 
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_engine_generic_fail() {
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.sync = function sync() {
     Svc.Obs.notify("weave:engine:sync:error", "", "catapult");
   };
 
   let log = Log4Moz.repository.getLogger("Sync.ErrorHandler");
   Svc.Prefs.set("log.appender.file.logOnError", true);
 
@@ -1671,17 +1672,17 @@ add_test(function test_logs_on_login_err
   Svc.Obs.notify("weave:service:login:error", {});
 });
 
 // This test should be the last one since it monkeypatches the engine object
 // and we should only have one engine object throughout the file (bug 629664).
 add_test(function test_engine_applyFailed() {
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   delete engine.exception;
   engine.sync = function sync() {
     Svc.Obs.notify("weave:engine:sync:applied", {newFailed:1}, "catapult");
   };
 
   let log = Log4Moz.repository.getLogger("Sync.ErrorHandler");
   Svc.Prefs.set("log.appender.file.logOnError", true);
--- a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
+++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
@@ -1,37 +1,41 @@
-Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/policies.js");
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
 
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/policies.js");
+Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
-Svc.DefaultPrefs.set("registerEngines", "");
-Cu.import("resource://services-sync/service.js");
-Cu.import("resource://services-sync/record.js");
 
 initTestLogging();
 
+let engineManager = Service.engineManager;
+engineManager.clear();
+
 function CatapultEngine() {
-  SyncEngine.call(this, "Catapult");
+  SyncEngine.call(this, "Catapult", Service);
 }
 CatapultEngine.prototype = {
   __proto__: SyncEngine.prototype,
   exception: null, // tests fill this in
   _sync: function _sync() {
     throw this.exception;
   }
 };
 
 function sync_httpd_setup() {
   let collectionsHelper = track_collections_helper();
   let upd = collectionsHelper.with_updated_collection;
   let collections = collectionsHelper.collections;
 
-  let catapultEngine = Engines.get("catapult");
+  let catapultEngine = engineManager.get("catapult");
   let engines        = {catapult: {version: catapultEngine.version,
                                    syncID:  catapultEngine.syncID}};
 
   // Track these using the collections helper, which keeps modified times
   // up-to-date.
   let clientsColl = new ServerCollection({}, true);
   let keysWBO     = new ServerWBO("keys");
   let globalWBO   = new ServerWBO("global", {storageVersion: STORAGE_VERSION,
@@ -62,17 +66,17 @@ function generateAndUploadKeys() {
 }
 
 
 add_test(function test_backoff500() {
   _("Test: HTTP 500 sets backoff status.");
   setUp();
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 500};
 
   try {
     do_check_false(Status.enforceBackoff);
 
     // Forcibly create and upload keys here -- otherwise we don't get to the 500!
     do_check_true(generateAndUploadKeys());
@@ -90,17 +94,17 @@ add_test(function test_backoff500() {
 });
 
 add_test(function test_backoff503() {
   _("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status.");
   setUp();
   let server = sync_httpd_setup();
 
   const BACKOFF = 42;
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function (subject) {
     backoffInterval = subject;
   });
@@ -125,17 +129,17 @@ add_test(function test_backoff503() {
   server.stop(run_next_test);
 });
 
 add_test(function test_overQuota() {
   _("Test: HTTP 400 with body error code 14 means over quota.");
   setUp();
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 400,
                       toString: function() "14"};
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
     do_check_true(generateAndUploadKeys());
@@ -194,17 +198,17 @@ add_test(function test_service_offline()
   run_next_test();
 });
 
 add_test(function test_engine_networkError() {
   _("Test: Network related exceptions from engine.sync() lead to the right status code.");
   setUp();
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST",
                                           Cr.NS_ERROR_UNKNOWN_HOST);
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
     do_check_true(generateAndUploadKeys());
@@ -220,17 +224,17 @@ add_test(function test_engine_networkErr
   }
   server.stop(run_next_test);
 });
 
 add_test(function test_resource_timeout() {
   setUp();
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = engineManager.get("catapult");
   engine.enabled = true;
   // Resource throws this when it encounters a timeout.
   engine.exception = Components.Exception("Aborting due to channel inactivity.",
                                           Cr.NS_ERROR_NET_TIMEOUT);
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
@@ -244,11 +248,11 @@ add_test(function test_resource_timeout(
   } finally {
     Status.resetSync();
     Service.startOver();
   }
   server.stop(run_next_test);
 });
 
 function run_test() {
-  Engines.register(CatapultEngine);
+  engineManager.register(CatapultEngine);
   run_next_test();
 }
--- a/services/sync/tests/unit/test_forms_store.js
+++ b/services/sync/tests/unit/test_forms_store.js
@@ -1,15 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 _("Make sure the form store follows the Store api and correctly accesses the backend form storage");
 Cu.import("resource://services-sync/engines/forms.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function run_test() {
   let baseuri = "http://fake/uri/";
-  let store = new FormEngine()._store;
+  let store = new FormEngine(Service)._store;
 
   function applyEnsureNoFailures(records) {
     do_check_eq(store.applyIncomingBatch(records).length, 0);
   }
 
   _("Remove any existing entries");
   store.wipe();
   for (let id in store.getAllIDs()) {
--- a/services/sync/tests/unit/test_forms_tracker.js
+++ b/services/sync/tests/unit/test_forms_tracker.js
@@ -1,15 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/engines/forms.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-common/log4moz.js");
 
 function run_test() {
   _("Verify we've got an empty tracker to work with.");
-  let tracker = new FormEngine()._tracker;
+  let tracker = new FormEngine(Service)._tracker;
   do_check_empty(tracker.changedIDs);
   Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
 
   try {
     _("Create an entry. Won't show because we haven't started tracking yet");
     Svc.Form.addEntry("name", "John Doe");
     do_check_empty(tracker.changedIDs);
 
@@ -29,21 +33,21 @@ function run_test() {
     Svc.Obs.notify("weave:engine:stop-tracking");
     Svc.Form.removeEntry("address", "Memory Lane");
     do_check_empty(tracker.changedIDs);
 
     _("Notifying twice won't do any harm.");
     Svc.Obs.notify("weave:engine:stop-tracking");
     Svc.Form.removeEntry("email", "john@doe.com");
     do_check_empty(tracker.changedIDs);
-  
+
     _("Test error detection.");
     // This throws an exception without the fix for Bug 597400.
     tracker.trackEntry("foo", "bar");
-    
+
   } finally {
     _("Clean up.");
     Svc.Form.removeAllEntries();
     if (tracker._lazySave) {
       tracker._lazySave.clear();
     }
   }
 }
--- a/services/sync/tests/unit/test_history_engine.js
+++ b/services/sync/tests/unit/test_history_engine.js
@@ -1,28 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines/history.js");
-Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/identity.js");
+Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 add_test(function test_processIncoming_mobile_history_batched() {
   _("SyncEngine._processIncoming works on history engine.");
 
   let FAKE_DOWNLOAD_LIMIT = 100;
 
   new SyncTestingInfrastructure();
 
   Svc.Prefs.set("client.type", "mobile");
   PlacesUtils.history.removeAllPages();
-  Engines.register(HistoryEngine);
+  Service.engineManager.register(HistoryEngine);
 
   // A collection that logs each GET
   let collection = new ServerCollection();
   collection.get_log = [];
   collection._get = collection.get;
   collection.get = function (options) {
     this.get_log.push(options);
     return this._get(options);
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -1,11 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/engines/history.js");
-Cu.import("resource://services-common/async.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 const TIMESTAMP1 = (Date.now() - 103406528) * 1000;
 const TIMESTAMP2 = (Date.now() - 6592903) * 1000;
 const TIMESTAMP3 = (Date.now() - 123894) * 1000;
 
 function queryPlaces(uri, options) {
   let query = PlacesUtils.history.getNewQuery();
@@ -59,17 +63,17 @@ function ensureThrows(func) {
       func.apply(this, arguments);
     } catch (ex) {
       PlacesUtils.history.removeAllPages();
       do_throw(ex);
     }
   };
 }
 
-let store = new HistoryEngine()._store;
+let store = new HistoryEngine(Service)._store;
 function applyEnsureNoFailures(records) {
   do_check_eq(store.applyIncomingBatch(records).length, 0);
 }
 
 let fxuri, fxguid, tburi, tbguid;
 
 function run_test() {
   initTestLogging("Trace");
--- a/services/sync/tests/unit/test_history_tracker.js
+++ b/services/sync/tests/unit/test_history_tracker.js
@@ -1,27 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines/history.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function onScoreUpdated(callback) {
   Svc.Obs.add("weave:engine:score:updated", function observer() {
     Svc.Obs.remove("weave:engine:score:updated", observer);
     try {
       callback();
     } catch (ex) {
       do_throw(ex);
     }
   });
 }
 
-Engines.register(HistoryEngine);
-let engine = Engines.get("history");
+Service.engineManager.clear();
+Service.engineManager.register(HistoryEngine);
+let engine = Service.engineManager.get("history");
 let tracker = engine._tracker;
 
 let _counter = 0;
 function addVisit() {
   let uri = Utils.makeURI("http://getfirefox.com/" + _counter);
   PlacesUtils.history.addVisit(uri, Date.now() * 1000, null, 1, false, 0);
   _counter++;
   return uri;
--- a/services/sync/tests/unit/test_hmac_error.js
+++ b/services/sync/tests/unit/test_hmac_error.js
@@ -1,10 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 // Track HMAC error counts.
 let hmacErrorCount = 0;
 (function () {
   let hHE = Service.handleHMACEvent;
@@ -18,30 +20,30 @@ function shared_setup() {
   hmacErrorCount = 0;
 
   // Do not instantiate SyncTestingInfrastructure; we need real crypto.
   setBasicCredentials("foo", "foo", "aabcdeabcdeabcdeabcdeabcde");
   Service.serverURL  = TEST_SERVER_URL;
   Service.clusterURL = TEST_CLUSTER_URL;
 
   // Make sure RotaryEngine is the only one we sync.
-  Engines._engines = {};
-  Engines.register(RotaryEngine);
-  let engine = Engines.get("rotary");
+  Service.engineManager._engines = {};
+  Service.engineManager.register(RotaryEngine);
+  let engine = Service.engineManager.get("rotary");
   engine.enabled = true;
   engine.lastSync = 123; // Needs to be non-zero so that tracker is queried.
   engine._store.items = {flying: "LNER Class A3 4472",
                          scotsman: "Flying Scotsman"};
   engine._tracker.addChangedID('scotsman', 0);
-  do_check_eq(1, Engines.getEnabled().length);
+  do_check_eq(1, Service.engineManager.getEnabled().length);
 
   let engines = {rotary:  {version: engine.version,
                            syncID:  engine.syncID},
-                 clients: {version: Clients.version,
-                           syncID:  Clients.syncID}};
+                 clients: {version: Service.clientsEngine.version,
+                           syncID:  Service.clientsEngine.syncID}};
 
   // Common server objects.
   let global      = new ServerWBO("global", {engines: engines});
   let keysWBO     = new ServerWBO("keys");
   let rotaryColl  = new ServerCollection({}, true);
   let clientsColl = new ServerCollection({}, true);
 
   return [engine, rotaryColl, clientsColl, keysWBO, global];
--- a/services/sync/tests/unit/test_interval_triggers.js
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -4,23 +4,24 @@
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 
 Svc.DefaultPrefs.set("registerEngines", "");
 Cu.import("resource://services-sync/service.js");
 
 let scheduler = Service.scheduler;
+let clientsEngine = Service.clientsEngine;
 
 function sync_httpd_setup() {
   let global = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
-    engines: {clients: {version: Clients.version,
-                        syncID: Clients.syncID}}
+    engines: {clients: {version: clientsEngine.version,
+                        syncID: clientsEngine.syncID}}
   });
   let clientsColl = new ServerCollection({}, true);
 
   // Tracking info/collections.
   let collectionsHelper = track_collections_helper();
   let upd = collectionsHelper.with_updated_collection;
 
   return httpd_setup({
@@ -104,17 +105,17 @@ add_test(function test_successful_sync_a
   do_check_eq(syncSuccesses, 4);
   do_check_true(scheduler.idle);
   do_check_false(scheduler.numClients > 1);
   do_check_true(scheduler.hasIncomingItems);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
 
   _("Test as long as idle && numClients > 1 our sync interval is idleInterval.");
   // idle == true && numClients > 1 && hasIncomingItems == true
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  Service.clientsEngine._store.create({id: "foo", cleartext: "bar"});
   Service.sync();
   do_check_eq(syncSuccesses, 5);
   do_check_true(scheduler.idle);
   do_check_true(scheduler.numClients > 1);
   do_check_true(scheduler.hasIncomingItems);
   do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
 
   // idle == true && numClients > 1 && hasIncomingItems == false
@@ -208,17 +209,17 @@ add_test(function test_unsuccessful_sync
   do_check_eq(syncFailures, 4);
   do_check_true(scheduler.idle);
   do_check_false(scheduler.numClients > 1);
   do_check_true(scheduler.hasIncomingItems);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
 
   _("Test as long as idle && numClients > 1 our sync interval is idleInterval.");
   // idle == true && numClients > 1 && hasIncomingItems == true
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  Service.clientsEngine._store.create({id: "foo", cleartext: "bar"});
 
   Service.sync();
   do_check_eq(syncFailures, 5);
   do_check_true(scheduler.idle);
   do_check_true(scheduler.numClients > 1);
   do_check_true(scheduler.hasIncomingItems);
   do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
 
@@ -261,26 +262,26 @@ add_test(function test_back_triggers_syn
   setUp();
 
   // Single device: no sync triggered.
   scheduler.idle = true;
   scheduler.observe(null, "back", Svc.Prefs.get("scheduler.idleTime"));
   do_check_false(scheduler.idle);
 
   // Multiple devices: sync is triggered.
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   scheduler.updateClientMode();
 
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
 
     Records.clearCache();
     Svc.Prefs.resetBranch("");
     scheduler.setDefaults();
-    Clients.resetClient();
+    clientsEngine.resetClient();
 
     Service.startOver();
     server.stop(run_next_test);
   });
 
   scheduler.idle = true;
   scheduler.observe(null, "back", Svc.Prefs.get("scheduler.idleTime"));
   do_check_false(scheduler.idle);
@@ -300,17 +301,17 @@ add_test(function test_adjust_interval_o
   _("Test unsuccessful sync updates client mode & sync intervals");
   // Force a sync fail.
   Svc.Prefs.set("firstSync", "notReady");
 
   do_check_eq(syncFailures, 0);
   do_check_false(scheduler.numClients > 1);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
 
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   Service.sync();
 
   do_check_eq(syncFailures, 1);
   do_check_true(scheduler.numClients > 1);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
 
   Svc.Obs.remove("weave:service:sync:error", onSyncError);
   Service.startOver();
@@ -371,34 +372,34 @@ add_test(function test_bug671378_scenari
 
       scheduler.scheduleNextSync();
       do_check_neq(scheduler.nextSync, 0);
       do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
       do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
     });
   });
 
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   Service.sync();
 });
 
 add_test(function test_adjust_timer_larger_syncInterval() {
   _("Test syncInterval > current timout period && nextSync != 0, syncInterval is NOT used.");
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   scheduler.updateClientMode();
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
 
   scheduler.scheduleNextSync();
 
   // Ensure we have a small interval.
   do_check_neq(scheduler.nextSync, 0);
   do_check_eq(scheduler.syncTimer.delay, scheduler.activeInterval);
 
   // Make interval large again
-  Clients._wipeClient();
+  clientsEngine._wipeClient();
   scheduler.updateClientMode();
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
 
   scheduler.scheduleNextSync();
 
   // Ensure timer delay remains as the small interval.
   do_check_neq(scheduler.nextSync, 0);
   do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
@@ -412,17 +413,17 @@ add_test(function test_adjust_timer_smal
   _("Test current timout > syncInterval period && nextSync != 0, syncInterval is used.");
   scheduler.scheduleNextSync();
 
   // Ensure we have a large interval.
   do_check_neq(scheduler.nextSync, 0);
   do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
 
   // Make interval smaller
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   scheduler.updateClientMode();
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
 
   scheduler.scheduleNextSync();
 
   // Ensure smaller timer delay is used.
   do_check_neq(scheduler.nextSync, 0);
   do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
--- a/services/sync/tests/unit/test_node_reassignment.js
+++ b/services/sync/tests/unit/test_node_reassignment.js
@@ -1,33 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 _("Test that node reassignment responses are respected on all kinds of " +
   "requests.");
 
-// Don't sync any engines by default.
-Svc.DefaultPrefs.set("registerEngines", "")
-
+Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-common/log4moz.js");
+
+Service.engineManager.clear();
 
 function run_test() {
   Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.ErrorHandler").level  = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Resource").level      = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.RESTRequest").level   = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Service").level       = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.SyncScheduler").level = Log4Moz.Level.Trace;
   initTestLogging();
 
-  Engines.register(RotaryEngine);
+  Service.engineManager.register(RotaryEngine);
 
   // 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);
 
@@ -153,17 +152,17 @@ function syncAndExpectNodeReassignment(s
 }
 
 add_test(function test_momentary_401_engine() {
   _("Test a failure for engine URLs that's resolved by reassignment.");
   let server = prepareServer();
   let john   = server.user("johndoe");
 
   _("Enabling the Rotary engine.");
-  let engine = Engines.get("rotary");
+  let engine = Service.engineManager.get("rotary");
   engine.enabled = true;
 
   // We need the server to be correctly set up prior to experimenting. Do this
   // through a sync.
   let global = {syncID: Service.syncID,
                 storageVersion: STORAGE_VERSION,
                 rotary: {version: engine.version,
                          syncID:  engine.syncID}}
@@ -348,17 +347,17 @@ add_test(function test_loop_avoidance_st
 
 add_test(function test_loop_avoidance_engine() {
   _("Test that a repeated 401 in an engine doesn't result in a sync loop " +
     "if node reassignment cannot resolve the failure.");
   let server = prepareServer();
   let john   = server.user("johndoe");
 
   _("Enabling the Rotary engine.");
-  let engine = Engines.get("rotary");
+  let engine = Service.engineManager.get("rotary");
   engine.enabled = true;
 
   // We need the server to be correctly set up prior to experimenting. Do this
   // through a sync.
   let global = {syncID: Service.syncID,
                 storageVersion: STORAGE_VERSION,
                 rotary: {version: engine.version,
                          syncID:  engine.syncID}}
--- a/services/sync/tests/unit/test_password_store.js
+++ b/services/sync/tests/unit/test_password_store.js
@@ -1,9 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines/passwords.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function run_test() {
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.Engine.Passwords").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Store.Passwords").level = Log4Moz.Level.Trace;
 
   const BOGUS_GUID_A = "zzzzzzzzzzzz";
@@ -19,39 +23,39 @@ function run_test() {
   let recordB = {id: BOGUS_GUID_B,
                   hostname: "http://foo.baz.com",
                   formSubmitURL: "http://foo.baz.com/baz",
                   username: "john",
                   password: "smith",
                   usernameField: "username",
                   passwordField: "password"};
 
-  let engine = new PasswordEngine();
+  let engine = Service.engineManager.get("passwords");
   let store = engine._store;
   function applyEnsureNoFailures(records) {
     do_check_eq(store.applyIncomingBatch(records).length, 0);
   }
 
   try {
     applyEnsureNoFailures([recordA, recordB]);
 
     // Only the good record makes it to Services.logins.
     let badCount = {};
     let goodCount = {};
     let badLogins = Services.logins.findLogins(badCount, recordA.hostname,
                                                recordA.formSubmitURL,
                                                recordA.httpRealm);
     let goodLogins = Services.logins.findLogins(goodCount, recordB.hostname,
                                                 recordB.formSubmitURL, null);
-    
+
     _("Bad: " + JSON.stringify(badLogins));
     _("Good: " + JSON.stringify(goodLogins));
     _("Count: " + badCount.value + ", " + goodCount.value);
-    
+
     do_check_eq(goodCount.value, 1);
     do_check_eq(badCount.value, 0);
-    
+
     do_check_true(!!store.getAllIDs()[BOGUS_GUID_B]);
     do_check_true(!store.getAllIDs()[BOGUS_GUID_A]);
   } finally {
     store.wipe();
   }
 }
--- a/services/sync/tests/unit/test_password_tracker.js
+++ b/services/sync/tests/unit/test_password_tracker.js
@@ -1,14 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines/passwords.js");
 Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/service.js");
 
-Engines.register(PasswordEngine);
-let engine = Engines.get("passwords");
+Service.engineManager.register(PasswordEngine);
+let engine = Service.engineManager.get("passwords");
 let store  = engine._store;
 
 function test_tracking() {
   let recordNum = 0;
 
   _("Verify we've got an empty tracker to work with.");
   let tracker = engine._tracker;
   do_check_empty(tracker.changedIDs);
--- a/services/sync/tests/unit/test_places_guid_downgrade.js
+++ b/services/sync/tests/unit/test_places_guid_downgrade.js
@@ -1,13 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/service.js");
 
 const kDBName = "places.sqlite";
 const storageSvc = Cc["@mozilla.org/storage/service;1"]
                      .getService(Ci.mozIStorageService);
 
 const fxuri = Utils.makeURI("http://getfirefox.com/");
 const tburi = Utils.makeURI("http://getthunderbird.com/");
 
@@ -78,17 +82,17 @@ add_test(function test_initial_state() {
   do_check_eq(db.schemaVersion, 10);
 
   db.close();
 
   run_next_test();
 });
 
 add_test(function test_history_guids() {
-  let engine = new HistoryEngine();
+  let engine = new HistoryEngine(Service);
   let store = engine._store;
 
   let places = [
     {
       uri: fxuri,
       title: "Get Firefox!",
       visits: [{
         visitDate: Date.now() * 1000,
@@ -147,17 +151,17 @@ add_test(function test_history_guids() {
     do_check_eq(result.length, 0);
     stmt.finalize();
 
     run_next_test();
   }
 });
 
 add_test(function test_bookmark_guids() {
-  let engine = new BookmarksEngine();
+  let engine = new BookmarksEngine(Service);
   let store = engine._store;
 
   let fxid = PlacesUtils.bookmarks.insertBookmark(
     PlacesUtils.bookmarks.toolbarFolder,
     fxuri,
     PlacesUtils.bookmarks.DEFAULT_INDEX,
     "Get Firefox!");
   let tbid = PlacesUtils.bookmarks.insertBookmark(
--- a/services/sync/tests/unit/test_prefs_store.js
+++ b/services/sync/tests/unit/test_prefs_store.js
@@ -1,30 +1,34 @@
-Cu.import("resource://services-sync/engines/prefs.js");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-common/utils.js");
-Cu.import("resource://services-common/preferences.js");
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://services-common/preferences.js");
+Cu.import("resource://services-common/utils.js");
+Cu.import("resource://services-sync/engines/prefs.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
 
 const PREFS_GUID = CommonUtils.encodeBase64URL(Services.appinfo.ID);
 
 loadAddonTestFunctions();
 startupManager();
 
 function makePersona(id) {
   return {
     id: id || Math.random().toString(),
     name: Math.random().toString(),
     headerURL: "http://localhost:1234/a"
   };
 }
 
 function run_test() {
-  let store = new PrefsEngine()._store;
+  let store = Service.engineManager.get("prefs")._store;
   let prefs = new Preferences();
   try {
 
     _("Test fixtures.");
     Svc.Prefs.set("prefs.sync.testing.int", true);
     Svc.Prefs.set("prefs.sync.testing.string", true);
     Svc.Prefs.set("prefs.sync.testing.bool", true);
     Svc.Prefs.set("prefs.sync.testing.dont.change", true);
--- a/services/sync/tests/unit/test_prefs_tracker.js
+++ b/services/sync/tests/unit/test_prefs_tracker.js
@@ -1,16 +1,20 @@
-Cu.import("resource://services-sync/engines/prefs.js");
-Cu.import("resource://services-sync/util.js");
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-common/preferences.js");
+Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-common/utils.js");
-Cu.import("resource://services-common/preferences.js");
+Cu.import("resource://services-sync/engines/prefs.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
 
 function run_test() {
-  let engine = new PrefsEngine();
+  let engine = Service.engineManager.get("prefs");
   let tracker = engine._tracker;
   let prefs = new Preferences();
 
   try {
 
     _("tracker.modified corresponds to preference.");
     do_check_eq(Svc.Prefs.get("engine.prefs.modified"), undefined);
     do_check_false(tracker.modified);
--- a/services/sync/tests/unit/test_score_triggers.js
+++ b/services/sync/tests/unit/test_score_triggers.js
@@ -1,21 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 
-Svc.DefaultPrefs.set("registerEngines", "");
-Cu.import("resource://services-sync/service.js");
-
-Engines.register(RotaryEngine);
-let engine = Engines.get("rotary");
+Service.engineManager.clear();
+Service.engineManager.register(RotaryEngine);
+let engine = Service.engineManager.get("rotary");
 let tracker = engine._tracker;
 engine.enabled = true;
 
 // Tracking info/collections.
 let collectionsHelper = track_collections_helper();
 let upd = collectionsHelper.with_updated_collection;
 
 function sync_httpd_setup() {
@@ -108,17 +107,17 @@ add_test(function test_clients_engine_sy
   Svc.Obs.add(TOPIC, function onSyncFinish() {
     Svc.Obs.remove(TOPIC, onSyncFinish);
     _("Sync due to clients engine change completed.");
     server.stop(run_next_test);
   });
 
   Service.scheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
   do_check_eq(Status.login, LOGIN_SUCCEEDED);
-  Clients._tracker.score += SCORE_INCREMENT_XLARGE;
+  Service.clientsEngine._tracker.score += SCORE_INCREMENT_XLARGE;
 });
 
 add_test(function test_incorrect_credentials_sync_not_triggered() {
   _("Ensure that score changes don't trigger a sync if Status.login != LOGIN_SUCCEEDED.");
   let server = sync_httpd_setup();
   setUp();
 
   // Ensure we don't actually try to sync.
--- a/services/sync/tests/unit/test_service_detect_upgrade.js
+++ b/services/sync/tests/unit/test_service_detect_upgrade.js
@@ -6,18 +6,18 @@ Cu.import("resource://services-sync/serv
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/engines/tabs.js");
 Cu.import("resource://services-common/log4moz.js");
-  
-Engines.register(TabEngine);
+
+Service.engineManager.register(TabEngine);
 
 add_test(function v4_upgrade() {
   let passphrase = "abcdeabcdeabcdeabcdeabcdea";
 
   let clients = new ServerCollection();
   let meta_global = new ServerWBO('global');
 
   // Tracking info/collections.
--- a/services/sync/tests/unit/test_service_startOver.js
+++ b/services/sync/tests/unit/test_service_startOver.js
@@ -1,30 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/util.js");
 
 function BlaEngine() {
-  SyncEngine.call(this, "Bla");
+  SyncEngine.call(this, "Bla", Service);
 }
 BlaEngine.prototype = {
   __proto__: SyncEngine.prototype,
 
   removed: false,
   removeClientData: function() {
     this.removed = true;
   }
 
 };
-Engines.register(BlaEngine);
+
+Service.engineManager.register(BlaEngine);
 
 
 function run_test() {
   initTestLogging("Trace");
   run_next_test();
 }
 
 add_test(function test_resetLocalData() {
@@ -60,17 +61,17 @@ add_test(function test_resetLocalData() 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.backoffInterval, 0);
   do_check_eq(Status.minimumNextSync, 0);
 
   run_next_test();
 });
 
 add_test(function test_removeClientData() {
-  let engine = Engines.get("bla");
+  let engine = Service.engineManager.get("bla");
 
   // No cluster URL = no removal.
   do_check_false(engine.removed);
   Service.startOver();
   do_check_false(engine.removed);
 
   Svc.Prefs.set("serverURL", TEST_SERVER_URL);
   Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
--- a/services/sync/tests/unit/test_service_startup.js
+++ b/services/sync/tests/unit/test_service_startup.js
@@ -1,34 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/observers.js");
+Cu.import("resource://services-sync/identity.js");
+Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-sync/engines.js");
+
+Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
+Cu.import("resource://services-sync/service.js");
 
 function run_test() {
   _("When imported, Service.onStartup is called");
   initTestLogging("Trace");
 
-  Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
   new SyncTestingInfrastructure();
 
   // Test fixtures
   Identity.username = "johndoe";
 
   Cu.import("resource://services-sync/service.js");
 
   _("Service is enabled.");
   do_check_eq(Service.enabled, true);
 
   _("Engines are registered.");
-  let engines = Engines.getAll();
+  let engines = Service.engineManager.getAll();
   do_check_true(Utils.deepEquals([engine.name for each (engine in engines)],
                                  ['tabs', 'bookmarks', 'forms', 'history']));
 
   _("Observers are notified of startup");
   do_test_pending();
   do_check_false(Status.ready);
   Observers.add("weave:service:ready", function (subject, data) {
     do_check_true(Status.ready);
--- a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
+++ b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
@@ -1,55 +1,54 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 
-Svc.DefaultPrefs.set("registerEngines", "");
-Cu.import("resource://services-sync/service.js");
-
 initTestLogging();
+Service.engineManager.clear();
 
 function QuietStore() {
   Store.call("Quiet");
 }
 QuietStore.prototype = {
   getAllIDs: function getAllIDs() {
     return [];
   }
 }
 
 function SteamEngine() {
-  SyncEngine.call(this, "Steam");
+  SyncEngine.call(this, "Steam", Service);
 }
 SteamEngine.prototype = {
   __proto__: SyncEngine.prototype,
   // We're not interested in engine sync but what the service does.
   _storeObj: QuietStore,
 
   _sync: function _sync() {
     this._syncStartup();
   }
 };
-Engines.register(SteamEngine);
+Service.engineManager.register(SteamEngine);
 
 function StirlingEngine() {
-  SyncEngine.call(this, "Stirling");
+  SyncEngine.call(this, "Stirling", Service);
 }
 StirlingEngine.prototype = {
   __proto__: SteamEngine.prototype,
   // This engine's enabled state is the same as the SteamEngine's.
   get prefName() "steam"
 };
-Engines.register(StirlingEngine);
+Service.engineManager.register(StirlingEngine);
 
 // Tracking info/collections.
 let collectionsHelper = track_collections_helper();
 let upd = collectionsHelper.with_updated_collection;
 
 function sync_httpd_setup(handlers) {
 
   handlers["/1.1/johndoe/info/collections"] = collectionsHelper.handler;
@@ -86,17 +85,17 @@ function run_test() {
   Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
 
   run_next_test();
 }
 
 add_test(function test_newAccount() {
   _("Test: New account does not disable locally enabled engines.");
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
   setUp();
 
   try {
     _("Engine is enabled from the beginning.");
@@ -113,17 +112,17 @@ add_test(function test_newAccount() {
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_enabledLocally() {
   _("Test: Engine is disabled on remote clients and enabled locally");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
   setUp();
@@ -144,17 +143,17 @@ add_test(function test_enabledLocally() 
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_disabledLocally() {
   _("Test: Engine is enabled on remote clients and disabled locally");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
     engines: {steam: {syncID: engine.syncID,
                       version: engine.version}}
   });
   let steamCollection = new ServerWBO("steam", PAYLOAD);
 
@@ -186,17 +185,17 @@ add_test(function test_disabledLocally()
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_disabledLocally_wipe503() {
   _("Test: Engine is enabled on remote clients and disabled locally");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
     engines: {steam: {syncID: engine.syncID,
                       version: engine.version}}
   });
   let steamCollection = new ServerWBO("steam", PAYLOAD);
 
@@ -230,17 +229,17 @@ add_test(function test_disabledLocally_w
 
   _("Sync.");
   Service.errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_enabledRemotely() {
   _("Test: Engine is disabled locally and enabled on a remote client");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
     engines: {steam: {syncID: engine.syncID,
                       version: engine.version}}
   });
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global":
@@ -274,17 +273,17 @@ add_test(function test_enabledRemotely()
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_disabledRemotelyTwoClients() {
   _("Test: Engine is enabled locally and disabled on a remote client... with two clients.");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global":
     upd("meta", metaWBO.handler()),
 
     "/1.1/johndoe/storage/steam":
@@ -303,32 +302,32 @@ add_test(function test_disabledRemotelyT
 
     _("Disable engine by deleting from meta/global.");
     let d = metaWBO.data;
     delete d.engines["steam"];
     metaWBO.payload = JSON.stringify(d);
     metaWBO.modified = Date.now() / 1000;
 
     _("Add a second client and verify that the local pref is changed.");
-    Clients._store._remoteClients["foobar"] = {name: "foobar", type: "desktop"};
+    Service.clientsEngine._store._remoteClients["foobar"] = {name: "foobar", type: "desktop"};
     Service.sync();
 
     _("Engine is disabled.");
     do_check_false(engine.enabled);
 
   } finally {
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_disabledRemotely() {
   _("Test: Engine is enabled locally and disabled on a remote client");
   Service.syncID = "abcdefghij";
-  let engine = Engines.get("steam");
+  let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
   setUp();
@@ -349,18 +348,18 @@ add_test(function test_disabledRemotely(
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_dependentEnginesEnabledLocally() {
   _("Test: Engine is disabled on remote clients and enabled locally");
   Service.syncID = "abcdefghij";
-  let steamEngine = Engines.get("steam");
-  let stirlingEngine = Engines.get("stirling");
+  let steamEngine = Service.engineManager.get("steam");
+  let stirlingEngine = Service.engineManager.get("stirling");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(),
     "/1.1/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler()
   });
@@ -384,18 +383,18 @@ add_test(function test_dependentEnginesE
     Service.startOver();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_dependentEnginesDisabledLocally() {
   _("Test: Two dependent engines are enabled on remote clients and disabled locally");
   Service.syncID = "abcdefghij";
-  let steamEngine = Engines.get("steam");
-  let stirlingEngine = Engines.get("stirling");
+  let steamEngine = Service.engineManager.get("steam");
+  let stirlingEngine = Service.engineManager.get("stirling");
   let metaWBO = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
     engines: {steam: {syncID: steamEngine.syncID,
                       version: steamEngine.version},
               stirling: {syncID: stirlingEngine.syncID,
                          version: stirlingEngine.version}}
   });
--- a/services/sync/tests/unit/test_service_wipeClient.js
+++ b/services/sync/tests/unit/test_service_wipeClient.js
@@ -1,73 +1,72 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/identity.js");
+Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
-Svc.DefaultPrefs.set("registerEngines", "");
-Cu.import("resource://services-sync/service.js");
-
+Service.engineManager.clear();
 
 function CanDecryptEngine() {
-  SyncEngine.call(this, "CanDecrypt");
+  SyncEngine.call(this, "CanDecrypt", Service);
 }
 CanDecryptEngine.prototype = {
   __proto__: SyncEngine.prototype,
 
   // Override these methods with mocks for the test
   canDecrypt: function canDecrypt() {
     return true;
   },
 
   wasWiped: false,
   wipeClient: function wipeClient() {
     this.wasWiped = true;
   }
 };
-Engines.register(CanDecryptEngine);
+Service.engineManager.register(CanDecryptEngine);
 
 
 function CannotDecryptEngine() {
-  SyncEngine.call(this, "CannotDecrypt");
+  SyncEngine.call(this, "CannotDecrypt", Service);
 }
 CannotDecryptEngine.prototype = {
   __proto__: SyncEngine.prototype,
 
   // Override these methods with mocks for the test
   canDecrypt: function canDecrypt() {
     return false;
   },
 
   wasWiped: false,
   wipeClient: function wipeClient() {
     this.wasWiped = true;
   }
 };
-Engines.register(CannotDecryptEngine);
+Service.engineManager.register(CannotDecryptEngine);
 
 
 add_test(function test_withEngineList() {
   try {
     _("Ensure initial scenario.");
-    do_check_false(Engines.get("candecrypt").wasWiped);
-    do_check_false(Engines.get("cannotdecrypt").wasWiped);
+    do_check_false(Service.engineManager.get("candecrypt").wasWiped);
+    do_check_false(Service.engineManager.get("cannotdecrypt").wasWiped);
 
     _("Wipe local engine data.");
     Service.wipeClient(["candecrypt", "cannotdecrypt"]);
 
     _("Ensure only the engine that can decrypt was wiped.");
-    do_check_true(Engines.get("candecrypt").wasWiped);
-    do_check_false(Engines.get("cannotdecrypt").wasWiped);
+    do_check_true(Service.engineManager.get("candecrypt").wasWiped);
+    do_check_false(Service.engineManager.get("cannotdecrypt").wasWiped);
   } finally {
-    Engines.get("candecrypt").wasWiped = false;
-    Engines.get("cannotdecrypt").wasWiped = false;
+    Service.engineManager.get("candecrypt").wasWiped = false;
+    Service.engineManager.get("cannotdecrypt").wasWiped = false;
     Service.startOver();
   }
 
   run_next_test();
 });
 
 add_test(function test_startOver_clears_keys() {
   generateNewKeys();
--- a/services/sync/tests/unit/test_syncengine.js
+++ b/services/sync/tests/unit/test_syncengine.js
@@ -1,13 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function makeSteamEngine() {
-  return new SyncEngine('Steam');
+  return new SyncEngine('Steam', Service);
 }
 
 function test_url_attributes() {
   _("SyncEngine url attributes");
   let syncTesting = new SyncTestingInfrastructure();
   Svc.Prefs.set("clusterURL", "https://cluster/");
   let engine = makeSteamEngine();
   try {
--- a/services/sync/tests/unit/test_syncengine_sync.js
+++ b/services/sync/tests/unit/test_syncengine_sync.js
@@ -6,17 +6,17 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function makeRotaryEngine() {
-  return new RotaryEngine();
+  return new RotaryEngine(Service);
 }
 
 function cleanAndGo(server) {
   Svc.Prefs.resetBranch("");
   Svc.Prefs.set("log.logger.engine.rotary", "Trace");
   Records.clearCache();
   server.stop(run_next_test);
 }
@@ -24,17 +24,17 @@ function cleanAndGo(server) {
 function configureService(username, password) {
   Service.clusterURL = TEST_CLUSTER_URL;
 
   Identity.account = username || "foo";
   Identity.basicPassword = password || "password";
 }
 
 function createServerAndConfigureClient() {
-  let engine = new RotaryEngine();
+  let engine = new RotaryEngine(Service);
 
   let contents = {
     meta: {global: {engines: {rotary: {version: engine.version,
                                        syncID:  engine.syncID}}}},
     crypto: {},
     rotary: {}
   };
 
--- a/services/sync/tests/unit/test_syncscheduler.js
+++ b/services/sync/tests/unit/test_syncscheduler.js
@@ -5,39 +5,40 @@ Cu.import("resource://services-sync/cons
 Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 
-Svc.DefaultPrefs.set("registerEngines", "");
+Service.engineManager.clear();
 
 function CatapultEngine() {
-  SyncEngine.call(this, "Catapult");
+  SyncEngine.call(this, "Catapult", Service);
 }
 CatapultEngine.prototype = {
   __proto__: SyncEngine.prototype,
   exception: null, // tests fill this in
   _sync: function _sync() {
     throw this.exception;
   }
 };
 
-Engines.register(CatapultEngine);
+Service.engineManager.register(CatapultEngine);
 
 let scheduler = new SyncScheduler(Service);
+let clientsEngine = Service.clientsEngine;
 
 function sync_httpd_setup() {
   let global = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
-    engines: {clients: {version: Clients.version,
-                        syncID: Clients.syncID}}
+    engines: {clients: {version: clientsEngine.version,
+                        syncID: clientsEngine.syncID}}
   });
   let clientsColl = new ServerCollection({}, true);
 
   // Tracking info/collections.
   let collectionsHelper = track_collections_helper();
   let upd = collectionsHelper.with_updated_collection;
 
   return httpd_setup({
@@ -144,26 +145,26 @@ add_test(function test_prefAttributes() 
 add_test(function test_updateClientMode() {
   _("Test updateClientMode adjusts scheduling attributes based on # of clients appropriately");
   do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.numClients > 1);
   do_check_false(scheduler.idle);
 
   // Trigger a change in interval & threshold by adding a client.
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   scheduler.updateClientMode();
 
   do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
   do_check_true(scheduler.numClients > 1);
   do_check_false(scheduler.idle);
 
   // Resets the number of clients to 0. 
-  Clients.resetClient();
+  clientsEngine.resetClient();
   scheduler.updateClientMode();
 
   // Goes back to single user if # clients is 1.
   do_check_eq(scheduler.numClients, 1);
   do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.numClients > 1);
   do_check_false(scheduler.idle);
@@ -405,28 +406,28 @@ add_test(function test_client_sync_finis
   setUp();
 
   // Confirm defaults.
   do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.idle);
 
   // Trigger a change in interval & threshold by adding a client.
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   do_check_false(scheduler.numClients > 1);
   scheduler.updateClientMode();
   Service.sync();
 
   do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
   do_check_true(scheduler.numClients > 1);
   do_check_false(scheduler.idle);
 
   // Resets the number of clients to 0. 
-  Clients.resetClient();
+  clientsEngine.resetClient();
   Service.sync();
 
   // Goes back to single user if # clients is 1.
   do_check_eq(scheduler.numClients, 1);
   do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.numClients > 1);
   do_check_false(scheduler.idle);
@@ -574,17 +575,17 @@ add_test(function test_idle_adjustSyncIn
 
   // Single device: nothing changes.
   scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
   do_check_eq(scheduler.idle, true);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
 
   // Multiple devices: switch to idle interval.
   scheduler.idle = false;
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
   scheduler.updateClientMode();
   scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
   do_check_eq(scheduler.idle, true);
   do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
 
   cleanUpAndGo();
 });
 
@@ -679,17 +680,17 @@ add_test(function test_no_sync_node() {
   cleanUpAndGo(server);
 });
 
 add_test(function test_sync_failed_partial_500s() {
   _("Test a 5xx status calls handleSyncError.");
   scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF;
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = Service.engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 500};
 
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
   do_check_true(setUp());
 
   Service.sync();
@@ -706,22 +707,22 @@ add_test(function test_sync_failed_parti
   cleanUpAndGo(server);
 });
 
 add_test(function test_sync_failed_partial_400s() {
   _("Test a non-5xx status doesn't call handleSyncError.");
   scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF;
   let server = sync_httpd_setup();
 
-  let engine = Engines.get("catapult");
+  let engine = Service.engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 400};
 
   // Have multiple devices for an active interval.
-  Clients._store.create({id: "foo", cleartext: "bar"});
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
 
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
   do_check_true(setUp());
 
   Service.sync();
 
   do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
@@ -753,20 +754,20 @@ add_test(function test_sync_X_Weave_Back
       response.setHeader("X-Weave-Backoff", "" + BACKOFF);
     }
     infoColl(request, response);
   }
   server.registerPathHandler(INFO_COLLECTIONS, infoCollWithBackoff);
 
   // Pretend we have two clients so that the regular sync interval is
   // sufficiently low.
-  Clients._store.create({id: "foo", cleartext: "bar"});
-  let rec = Clients._store.createRecord("foo", "clients");
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
+  let rec = clientsEngine._store.createRecord("foo", "clients");
   rec.encrypt();
-  rec.upload(Clients.engineURL + rec.id);
+  rec.upload(clientsEngine.engineURL + rec.id);
 
   // Sync once to log in and get everything set up. Let's verify our initial
   // values.
   Service.sync();
   do_check_eq(Status.backoffInterval, 0);
   do_check_eq(Status.minimumNextSync, 0);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
   do_check_true(scheduler.nextSync <=
@@ -810,20 +811,20 @@ add_test(function test_sync_503_Retry_Af
     }
     response.setHeader("Retry-After", "" + BACKOFF);
     response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
   }
   server.registerPathHandler(INFO_COLLECTIONS, infoCollWithMaintenance);
 
   // Pretend we have two clients so that the regular sync interval is
   // sufficiently low.
-  Clients._store.create({id: "foo", cleartext: "bar"});
-  let rec = Clients._store.createRecord("foo", "clients");
+  clientsEngine._store.create({id: "foo", cleartext: "bar"});
+  let rec = clientsEngine._store.createRecord("foo", "clients");
   rec.encrypt();
-  rec.upload(Clients.engineURL + rec.id);
+  rec.upload(clientsEngine.engineURL + rec.id);
 
   // Sync once to log in and get everything set up. Let's verify our initial
   // values.
   Service.sync();
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.backoffInterval, 0);
   do_check_eq(Status.minimumNextSync, 0);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
--- a/services/sync/tests/unit/test_tab_engine.js
+++ b/services/sync/tests/unit/test_tab_engine.js
@@ -1,9 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines/tabs.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 function fakeSessionSvc() {
   let tabs = [];
   for(let i = 0; i < arguments.length; i++) {
     tabs.push({
       index: 1,
       entries: [{
@@ -25,17 +29,17 @@ function fakeSessionSvc() {
   Svc.Session = {
     getBrowserState: function() JSON.stringify(obj)
   };
 }
 
 function run_test() {
 
   _("test locallyOpenTabMatchesURL");
-  let engine = new TabEngine();
+  let engine = new TabEngine(Service);
 
   // 3 tabs
   fakeSessionSvc("http://bar.com", "http://foo.com", "http://foobar.com");
 
   let matches;
  
   _("  test matching works (true)");
   matches = engine.locallyOpenTabMatchesURL("http://foo.com");
--- a/services/sync/tests/unit/test_tab_store.js
+++ b/services/sync/tests/unit/test_tab_store.js
@@ -1,17 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines/tabs.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services-common/utils.js");
 
 function test_lastUsed() {
-  let store = new TabEngine()._store;
+  let store = new TabEngine(Service)._store;
 
   _("Check extraction of last used times from tab objects.");
   let expected = [
     [0,         {}],
     [0,         {extData: null}],
     [0,         {extData: {}}],
     [0,         {extData: {weaveLastUsed: null}}],
     [123456789, {extData: {weaveLastUsed: "123456789"}}],
@@ -20,17 +21,17 @@ function test_lastUsed() {
   ];
 
   for each (let [ex, input] in expected) {
     do_check_eq(ex, store.tabLastUsed(input));
   }
 }
 
 function test_create() {
-  let store = new TabEngine()._store;
+  let store = new TabEngine(Service)._store;
 
   _("Create a first record");
   let rec = {id: "id1",
              clientName: "clientName1",
              cleartext: "cleartext1",
              modified: 1000};
   store.applyIncoming(rec);
   do_check_eq(store._remoteClients["id1"], "cleartext1");
@@ -87,17 +88,17 @@ function fakeSessionSvc(url, numtabs) {
           tabs.push(TestingUtils.deepCopy(tabs[0]));
       }
       return JSON.stringify(obj);
     }
   };
 };
 
 function test_getAllTabs() {
-  let store = new TabEngine()._store, tabs;
+  let store = new TabEngine(Service)._store, tabs;
 
   _("get all tabs");
   fakeSessionSvc("http://foo.com");
   tabs = store.getAllTabs();
   do_check_eq(tabs.length, 1);
   do_check_eq(tabs[0].title, "title");
   do_check_eq(tabs[0].urlHistory.length, 1);
   do_check_eq(tabs[0].urlHistory[0], ["http://foo.com"]);
@@ -108,17 +109,17 @@ function test_getAllTabs() {
   // we don't bother testing every URL type here, the
   // filteredUrls regex really should have it own tests
   fakeSessionSvc("about:foo");
   tabs = store.getAllTabs(true);
   do_check_eq(tabs.length, 0);
 }
 
 function test_createRecord() {
-  let store = new TabEngine()._store, record;
+  let store = new TabEngine(Service)._store, record;
 
   // get some values before testing
   fakeSessionSvc("http://foo.com");
   let tabs = store.getAllTabs();
   let tabsize = JSON.stringify(tabs[0]).length;
   let numtabs = Math.ceil(20000./77.);
 
   _("create a record");
--- a/services/sync/tests/unit/test_tab_tracker.js
+++ b/services/sync/tests/unit/test_tab_tracker.js
@@ -1,12 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines/tabs.js");
-Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
+let clientsEngine = Service.clientsEngine;
+
 function fakeSvcWinMediator() {
   // actions on windows are captured in logs
   let logs = [];
   delete Services.wm;
   Services.wm = {
     getEnumerator: function() {
       return {
         cnt: 2,
@@ -37,23 +42,23 @@ function fakeSvcSession() {
     setTabValue: function(target, prop, value) {
       logs.push({target: target, prop: prop, value: value});
     }
   };
   return logs;
 }
 
 function run_test() {
-  let engine = new TabEngine();
+  let engine = Service.engineManager.get("tabs");
 
   _("We assume that tabs have changed at startup.");
   let tracker = engine._tracker;
   do_check_true(tracker.modified);
   do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
-                                 [Clients.localID]));
+                                 [clientsEngine.localID]));
 
   let logs;
 
   _("Test listeners are registered on windows");
   logs = fakeSvcWinMediator();
   Svc.Obs.notify("weave:engine:start-tracking");
   do_check_eq(logs.length, 2);
   for each (let log in logs) {
@@ -87,28 +92,28 @@ function run_test() {
     // Pretend we just synced.
     tracker.clearChangedIDs();
     do_check_false(tracker.modified);
 
     // Send a fake tab event
     tracker.onTab({type: evttype , originalTarget: evttype});
     do_check_true(tracker.modified);
     do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
-                                   [Clients.localID]));
+                                   [clientsEngine.localID]));
     do_check_eq(logs.length, idx+1);
     do_check_eq(logs[idx].target, evttype);
     do_check_eq(logs[idx].prop, "weaveLastUsed");
     do_check_true(typeof logs[idx].value == "number");
     idx++;
   }
 
   // Pretend we just synced.
   tracker.clearChangedIDs();
   do_check_false(tracker.modified);
 
   tracker.onTab({type: "pageshow", originalTarget: "pageshow"});
   do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
-                                 [Clients.localID]));
+                                 [clientsEngine.localID]));
   do_check_eq(logs.length, idx); // test that setTabValue isn't called
   if (tracker._lazySave) {
     tracker._lazySave.clear();
   }
 }
--- a/services/sync/tests/unit/test_tracker_addChanged.js
+++ b/services/sync/tests/unit/test_tracker_addChanged.js
@@ -1,12 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/service.js");
 
 function run_test() {
-  let tracker = new Tracker();
+  let tracker = new Tracker("Tracker", Service);
   let id = "the_id!";
 
   _("Make sure nothing exists yet..");
   do_check_eq(tracker.changedIDs[id], null);
 
   _("Make sure adding of time 0 works");
   tracker.addChangedID(id, 0);
   do_check_eq(tracker.changedIDs[id], 0);