Merge mozilla-cental into mozilla-inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 31 Jan 2013 11:10:40 -0500
changeset 130340 683b08dc1afd73e823f85c6352115d94e8df8168
parent 130339 6950feef17ea714bdf14c287892e1f442a3b2e71 (current diff)
parent 130235 cbeab7da0e3aad6f051ddfd50a089c3265b01ae4 (diff)
child 130341 50cf5bbcb1803ec23cb6cb0847b5cb8101eb864f
child 130355 a03b43bb56a138d6caa2b04377ab505ba51d1250
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-cental into mozilla-inbound
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -116,19 +116,27 @@ SettingsListener.observe('language.curre
   }
 
   let intl = '';
   try {
     intl = Services.prefs.getComplexValue(prefName,
                                           Ci.nsIPrefLocalizedString).data;
   } catch(e) {}
 
+  // Bug 830782 - Homescreen is in English instead of selected locale after
+  // the first run experience.
+  // In order to ensure the current intl value is reflected on the child
+  // process let's always write a user value, even if this one match the
+  // current localized pref value.
   if (!((new RegExp('^' + value + '[^a-z-_] *[,;]?', 'i')).test(intl))) {
-    Services.prefs.setCharPref(prefName, value + ', ' + intl);
+    value = value + ', ' + intl;
+  } else {
+    value = intl;
   }
+  Services.prefs.setCharPref(prefName, value);
 
   if (shell.hasStarted() == false) {
     shell.start();
   }
 });
 
 // =================== RIL ====================
 (function RILSettingsToPrefs() {
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -18,17 +18,20 @@ let gSyncUI = {
          "weave:ui:clear-error",
   ],
 
   _unloaded: false,
 
   init: function SUI_init() {
     // Proceed to set up the UI if Sync has already started up.
     // Otherwise we'll do it when Sync is firing up.
-    if (Weave.Status.ready) {
+    let xps = Components.classes["@mozilla.org/weave/service;1"]
+                                .getService(Components.interfaces.nsISupports)
+                                .wrappedJSObject;
+    if (xps.ready) {
       this.initUI();
       return;
     }
 
     Services.obs.addObserver(this, "weave:service:ready", true);
 
     // Remove the observer if the window is closed before the observer
     // was triggered.
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -28,16 +28,47 @@ let gSyncPane = {
   needsUpdate: function () {
     this.page = PAGE_NEEDS_UPDATE;
     let label = document.getElementById("loginError");
     label.value = Weave.Utils.getErrorString(Weave.Status.login);
     label.className = "error";
   },
 
   init: function () {
+    // If the Service hasn't finished initializing, wait for it.
+    let xps = Components.classes["@mozilla.org/weave/service;1"]
+                                .getService(Components.interfaces.nsISupports)
+                                .wrappedJSObject;
+
+    if (xps.ready) {
+      this._init();
+      return;
+    }
+
+    let onUnload = function () {
+      window.removeEventListener("unload", onUnload, false);
+      try {
+        Services.obs.removeObserver(onReady, "weave:service:ready");
+      } catch (e) {}
+    };
+
+    let onReady = function () {
+      Services.obs.removeObserver(onReady, "weave:service:ready");
+      window.removeEventListener("unload", onUnload, false);
+      this._init();
+    }.bind(this);
+
+
+    Services.obs.addObserver(onReady, "weave:service:ready", false);
+    window.addEventListener("unload", onUnload, false);
+
+    xps.ensureLoaded();
+  },
+
+  _init: function () {
     let topics = ["weave:service:login:error",
                   "weave:service:login:finish",
                   "weave:service:start-over",
                   "weave:service:setup-complete",
                   "weave:service:logout:finish"];
 
     // Add the observers now and remove them on unload
     //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -29,16 +29,46 @@ let gSyncPane = {
   needsUpdate: function () {
     this.page = PAGE_NEEDS_UPDATE;
     let label = document.getElementById("loginError");
     label.value = Weave.Utils.getErrorString(Weave.Status.login);
     label.className = "error";
   },
 
   init: function () {
+    // If the Service hasn't finished initializing, wait for it.
+    let xps = Components.classes["@mozilla.org/weave/service;1"]
+                                .getService(Components.interfaces.nsISupports)
+                                .wrappedJSObject;
+
+    if (xps.ready) {
+      this._init();
+      return;
+    }
+
+    let onUnload = function () {
+      window.removeEventListener("unload", onUnload, false);
+      try {
+        Services.obs.removeObserver(onReady, "weave:service:ready");
+      } catch (e) {}
+    };
+
+    let onReady = function () {
+      Services.obs.removeObserver(onReady, "weave:service:ready");
+      window.removeEventListener("unload", onUnload, false);
+      this._init();
+    }.bind(this);
+
+    Services.obs.addObserver(onReady, "weave:service:ready", false);
+    window.addEventListener("unload", onUnload, false);
+
+    xps.ensureLoaded();
+  },
+
+  _init: function () {
     let topics = ["weave:service:login:error",
                   "weave:service:login:finish",
                   "weave:service:start-over",
                   "weave:service:setup-complete",
                   "weave:service:logout:finish"];
 
     // Add the observers now and remove them on unload
     //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -144,16 +144,18 @@ static nsITimer *sInterSliceGCTimer;
 static PRTime sLastCCEndTime;
 
 static bool sCCLockedOut;
 static PRTime sCCLockedOutTime;
 
 static JS::GCSliceCallback sPrevGCSliceCallback;
 static js::AnalysisPurgeCallback sPrevAnalysisPurgeCallback;
 
+static bool sHasRunGC;
+
 // The number of currently pending document loads. This count isn't
 // guaranteed to always reflect reality and can't easily as we don't
 // have an easy place to know when a load ends or is interrupted in
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static uint32_t sPendingLoadCount;
 static bool sLoadingInProgress;
@@ -3075,17 +3077,17 @@ nsJSContext::PokeShrinkGCBuffers()
                                               NS_SHRINK_GC_BUFFERS_DELAY,
                                               nsITimer::TYPE_ONE_SHOT);
 }
 
 // static
 void
 nsJSContext::MaybePokeCC()
 {
-  if (sCCTimer || sShuttingDown) {
+  if (sCCTimer || sShuttingDown || !sHasRunGC) {
     return;
   }
 
   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
     sCCTimerFireCount = 0;
     CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
     if (!sCCTimer) {
       return;
@@ -3234,16 +3236,17 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
 
   if (aProgress == JS::GC_CYCLE_END) {
     // May need to kill the inter-slice GC timer
     nsJSContext::KillInterSliceGCTimer();
 
     sCCollectedWaitingForGC = 0;
     sCleanupsSinceLastGC = 0;
     sNeedsFullCC = true;
+    sHasRunGC = true;
     nsJSContext::MaybePokeCC();
 
     if (aDesc.isCompartment) {
       ++sCompartmentGCCount;
       if (!sFullGCTimer && !sShuttingDown) {
         CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
         JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER;
         sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
@@ -3353,16 +3356,17 @@ nsJSRuntime::CreateContext(bool aGCOnDes
 void
 nsJSRuntime::Startup()
 {
   // initialize all our statics, so that we can restart XPCOM
   sGCTimer = sFullGCTimer = sCCTimer = nullptr;
   sCCLockedOut = false;
   sCCLockedOutTime = 0;
   sLastCCEndTime = 0;
+  sHasRunGC = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sPostGCEventsToConsole = false;
   sDisableExplicitCompartmentGC = false;
   sNeedsFullCC = false;
   gNameSpaceManager = nullptr;
   sRuntimeService = nullptr;
--- a/mobile/xul/chrome/content/sync.js
+++ b/mobile/xul/chrome/content/sync.js
@@ -9,19 +9,38 @@ let WeaveGlue = {
   jpake: null,
   _bundle: null,
   _loginError: false,
   _progressBar: null,
   _progressValue: 0,
   _progressMax: null,
 
   init: function init() {
-    if (this._bundle)
+    if (this._bundle) {
+      return;
+    }
+
+    let service = Components.classes["@mozilla.org/weave/service;1"]
+                                    .getService(Components.interfaces.nsISupports)
+                                    .wrappedJSObject;
+
+    if (service.ready) {
+      this._init();
       return;
+    }
 
+    Services.obs.addObserver(function onReady() {
+      Services.obs.removeObserver(onReady, "weave:service:ready");
+      this._init();
+    }.bind(this), "weave:service:ready", false);
+
+    service.ensureLoaded();
+  },
+
+  _init: function () {
     this._bundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
     this._msg = document.getElementById("prefs-messages");
 
     this._addListeners();
 
     this.setupData = { account: "", password: "" , synckey: "", serverURL: "" };
 
     if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
--- a/services/common/Makefile.in
+++ b/services/common/Makefile.in
@@ -5,27 +5,31 @@
 DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 modules := \
-  async.js \
   log4moz.js \
-  observers.js \
   preferences.js \
-  rest.js \
   storageservice.js \
   stringbundle.js \
   tokenserverclient.js \
   utils.js \
   $(NULL)
 
+pp_modules := \
+  async.js \
+  bagheeraclient.js \
+  observers.js \
+  rest.js \
+  $(NULL)
+
 testing_modules := \
   aitcserver.js \
   bagheeraserver.js \
   logging.js \
   storageserver.js \
   utils.js \
   $(NULL)
 
@@ -39,17 +43,17 @@ PREF_JS_EXPORTS = $(srcdir)/services-com
 
 MODULES_FILES := $(modules)
 MODULES_DEST = $(FINAL_TARGET)/modules/services-common
 INSTALL_TARGETS += MODULES
 
 TESTING_JS_MODULES := $(addprefix modules-testing/,$(testing_modules))
 TESTING_JS_MODULE_DIR := services-common
 
-PP_JS_MODULES := bagheeraclient.js
+PP_JS_MODULES := $(pp_modules)
 PP_JS_MODULES_PATH = $(FINAL_TARGET)/modules/services-common
 PP_TARGETS += PP_JS_MODULES
 
 include $(topsrcdir)/config/rules.mk
 
 # What follows is a helper to launch a standalone storage server instance.
 # Most of the code lives in a Python script in the tests directory. If we
 # ever consolidate our Python code, and/or have a supplemental driver for the
--- a/services/common/async.js
+++ b/services/common/async.js
@@ -1,16 +1,20 @@
 /* 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/. */
 
+#ifndef MERGED_COMPARTMENT
+
 this.EXPORTED_SYMBOLS = ["Async"];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
+#endif
+
 // Constants for makeSyncCallback, waitForSyncCallback.
 const CB_READY = {};
 const CB_COMPLETE = {};
 const CB_FAIL = {};
 
 const REASON_ERROR = Ci.mozIStorageStatementCallback.REASON_ERROR;
 
 Cu.import("resource://gre/modules/Services.jsm");
--- a/services/common/bagheeraclient.js
+++ b/services/common/bagheeraclient.js
@@ -15,21 +15,21 @@
 
 this.EXPORTED_SYMBOLS = [
   "BagheeraClient",
   "BagheeraClientRequestResult",
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://services-common/rest.js");
 #endif
 
 Cu.import("resource://gre/modules/commonjs/promise/core.js");
 Cu.import("resource://services-common/log4moz.js");
-Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-common/utils.js");
 
 
 /**
  * Represents the result of a Bagheera request.
  */
 this.BagheeraClientRequestResult = function BagheeraClientRequestResult() {
   this.transportSuccess = false;
--- a/services/common/observers.js
+++ b/services/common/observers.js
@@ -1,19 +1,23 @@
 /* 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/. */
 
+#ifndef MERGED_COMPARTMENT
+
 this.EXPORTED_SYMBOLS = ["Observers"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
+#endif
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 /**
  * A service for adding, removing and notifying observers of notifications.
  * Wraps the nsIObserverService interface.
  *
  * @version 0.2
  */
--- a/services/common/rest.js
+++ b/services/common/rest.js
@@ -1,27 +1,33 @@
 /* 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/. */
 
+#ifndef MERGED_COMPARTMENT
+
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = [
   "RESTRequest",
   "RESTResponse",
   "TokenAuthenticatedRESTRequest"
 ];
 
+#endif
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-common/utils.js");
 
+XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils",
+                                  "resource://services-crypto/utils.js");
+
 const Prefs = new Preferences("services.common.rest.");
 
 /**
  * Single use HTTP requests to RESTish resources.
  *
  * @param uri
  *        URI for the request. This can be an nsIURI object or a string
  *        that can be used to create one. An exception will be thrown if
--- a/services/datareporting/DataReportingService.js
+++ b/services/datareporting/DataReportingService.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://services-common/observers.js");
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-common/utils.js");
 
 
 const ROOT_BRANCH = "datareporting.";
 const POLICY_BRANCH = ROOT_BRANCH + "policy.";
 const SESSIONS_BRANCH = ROOT_BRANCH + "sessions.";
 const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport.";
@@ -252,13 +251,15 @@ DataReportingService.prototype = Object.
                                                  this.sessionRecorder);
   },
 });
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]);
 
 #define MERGED_COMPARTMENT
 
+#include ../common/observers.js
+;
 #include policy.jsm
 ;
 #include sessions.jsm
 ;
 
--- a/services/healthreport/HealthReport.jsm
+++ b/services/healthreport/HealthReport.jsm
@@ -19,16 +19,18 @@ const {classes: Cc, interfaces: Ci, util
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
 // We concatenate the JSMs together to eliminate compartment overhead.
 // This is a giant hack until compartment overhead is no longer an
 // issue.
 #define MERGED_COMPARTMENT
 
+#include ../common/async.js
+;
 #include ../common/bagheeraclient.js
 ;
 #include ../metrics/Metrics.jsm
 ;
 #include healthreporter.jsm
 ;
 #include profile.jsm
 ;
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -9,19 +9,19 @@
 this.EXPORTED_SYMBOLS = ["HealthReporter"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
 Cu.import("resource://gre/modules/Metrics.jsm");
 Cu.import("resource://services-common/bagheeraclient.js");
+Cu.import("resource://services-common/async.js");
 #endif
 
-Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://gre/modules/commonjs/promise/core.js");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
--- a/services/sync/Weave.js
+++ b/services/sync/Weave.js
@@ -5,41 +5,98 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
+const SYNC_PREFS_BRANCH = "services.sync.";
+
+
+/**
+ * Sync's XPCOM service.
+ *
+ * It is named "Weave" for historical reasons.
+ *
+ * It's worth noting how Sync is lazily loaded. We register a timer that
+ * loads Sync a few seconds after app startup. This is so Sync does not
+ * adversely affect application start time.
+ *
+ * If Sync is not configured, no extra Sync code is loaded. If an
+ * external component (say the UI) needs to interact with Sync, it
+ * should do something like the following:
+ *
+ * // 1. Grab a handle to the Sync XPCOM service.
+ * let service = Cc["@mozilla.org/weave/service;1"]
+ *                 .getService(Components.interfaces.nsISupports)
+ *                 .wrappedJSObject;
+ *
+ * // 2. Check if the service has been initialized.
+ * if (service.ready) {
+ *   // You are free to interact with "Weave." objects.
+ *   return;
+ * }
+ *
+ * // 3. Install "ready" listener.
+ * Services.obs.addObserver(function onReady() {
+ *   Services.obs.removeObserver(onReady, "weave:service:ready");
+ *
+ *   // You are free to interact with "Weave." objects.
+ * }, "weave:service:ready", false);
+ *
+ * // 4. Trigger loading of Sync.
+ * service.ensureLoaded();
+ */
 function WeaveService() {
   this.wrappedJSObject = this;
+  this.ready = false;
 }
 WeaveService.prototype = {
   classID: Components.ID("{74b89fb0-f200-4ae8-a3ec-dd164117f6de}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
-  observe: function BSS__observe(subject, topic, data) {
+  ensureLoaded: function () {
+    Components.utils.import("resource://services-sync/main.js");
+
+    // Side-effect of accessing the service is that it is instantiated.
+    Weave.Service;
+  },
+
+  observe: function (subject, topic, data) {
     switch (topic) {
     case "app-startup":
       let os = Cc["@mozilla.org/observer-service;1"].
                getService(Ci.nsIObserverService);
       os.addObserver(this, "final-ui-startup", true);
       break;
 
     case "final-ui-startup":
       // Force Weave service to load if it hasn't triggered from overlays
       this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       this.timer.initWithCallback({
         notify: function() {
-          Cu.import("resource://services-sync/main.js");
-          if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED)
-            Weave.Service;
+          // We only load more if it looks like Sync is configured.
+          let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
+
+          if (!prefs.prefHasUserValue("username")) {
+            return;
+          }
+
+          // We have a username. So, do a more thorough check. This will
+          // import a number of modules and thus increase memory
+          // accordingly. We could potentially copy code performed by
+          // this check into this file if our above code is yielding too
+          // many false positives.
+          if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
+            this.ensureLoaded();
+          }
         }
       }, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
       break;
     }
   }
 };
 
 function AboutWeaveLog() {}
--- a/services/sync/modules/identity.js
+++ b/services/sync/modules/identity.js
@@ -3,21 +3,28 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["IdentityManager"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/util.js");
 
+// Lazy import to prevent unnecessary load on startup.
+for (let symbol of ["BulkKeyBundle", "SyncKeyBundle"]) {
+  XPCOMUtils.defineLazyModuleGetter(this, symbol,
+                                    "resource://services-sync/keys.js",
+                                    symbol);
+}
+
 /**
  * Manages identity and authentication for Sync.
  *
  * The following entities are managed:
  *
  *   account - The main Sync/services account. This is typically an email
  *     address.
  *   username - A normalized version of your account. This is what's
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -357,16 +357,24 @@ Sync11Service.prototype = {
       Svc.Obs.notify("weave:engine:start-tracking");
     }
 
     // Send an event now that Weave service is ready.  We don't do this
     // synchronously so that observers can import this module before
     // registering an observer.
     Utils.nextTick(function onNextTick() {
       this.status.ready = true;
+
+      // UI code uses the flag on the XPCOM service so it doesn't have
+      // to load a bunch of modules.
+      let xps = Cc["@mozilla.org/weave/service;1"]
+                  .getService(Ci.nsISupports)
+                  .wrappedJSObject;
+      xps.ready = true;
+
       Svc.Obs.notify("weave:service:ready");
     }.bind(this));
   },
 
   _checkSetup: function _checkSetup() {
     if (!this.enabled) {
       return this.status.service = STATUS_DISABLED;
     }
--- a/services/sync/tests/unit/test_service_startup.js
+++ b/services/sync/tests/unit/test_service_startup.js
@@ -25,17 +25,24 @@ function run_test() {
 
   _("Engines are registered.");
   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();
+
+  let xps = Cc["@mozilla.org/weave/service;1"]
+              .getService(Ci.nsISupports)
+              .wrappedJSObject;
+
   do_check_false(Service.status.ready);
+  do_check_false(xps.ready);
   Observers.add("weave:service:ready", function (subject, data) {
     do_check_true(Service.status.ready);
+    do_check_true(xps.ready);
 
     // Clean up.
     Svc.Prefs.resetBranch("");
     do_test_finished();
   });
 }