summaryrefslogtreecommitdiffstats
path: root/services/sync/Weave.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/Weave.js')
-rw-r--r--services/sync/Weave.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/services/sync/Weave.js b/services/sync/Weave.js
new file mode 100644
index 000000000..4d79144e3
--- /dev/null
+++ b/services/sync/Weave.js
@@ -0,0 +1,200 @@
+/* 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 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");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://services-sync/util.js");
+
+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 use the promise-base function whenLoaded() - 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. Use the .then method of the promise.
+ * service.whenLoaded().then(() => {
+ * // You are free to interact with "Weave." objects.
+ * return;
+ * });
+ *
+ * And that's it! However, if you really want to avoid promises and do it
+ * old-school, then
+ *
+ * // 1. Get a reference to the service as done in (1) above.
+ *
+ * // 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]),
+
+ ensureLoaded: function () {
+ // If we are loaded and not using FxA, load the migration module.
+ if (!this.fxAccountsEnabled) {
+ Cu.import("resource://services-sync/FxaMigrator.jsm");
+ }
+
+ Components.utils.import("resource://services-sync/main.js");
+
+ // Side-effect of accessing the service is that it is instantiated.
+ Weave.Service;
+ },
+
+ whenLoaded: function() {
+ if (this.ready) {
+ return Promise.resolve();
+ }
+ let deferred = Promise.defer();
+
+ Services.obs.addObserver(function onReady() {
+ Services.obs.removeObserver(onReady, "weave:service:ready");
+ deferred.resolve();
+ }, "weave:service:ready", false);
+ this.ensureLoaded();
+ return deferred.promise;
+ },
+
+ /**
+ * Whether Firefox Accounts is enabled.
+ *
+ * @return bool
+ */
+ get fxAccountsEnabled() {
+ try {
+ // Old sync guarantees '@' will never appear in the username while FxA
+ // uses the FxA email address - so '@' is the flag we use.
+ let username = Services.prefs.getCharPref(SYNC_PREFS_BRANCH + "username");
+ return !username || username.includes('@');
+ } catch (_) {
+ return true; // No username == only allow FxA to be configured.
+ }
+ },
+
+ /**
+ * Whether Sync appears to be enabled.
+ *
+ * This returns true if all the Sync preferences for storing account
+ * and server configuration are populated.
+ *
+ * It does *not* perform a robust check to see if the client is working.
+ * For that, you'll want to check Weave.Status.checkSetup().
+ */
+ get enabled() {
+ let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
+ return prefs.prefHasUserValue("username");
+ },
+
+ 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() {
+ let isConfigured = false;
+ // We only load more if it looks like Sync is configured.
+ let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
+ if (prefs.prefHasUserValue("username")) {
+ // 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.
+ Components.utils.import("resource://services-sync/main.js");
+ isConfigured = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED;
+ }
+ let getHistogramById = Services.telemetry.getHistogramById;
+ getHistogramById("WEAVE_CONFIGURED").add(isConfigured);
+ if (isConfigured) {
+ getHistogramById("WEAVE_CONFIGURED_MASTER_PASSWORD").add(Utils.mpEnabled());
+ this.ensureLoaded();
+ }
+ }.bind(this)
+ }, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
+ break;
+ }
+ }
+};
+
+function AboutWeaveLog() {}
+AboutWeaveLog.prototype = {
+ classID: Components.ID("{d28f8a0b-95da-48f4-b712-caf37097be41}"),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule,
+ Ci.nsISupportsWeakReference]),
+
+ getURIFlags: function(aURI) {
+ return 0;
+ },
+
+ newChannel: function(aURI, aLoadInfo) {
+ let dir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
+ let uri = Services.io.newFileURI(dir);
+ let channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
+
+ channel.originalURI = aURI;
+
+ // Ensure that the about page has the same privileges as a regular directory
+ // view. That way links to files can be opened. make sure we use the correct
+ // origin attributes when creating the principal for accessing the
+ // about:sync-log data.
+ let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ let principal = ssm.createCodebasePrincipal(uri, aLoadInfo.originAttributes);
+
+ channel.owner = principal;
+ return channel;
+ }
+};
+
+const components = [WeaveService, AboutWeaveLog];
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);