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: this.EXPORTED_SYMBOLS = ['PrefsEngine', 'PrefRec']; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: const WEAVE_SYNC_PREFS = "services.sync.prefs.sync."; michael@0: michael@0: Cu.import("resource://services-sync/engines.js"); michael@0: Cu.import("resource://services-sync/record.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-common/utils.js"); michael@0: Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); michael@0: Cu.import("resource://gre/modules/Preferences.jsm"); michael@0: michael@0: const PREFS_GUID = CommonUtils.encodeBase64URL(Services.appinfo.ID); michael@0: michael@0: this.PrefRec = function PrefRec(collection, id) { michael@0: CryptoWrapper.call(this, collection, id); michael@0: } michael@0: PrefRec.prototype = { michael@0: __proto__: CryptoWrapper.prototype, michael@0: _logName: "Sync.Record.Pref", michael@0: }; michael@0: michael@0: Utils.deferGetSet(PrefRec, "cleartext", ["value"]); michael@0: michael@0: michael@0: this.PrefsEngine = function PrefsEngine(service) { michael@0: SyncEngine.call(this, "Prefs", service); michael@0: } michael@0: PrefsEngine.prototype = { michael@0: __proto__: SyncEngine.prototype, michael@0: _storeObj: PrefStore, michael@0: _trackerObj: PrefTracker, michael@0: _recordObj: PrefRec, michael@0: version: 2, michael@0: michael@0: getChangedIDs: function getChangedIDs() { michael@0: // No need for a proper timestamp (no conflict resolution needed). michael@0: let changedIDs = {}; michael@0: if (this._tracker.modified) michael@0: changedIDs[PREFS_GUID] = 0; michael@0: return changedIDs; michael@0: }, michael@0: michael@0: _wipeClient: function _wipeClient() { michael@0: SyncEngine.prototype._wipeClient.call(this); michael@0: this.justWiped = true; michael@0: }, michael@0: michael@0: _reconcile: function _reconcile(item) { michael@0: // Apply the incoming item if we don't care about the local data michael@0: if (this.justWiped) { michael@0: this.justWiped = false; michael@0: return true; michael@0: } michael@0: return SyncEngine.prototype._reconcile.call(this, item); michael@0: } michael@0: }; michael@0: michael@0: michael@0: function PrefStore(name, engine) { michael@0: Store.call(this, name, engine); michael@0: Svc.Obs.add("profile-before-change", function () { michael@0: this.__prefs = null; michael@0: }, this); michael@0: } michael@0: PrefStore.prototype = { michael@0: __proto__: Store.prototype, michael@0: michael@0: __prefs: null, michael@0: get _prefs() { michael@0: if (!this.__prefs) michael@0: this.__prefs = new Preferences(); michael@0: return this.__prefs; michael@0: }, michael@0: michael@0: _getSyncPrefs: function _getSyncPrefs() { michael@0: let syncPrefs = Cc["@mozilla.org/preferences-service;1"] michael@0: .getService(Ci.nsIPrefService) michael@0: .getBranch(WEAVE_SYNC_PREFS) michael@0: .getChildList("", {}); michael@0: // Also sync preferences that determine which prefs get synced. michael@0: return syncPrefs.concat( michael@0: syncPrefs.map(function (pref) { return WEAVE_SYNC_PREFS + pref; })); michael@0: }, michael@0: michael@0: _isSynced: function _isSyncedPref(pref) { michael@0: return (pref.indexOf(WEAVE_SYNC_PREFS) == 0) michael@0: || this._prefs.get(WEAVE_SYNC_PREFS + pref, false); michael@0: }, michael@0: michael@0: _getAllPrefs: function () { michael@0: let values = {}; michael@0: for each (let pref in this._getSyncPrefs()) { michael@0: if (this._isSynced(pref)) { michael@0: // Missing prefs get the null value. michael@0: values[pref] = this._prefs.get(pref, null); michael@0: } michael@0: } michael@0: return values; michael@0: }, michael@0: michael@0: _setAllPrefs: function PrefStore__setAllPrefs(values) { michael@0: let enabledPref = "lightweightThemes.isThemeSelected"; michael@0: let enabledBefore = this._prefs.get(enabledPref, false); michael@0: let prevTheme = LightweightThemeManager.currentTheme; michael@0: michael@0: for (let [pref, value] in Iterator(values)) { michael@0: if (!this._isSynced(pref)) michael@0: continue; michael@0: michael@0: // Pref has gone missing, best we can do is reset it. michael@0: if (value == null) { michael@0: this._prefs.reset(pref); michael@0: continue; michael@0: } michael@0: michael@0: try { michael@0: this._prefs.set(pref, value); michael@0: } catch(ex) { michael@0: this._log.trace("Failed to set pref: " + pref + ": " + ex); michael@0: } michael@0: } michael@0: michael@0: // Notify the lightweight theme manager of all the new values michael@0: let enabledNow = this._prefs.get(enabledPref, false); michael@0: if (enabledBefore && !enabledNow) { michael@0: LightweightThemeManager.currentTheme = null; michael@0: } else if (enabledNow && LightweightThemeManager.usedThemes[0] != prevTheme) { michael@0: LightweightThemeManager.currentTheme = null; michael@0: LightweightThemeManager.currentTheme = LightweightThemeManager.usedThemes[0]; michael@0: } michael@0: }, michael@0: michael@0: getAllIDs: function PrefStore_getAllIDs() { michael@0: /* We store all prefs in just one WBO, with just one GUID */ michael@0: let allprefs = {}; michael@0: allprefs[PREFS_GUID] = true; michael@0: return allprefs; michael@0: }, michael@0: michael@0: changeItemID: function PrefStore_changeItemID(oldID, newID) { michael@0: this._log.trace("PrefStore GUID is constant!"); michael@0: }, michael@0: michael@0: itemExists: function FormStore_itemExists(id) { michael@0: return (id === PREFS_GUID); michael@0: }, michael@0: michael@0: createRecord: function createRecord(id, collection) { michael@0: let record = new PrefRec(collection, id); michael@0: michael@0: if (id == PREFS_GUID) { michael@0: record.value = this._getAllPrefs(); michael@0: } else { michael@0: record.deleted = true; michael@0: } michael@0: michael@0: return record; michael@0: }, michael@0: michael@0: create: function PrefStore_create(record) { michael@0: this._log.trace("Ignoring create request"); michael@0: }, michael@0: michael@0: remove: function PrefStore_remove(record) { michael@0: this._log.trace("Ignoring remove request"); michael@0: }, michael@0: michael@0: update: function PrefStore_update(record) { michael@0: // Silently ignore pref updates that are for other apps. michael@0: if (record.id != PREFS_GUID) michael@0: return; michael@0: michael@0: this._log.trace("Received pref updates, applying..."); michael@0: this._setAllPrefs(record.value); michael@0: }, michael@0: michael@0: wipe: function PrefStore_wipe() { michael@0: this._log.trace("Ignoring wipe request"); michael@0: } michael@0: }; michael@0: michael@0: function PrefTracker(name, engine) { michael@0: Tracker.call(this, name, engine); michael@0: Svc.Obs.add("profile-before-change", this); michael@0: Svc.Obs.add("weave:engine:start-tracking", this); michael@0: Svc.Obs.add("weave:engine:stop-tracking", this); michael@0: } michael@0: PrefTracker.prototype = { michael@0: __proto__: Tracker.prototype, michael@0: michael@0: get modified() { michael@0: return Svc.Prefs.get("engine.prefs.modified", false); michael@0: }, michael@0: set modified(value) { michael@0: Svc.Prefs.set("engine.prefs.modified", value); michael@0: }, michael@0: michael@0: loadChangedIDs: function loadChangedIDs() { michael@0: // Don't read changed IDs from disk at start up. michael@0: }, michael@0: michael@0: clearChangedIDs: function clearChangedIDs() { michael@0: this.modified = false; michael@0: }, michael@0: michael@0: __prefs: null, michael@0: get _prefs() { michael@0: if (!this.__prefs) { michael@0: this.__prefs = new Preferences(); michael@0: } michael@0: return this.__prefs; michael@0: }, michael@0: michael@0: startTracking: function () { michael@0: Services.prefs.addObserver("", this, false); michael@0: }, michael@0: michael@0: stopTracking: function () { michael@0: this.__prefs = null; michael@0: Services.prefs.removeObserver("", this); michael@0: }, michael@0: michael@0: observe: function (subject, topic, data) { michael@0: Tracker.prototype.observe.call(this, subject, topic, data); michael@0: michael@0: switch (topic) { michael@0: case "profile-before-change": michael@0: this.stopTracking(); michael@0: break; michael@0: case "nsPref:changed": michael@0: // Trigger a sync for MULTI-DEVICE for a change that determines michael@0: // which prefs are synced or a regular pref change. michael@0: if (data.indexOf(WEAVE_SYNC_PREFS) == 0 || michael@0: this._prefs.get(WEAVE_SYNC_PREFS + data, false)) { michael@0: this.score += SCORE_INCREMENT_XLARGE; michael@0: this.modified = true; michael@0: this._log.trace("Preference " + data + " changed"); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: };