prefs.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. this.EXPORTED_SYMBOLS = ['PrefsEngine', 'PrefRec'];
  5. var Cc = Components.classes;
  6. var Ci = Components.interfaces;
  7. var Cu = Components.utils;
  8. const SYNC_PREFS_PREFIX = "services.sync.prefs.sync.";
  9. Cu.import("resource://services-sync/engines.js");
  10. Cu.import("resource://services-sync/record.js");
  11. Cu.import("resource://services-sync/util.js");
  12. Cu.import("resource://services-sync/constants.js");
  13. Cu.import("resource://services-common/utils.js");
  14. Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
  15. Cu.import("resource://gre/modules/Preferences.jsm");
  16. const PREFS_GUID = CommonUtils.encodeBase64URL(Services.appinfo.ID);
  17. this.PrefRec = function PrefRec(collection, id) {
  18. CryptoWrapper.call(this, collection, id);
  19. }
  20. PrefRec.prototype = {
  21. __proto__: CryptoWrapper.prototype,
  22. _logName: "Sync.Record.Pref",
  23. };
  24. Utils.deferGetSet(PrefRec, "cleartext", ["value"]);
  25. this.PrefsEngine = function PrefsEngine(service) {
  26. SyncEngine.call(this, "Prefs", service);
  27. }
  28. PrefsEngine.prototype = {
  29. __proto__: SyncEngine.prototype,
  30. _storeObj: PrefStore,
  31. _trackerObj: PrefTracker,
  32. _recordObj: PrefRec,
  33. version: 2,
  34. syncPriority: 1,
  35. getChangedIDs: function () {
  36. // No need for a proper timestamp (no conflict resolution needed).
  37. let changedIDs = {};
  38. if (this._tracker.modified)
  39. changedIDs[PREFS_GUID] = 0;
  40. return changedIDs;
  41. },
  42. _wipeClient: function () {
  43. SyncEngine.prototype._wipeClient.call(this);
  44. this.justWiped = true;
  45. },
  46. _reconcile: function (item) {
  47. // Apply the incoming item if we don't care about the local data
  48. if (this.justWiped) {
  49. this.justWiped = false;
  50. return true;
  51. }
  52. return SyncEngine.prototype._reconcile.call(this, item);
  53. }
  54. };
  55. function PrefStore(name, engine) {
  56. Store.call(this, name, engine);
  57. Svc.Obs.add("profile-before-change", function () {
  58. this.__prefs = null;
  59. }, this);
  60. }
  61. PrefStore.prototype = {
  62. __proto__: Store.prototype,
  63. __prefs: null,
  64. get _prefs() {
  65. if (!this.__prefs) {
  66. this.__prefs = new Preferences();
  67. }
  68. return this.__prefs;
  69. },
  70. _getSyncPrefs: function () {
  71. let syncPrefs = Cc["@mozilla.org/preferences-service;1"]
  72. .getService(Ci.nsIPrefService)
  73. .getBranch(SYNC_PREFS_PREFIX)
  74. .getChildList("", {});
  75. // Also sync preferences that determine which prefs get synced.
  76. let controlPrefs = syncPrefs.map(pref => SYNC_PREFS_PREFIX + pref);
  77. return controlPrefs.concat(syncPrefs);
  78. },
  79. _isSynced: function (pref) {
  80. return pref.startsWith(SYNC_PREFS_PREFIX) ||
  81. this._prefs.get(SYNC_PREFS_PREFIX + pref, false);
  82. },
  83. _getAllPrefs: function () {
  84. let values = {};
  85. for each (let pref in this._getSyncPrefs()) {
  86. if (this._isSynced(pref)) {
  87. // Missing prefs get the null value.
  88. values[pref] = this._prefs.get(pref, null);
  89. }
  90. }
  91. return values;
  92. },
  93. _setAllPrefs: function (values) {
  94. let enabledPref = "lightweightThemes.isThemeSelected";
  95. let enabledBefore = this._prefs.get(enabledPref, false);
  96. let prevTheme = LightweightThemeManager.currentTheme;
  97. // Update 'services.sync.prefs.sync.foo.pref' before 'foo.pref', otherwise
  98. // _isSynced returns false when 'foo.pref' doesn't exist (e.g., on a new device).
  99. let prefs = Object.keys(values).sort(a => -a.indexOf(SYNC_PREFS_PREFIX));
  100. for (let pref of prefs) {
  101. if (!this._isSynced(pref)) {
  102. continue;
  103. }
  104. let value = values[pref];
  105. // Pref has gone missing. The best we can do is reset it.
  106. if (value == null) {
  107. this._prefs.reset(pref);
  108. continue;
  109. }
  110. try {
  111. this._prefs.set(pref, value);
  112. } catch(ex) {
  113. this._log.trace("Failed to set pref: " + pref + ": " + ex);
  114. }
  115. }
  116. // Notify the lightweight theme manager of all the new values
  117. let enabledNow = this._prefs.get(enabledPref, false);
  118. if (enabledBefore && !enabledNow) {
  119. LightweightThemeManager.currentTheme = null;
  120. } else if (enabledNow && LightweightThemeManager.usedThemes[0] != prevTheme) {
  121. LightweightThemeManager.currentTheme = null;
  122. LightweightThemeManager.currentTheme = LightweightThemeManager.usedThemes[0];
  123. }
  124. },
  125. getAllIDs: function () {
  126. /* We store all prefs in just one WBO, with just one GUID */
  127. let allprefs = {};
  128. allprefs[PREFS_GUID] = true;
  129. return allprefs;
  130. },
  131. changeItemID: function (oldID, newID) {
  132. this._log.trace("PrefStore GUID is constant!");
  133. },
  134. itemExists: function (id) {
  135. return (id === PREFS_GUID);
  136. },
  137. createRecord: function (id, collection) {
  138. let record = new PrefRec(collection, id);
  139. if (id == PREFS_GUID) {
  140. record.value = this._getAllPrefs();
  141. } else {
  142. record.deleted = true;
  143. }
  144. return record;
  145. },
  146. create: function (record) {
  147. this._log.trace("Ignoring create request");
  148. },
  149. remove: function (record) {
  150. this._log.trace("Ignoring remove request");
  151. },
  152. update: function (record) {
  153. // Silently ignore pref updates that are for other apps.
  154. if (record.id != PREFS_GUID)
  155. return;
  156. this._log.trace("Received pref updates, applying...");
  157. this._setAllPrefs(record.value);
  158. },
  159. wipe: function () {
  160. this._log.trace("Ignoring wipe request");
  161. }
  162. };
  163. function PrefTracker(name, engine) {
  164. Tracker.call(this, name, engine);
  165. Svc.Obs.add("profile-before-change", this);
  166. Svc.Obs.add("weave:engine:start-tracking", this);
  167. Svc.Obs.add("weave:engine:stop-tracking", this);
  168. }
  169. PrefTracker.prototype = {
  170. __proto__: Tracker.prototype,
  171. get modified() {
  172. return Svc.Prefs.get("engine.prefs.modified", false);
  173. },
  174. set modified(value) {
  175. Svc.Prefs.set("engine.prefs.modified", value);
  176. },
  177. loadChangedIDs: function loadChangedIDs() {
  178. // Don't read changed IDs from disk at start up.
  179. },
  180. clearChangedIDs: function clearChangedIDs() {
  181. this.modified = false;
  182. },
  183. __prefs: null,
  184. get _prefs() {
  185. if (!this.__prefs) {
  186. this.__prefs = new Preferences();
  187. }
  188. return this.__prefs;
  189. },
  190. startTracking: function () {
  191. Services.prefs.addObserver("", this, false);
  192. },
  193. stopTracking: function () {
  194. this.__prefs = null;
  195. Services.prefs.removeObserver("", this);
  196. },
  197. observe: function (subject, topic, data) {
  198. Tracker.prototype.observe.call(this, subject, topic, data);
  199. switch (topic) {
  200. case "profile-before-change":
  201. this.stopTracking();
  202. break;
  203. case "nsPref:changed":
  204. // Trigger a sync for MULTI-DEVICE for a change that determines
  205. // which prefs are synced or a regular pref change.
  206. if (data.indexOf(SYNC_PREFS_PREFIX) == 0 ||
  207. this._prefs.get(SYNC_PREFS_PREFIX + data, false)) {
  208. this.score += SCORE_INCREMENT_XLARGE;
  209. this.modified = true;
  210. this._log.trace("Preference " + data + " changed");
  211. }
  212. break;
  213. }
  214. }
  215. };