michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/FileUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Promise.jsm"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: michael@0: const SYNC_PREFS_BRANCH = "services.sync."; michael@0: michael@0: michael@0: /** michael@0: * Sync's XPCOM service. michael@0: * michael@0: * It is named "Weave" for historical reasons. michael@0: * michael@0: * It's worth noting how Sync is lazily loaded. We register a timer that michael@0: * loads Sync a few seconds after app startup. This is so Sync does not michael@0: * adversely affect application start time. michael@0: * michael@0: * If Sync is not configured, no extra Sync code is loaded. If an michael@0: * external component (say the UI) needs to interact with Sync, it michael@0: * should use the promise-base function whenLoaded() - something like the michael@0: * following: michael@0: * michael@0: * // 1. Grab a handle to the Sync XPCOM service. michael@0: * let service = Cc["@mozilla.org/weave/service;1"] michael@0: * .getService(Components.interfaces.nsISupports) michael@0: * .wrappedJSObject; michael@0: * michael@0: * // 2. Use the .then method of the promise. michael@0: * service.whenLoaded().then(() => { michael@0: * // You are free to interact with "Weave." objects. michael@0: * return; michael@0: * }); michael@0: * michael@0: * And that's it! However, if you really want to avoid promises and do it michael@0: * old-school, then michael@0: * michael@0: * // 1. Get a reference to the service as done in (1) above. michael@0: * michael@0: * // 2. Check if the service has been initialized. michael@0: * if (service.ready) { michael@0: * // You are free to interact with "Weave." objects. michael@0: * return; michael@0: * } michael@0: * michael@0: * // 3. Install "ready" listener. michael@0: * Services.obs.addObserver(function onReady() { michael@0: * Services.obs.removeObserver(onReady, "weave:service:ready"); michael@0: * michael@0: * // You are free to interact with "Weave." objects. michael@0: * }, "weave:service:ready", false); michael@0: * michael@0: * // 4. Trigger loading of Sync. michael@0: * service.ensureLoaded(); michael@0: */ michael@0: function WeaveService() { michael@0: this.wrappedJSObject = this; michael@0: this.ready = false; michael@0: } michael@0: WeaveService.prototype = { michael@0: classID: Components.ID("{74b89fb0-f200-4ae8-a3ec-dd164117f6de}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, michael@0: Ci.nsISupportsWeakReference]), michael@0: michael@0: ensureLoaded: function () { michael@0: Components.utils.import("resource://services-sync/main.js"); michael@0: michael@0: // Side-effect of accessing the service is that it is instantiated. michael@0: Weave.Service; michael@0: }, michael@0: michael@0: whenLoaded: function() { michael@0: if (this.ready) { michael@0: return Promise.resolve(); michael@0: } michael@0: let deferred = Promise.defer(); michael@0: michael@0: Services.obs.addObserver(function onReady() { michael@0: Services.obs.removeObserver(onReady, "weave:service:ready"); michael@0: deferred.resolve(); michael@0: }, "weave:service:ready", false); michael@0: this.ensureLoaded(); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: /** michael@0: * Whether Firefox Accounts is enabled. michael@0: * michael@0: * @return bool michael@0: */ michael@0: get fxAccountsEnabled() { michael@0: try { michael@0: // Old sync guarantees '@' will never appear in the username while FxA michael@0: // uses the FxA email address - so '@' is the flag we use. michael@0: let username = Services.prefs.getCharPref(SYNC_PREFS_BRANCH + "username"); michael@0: return !username || username.contains('@'); michael@0: } catch (_) { michael@0: return true; // No username == only allow FxA to be configured. michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Returns whether the password engine is allowed. We explicitly disallow michael@0: * the password engine when a master password is used to ensure those can't michael@0: * be accessed without the master key. michael@0: */ michael@0: get allowPasswordsEngine() { michael@0: // This doesn't apply to old-style sync, it's only an issue for FxA. michael@0: return !this.fxAccountsEnabled || !Utils.mpEnabled(); michael@0: }, michael@0: michael@0: /** michael@0: * Whether Sync appears to be enabled. michael@0: * michael@0: * This returns true if all the Sync preferences for storing account michael@0: * and server configuration are populated. michael@0: * michael@0: * It does *not* perform a robust check to see if the client is working. michael@0: * For that, you'll want to check Weave.Status.checkSetup(). michael@0: */ michael@0: get enabled() { michael@0: let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH); michael@0: return prefs.prefHasUserValue("username") && michael@0: prefs.prefHasUserValue("clusterURL"); michael@0: }, michael@0: michael@0: observe: function (subject, topic, data) { michael@0: switch (topic) { michael@0: case "app-startup": michael@0: let os = Cc["@mozilla.org/observer-service;1"]. michael@0: getService(Ci.nsIObserverService); michael@0: os.addObserver(this, "final-ui-startup", true); michael@0: break; michael@0: michael@0: case "final-ui-startup": michael@0: // Force Weave service to load if it hasn't triggered from overlays michael@0: this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); michael@0: this.timer.initWithCallback({ michael@0: notify: function() { michael@0: // We only load more if it looks like Sync is configured. michael@0: let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH); michael@0: if (!prefs.prefHasUserValue("username")) { michael@0: return; michael@0: } michael@0: michael@0: // We have a username. So, do a more thorough check. This will michael@0: // import a number of modules and thus increase memory michael@0: // accordingly. We could potentially copy code performed by michael@0: // this check into this file if our above code is yielding too michael@0: // many false positives. michael@0: Components.utils.import("resource://services-sync/main.js"); michael@0: if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) { michael@0: this.ensureLoaded(); michael@0: } michael@0: }.bind(this) michael@0: }, 10000, Ci.nsITimer.TYPE_ONE_SHOT); michael@0: break; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: function AboutWeaveLog() {} michael@0: AboutWeaveLog.prototype = { michael@0: classID: Components.ID("{d28f8a0b-95da-48f4-b712-caf37097be41}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule, michael@0: Ci.nsISupportsWeakReference]), michael@0: michael@0: getURIFlags: function(aURI) { michael@0: return 0; michael@0: }, michael@0: michael@0: newChannel: function(aURI) { michael@0: let dir = FileUtils.getDir("ProfD", ["weave", "logs"], true); michael@0: let uri = Services.io.newFileURI(dir); michael@0: let channel = Services.io.newChannelFromURI(uri); michael@0: channel.originalURI = aURI; michael@0: michael@0: // Ensure that the about page has the same privileges as a regular directory michael@0: // view. That way links to files can be opened. michael@0: let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] michael@0: .getService(Ci.nsIScriptSecurityManager); michael@0: let principal = ssm.getNoAppCodebasePrincipal(uri); michael@0: channel.owner = principal; michael@0: return channel; michael@0: } michael@0: }; michael@0: michael@0: const components = [WeaveService, AboutWeaveLog]; michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);