1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/Weave.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,199 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const Cc = Components.classes; 1.9 +const Ci = Components.interfaces; 1.10 +const Cu = Components.utils; 1.11 + 1.12 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.13 +Cu.import("resource://gre/modules/Services.jsm"); 1.14 +Cu.import("resource://gre/modules/FileUtils.jsm"); 1.15 +Cu.import("resource://gre/modules/Promise.jsm"); 1.16 +Cu.import("resource://services-sync/util.js"); 1.17 + 1.18 +const SYNC_PREFS_BRANCH = "services.sync."; 1.19 + 1.20 + 1.21 +/** 1.22 + * Sync's XPCOM service. 1.23 + * 1.24 + * It is named "Weave" for historical reasons. 1.25 + * 1.26 + * It's worth noting how Sync is lazily loaded. We register a timer that 1.27 + * loads Sync a few seconds after app startup. This is so Sync does not 1.28 + * adversely affect application start time. 1.29 + * 1.30 + * If Sync is not configured, no extra Sync code is loaded. If an 1.31 + * external component (say the UI) needs to interact with Sync, it 1.32 + * should use the promise-base function whenLoaded() - something like the 1.33 + * following: 1.34 + * 1.35 + * // 1. Grab a handle to the Sync XPCOM service. 1.36 + * let service = Cc["@mozilla.org/weave/service;1"] 1.37 + * .getService(Components.interfaces.nsISupports) 1.38 + * .wrappedJSObject; 1.39 + * 1.40 + * // 2. Use the .then method of the promise. 1.41 + * service.whenLoaded().then(() => { 1.42 + * // You are free to interact with "Weave." objects. 1.43 + * return; 1.44 + * }); 1.45 + * 1.46 + * And that's it! However, if you really want to avoid promises and do it 1.47 + * old-school, then 1.48 + * 1.49 + * // 1. Get a reference to the service as done in (1) above. 1.50 + * 1.51 + * // 2. Check if the service has been initialized. 1.52 + * if (service.ready) { 1.53 + * // You are free to interact with "Weave." objects. 1.54 + * return; 1.55 + * } 1.56 + * 1.57 + * // 3. Install "ready" listener. 1.58 + * Services.obs.addObserver(function onReady() { 1.59 + * Services.obs.removeObserver(onReady, "weave:service:ready"); 1.60 + * 1.61 + * // You are free to interact with "Weave." objects. 1.62 + * }, "weave:service:ready", false); 1.63 + * 1.64 + * // 4. Trigger loading of Sync. 1.65 + * service.ensureLoaded(); 1.66 + */ 1.67 +function WeaveService() { 1.68 + this.wrappedJSObject = this; 1.69 + this.ready = false; 1.70 +} 1.71 +WeaveService.prototype = { 1.72 + classID: Components.ID("{74b89fb0-f200-4ae8-a3ec-dd164117f6de}"), 1.73 + 1.74 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, 1.75 + Ci.nsISupportsWeakReference]), 1.76 + 1.77 + ensureLoaded: function () { 1.78 + Components.utils.import("resource://services-sync/main.js"); 1.79 + 1.80 + // Side-effect of accessing the service is that it is instantiated. 1.81 + Weave.Service; 1.82 + }, 1.83 + 1.84 + whenLoaded: function() { 1.85 + if (this.ready) { 1.86 + return Promise.resolve(); 1.87 + } 1.88 + let deferred = Promise.defer(); 1.89 + 1.90 + Services.obs.addObserver(function onReady() { 1.91 + Services.obs.removeObserver(onReady, "weave:service:ready"); 1.92 + deferred.resolve(); 1.93 + }, "weave:service:ready", false); 1.94 + this.ensureLoaded(); 1.95 + return deferred.promise; 1.96 + }, 1.97 + 1.98 + /** 1.99 + * Whether Firefox Accounts is enabled. 1.100 + * 1.101 + * @return bool 1.102 + */ 1.103 + get fxAccountsEnabled() { 1.104 + try { 1.105 + // Old sync guarantees '@' will never appear in the username while FxA 1.106 + // uses the FxA email address - so '@' is the flag we use. 1.107 + let username = Services.prefs.getCharPref(SYNC_PREFS_BRANCH + "username"); 1.108 + return !username || username.contains('@'); 1.109 + } catch (_) { 1.110 + return true; // No username == only allow FxA to be configured. 1.111 + } 1.112 + }, 1.113 + 1.114 + /** 1.115 + * Returns whether the password engine is allowed. We explicitly disallow 1.116 + * the password engine when a master password is used to ensure those can't 1.117 + * be accessed without the master key. 1.118 + */ 1.119 + get allowPasswordsEngine() { 1.120 + // This doesn't apply to old-style sync, it's only an issue for FxA. 1.121 + return !this.fxAccountsEnabled || !Utils.mpEnabled(); 1.122 + }, 1.123 + 1.124 + /** 1.125 + * Whether Sync appears to be enabled. 1.126 + * 1.127 + * This returns true if all the Sync preferences for storing account 1.128 + * and server configuration are populated. 1.129 + * 1.130 + * It does *not* perform a robust check to see if the client is working. 1.131 + * For that, you'll want to check Weave.Status.checkSetup(). 1.132 + */ 1.133 + get enabled() { 1.134 + let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH); 1.135 + return prefs.prefHasUserValue("username") && 1.136 + prefs.prefHasUserValue("clusterURL"); 1.137 + }, 1.138 + 1.139 + observe: function (subject, topic, data) { 1.140 + switch (topic) { 1.141 + case "app-startup": 1.142 + let os = Cc["@mozilla.org/observer-service;1"]. 1.143 + getService(Ci.nsIObserverService); 1.144 + os.addObserver(this, "final-ui-startup", true); 1.145 + break; 1.146 + 1.147 + case "final-ui-startup": 1.148 + // Force Weave service to load if it hasn't triggered from overlays 1.149 + this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.150 + this.timer.initWithCallback({ 1.151 + notify: function() { 1.152 + // We only load more if it looks like Sync is configured. 1.153 + let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH); 1.154 + if (!prefs.prefHasUserValue("username")) { 1.155 + return; 1.156 + } 1.157 + 1.158 + // We have a username. So, do a more thorough check. This will 1.159 + // import a number of modules and thus increase memory 1.160 + // accordingly. We could potentially copy code performed by 1.161 + // this check into this file if our above code is yielding too 1.162 + // many false positives. 1.163 + Components.utils.import("resource://services-sync/main.js"); 1.164 + if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) { 1.165 + this.ensureLoaded(); 1.166 + } 1.167 + }.bind(this) 1.168 + }, 10000, Ci.nsITimer.TYPE_ONE_SHOT); 1.169 + break; 1.170 + } 1.171 + } 1.172 +}; 1.173 + 1.174 +function AboutWeaveLog() {} 1.175 +AboutWeaveLog.prototype = { 1.176 + classID: Components.ID("{d28f8a0b-95da-48f4-b712-caf37097be41}"), 1.177 + 1.178 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule, 1.179 + Ci.nsISupportsWeakReference]), 1.180 + 1.181 + getURIFlags: function(aURI) { 1.182 + return 0; 1.183 + }, 1.184 + 1.185 + newChannel: function(aURI) { 1.186 + let dir = FileUtils.getDir("ProfD", ["weave", "logs"], true); 1.187 + let uri = Services.io.newFileURI(dir); 1.188 + let channel = Services.io.newChannelFromURI(uri); 1.189 + channel.originalURI = aURI; 1.190 + 1.191 + // Ensure that the about page has the same privileges as a regular directory 1.192 + // view. That way links to files can be opened. 1.193 + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] 1.194 + .getService(Ci.nsIScriptSecurityManager); 1.195 + let principal = ssm.getNoAppCodebasePrincipal(uri); 1.196 + channel.owner = principal; 1.197 + return channel; 1.198 + } 1.199 +}; 1.200 + 1.201 +const components = [WeaveService, AboutWeaveLog]; 1.202 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);