Bug 836120 - Reduce memory overhead of Sync when it's not configured; r=rnewman

If Sync is (likely) not configured, the only loaded JS is for the XPCOM
service itself.

The UI code is now smart enough to initialize the Sync service if it
isn't yet loaded. This addresses bug 825728.
This commit is contained in:
Gregory Szorc 2013-01-30 07:05:12 -08:00
parent 961b3eb72e
commit 3a3280675a
7 changed files with 161 additions and 6 deletions

View File

@ -23,7 +23,10 @@ let gSyncUI = {
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;
}

View File

@ -33,6 +33,37 @@ let gSyncPane = {
},
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",

View File

@ -34,6 +34,36 @@ let gSyncPane = {
},
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",

View File

@ -14,9 +14,28 @@ let WeaveGlue = {
_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");

View File

@ -10,8 +10,46 @@ 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}"),
@ -19,7 +57,14 @@ WeaveService.prototype = {
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"].
@ -32,9 +77,21 @@ WeaveService.prototype = {
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;

View File

@ -362,6 +362,14 @@ Sync11Service.prototype = {
// 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));
},

View File

@ -30,9 +30,16 @@ function run_test() {
_("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("");