|
@@ -42,6 +42,8 @@ const Ci = Components.interfaces;
|
|
|
const Cr = Components.results;
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
+
|
|
|
function Preferences(prefBranch) {
|
|
|
if (prefBranch)
|
|
|
this._prefBranch = prefBranch;
|
|
@@ -152,12 +154,102 @@ Preferences.prototype = {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * A cache of preference branch observers.
|
|
|
+ *
|
|
|
+ * We use this to remove observers when a caller calls |remove|.
|
|
|
+ *
|
|
|
+ * XXX This might result in reference cycles, causing memory leaks,
|
|
|
+ * if we hold a reference to an observer that holds a reference to us.
|
|
|
+ * Could we fix that by making this an independent top-level object
|
|
|
+ * rather than a property of the Preferences prototype?
|
|
|
+ *
|
|
|
+ * Note: all Preferences instances share this object, since all of them
|
|
|
+ * have the same prototype. This is intentional, because we want callers
|
|
|
+ * to be able to remove an observer using a different Preferences object
|
|
|
+ * than the one with which they added it. But it means we have to index
|
|
|
+ * the observers in this object by their complete pref branch, not just
|
|
|
+ * the branch relative to the root branch of any given Preferences object.
|
|
|
+ */
|
|
|
+ _observers: [],
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Observe a pref branch. The callback can be a function, a method
|
|
|
+ * (when thisObject is provided), or any object that implements nsIObserver.
|
|
|
+ * The pref branch can be any string and is appended to the root branch
|
|
|
+ * for the Preferences instance on which this method is called.
|
|
|
+ *
|
|
|
+ * For example, if the Preferences instance has root branch "foo.",
|
|
|
+ * and this method is called with branch "bar.", then the callback
|
|
|
+ * will observe the complete branch "foo.bar.". If the Preferences instance
|
|
|
+ * has the root branch "", and this method is called with branch "",
|
|
|
+ * then the callback will observe changes to all preferences.
|
|
|
+ *
|
|
|
+ * @param branch {String} [optional]
|
|
|
+ * the branch to observe
|
|
|
+ *
|
|
|
+ * @param callback {Object}
|
|
|
+ * the callback to call when a pref on the branch changes;
|
|
|
+ * a Function or an Object that implements nsIObserver
|
|
|
+ *
|
|
|
+ * @param thisObject {Object} [optional]
|
|
|
+ * the object to use as |this| when calling a Function callback;
|
|
|
+ * allows the callback to behave like a method when observing changes
|
|
|
+ *
|
|
|
+ * @returns the wrapped observer
|
|
|
+ */
|
|
|
+ observe: function(branch, callback, thisObject) {
|
|
|
+ let fullBranch = this._prefBranch + (branch || "");
|
|
|
+
|
|
|
+ let observer = new PrefObserver(fullBranch, callback, thisObject);
|
|
|
+ Preferences._prefSvc.addObserver(fullBranch, observer, true);
|
|
|
+ Preferences._observers.push(observer);
|
|
|
+
|
|
|
+ return observer;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Stop observing a pref branch. This method must be called with the same
|
|
|
+ * branch, callback, and thisObject with which the observer was originally
|
|
|
+ * registered. However, you don't have to call this method on the same
|
|
|
+ * exact instance of Preferences. You can call it on any instance.
|
|
|
+ *
|
|
|
+ * @param branch {String} [optional]
|
|
|
+ * the branch being observed
|
|
|
+ *
|
|
|
+ * @param callback {Object}
|
|
|
+ * the callback doing the observing
|
|
|
+ *
|
|
|
+ * @param thisObject {Object} [optional]
|
|
|
+ * the object being used as |this| when calling a Function callback
|
|
|
+ */
|
|
|
+ ignore: function(branch, callback, thisObject) {
|
|
|
+ let fullBranch = this._prefBranch + (branch || "");
|
|
|
+
|
|
|
+ // This seems fairly inefficient, but I'm not sure how much better we can
|
|
|
+ // make it. We could index by fullBranch, but we can't index by callback
|
|
|
+ // or thisObject, as far as I know, since the keys to JavaScript hashes
|
|
|
+ // (a.k.a. objects) can apparently only be primitive values.
|
|
|
+ let [observer] =
|
|
|
+ Preferences._observers.filter(function(v) v.branch == fullBranch &&
|
|
|
+ v.callback == callback &&
|
|
|
+ v.thisObject == thisObject);
|
|
|
+
|
|
|
+ if (observer) {
|
|
|
+ Preferences._prefSvc.removeObserver(fullBranch, observer);
|
|
|
+ Preferences._observers.splice(Preferences._observers.indexOf(observer), 1);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
// FIXME: make the methods below accept an array of pref names.
|
|
|
|
|
|
has: function(prefName) {
|
|
|
return (this._prefSvc.getPrefType(prefName) != Ci.nsIPrefBranch.PREF_INVALID);
|
|
|
},
|
|
|
|
|
|
+ // FIXME: change this to isSet (for consistency with set and reset).
|
|
|
modified: function(prefName) {
|
|
|
return (this.has(prefName) && this._prefSvc.prefHasUserValue(prefName));
|
|
|
},
|
|
@@ -195,6 +287,26 @@ Preferences.prototype = {
|
|
|
// first.
|
|
|
Preferences.__proto__ = Preferences.prototype;
|
|
|
|
|
|
+function PrefObserver(branch, callback, thisObject) {
|
|
|
+ this.branch = branch;
|
|
|
+ this.callback = callback;
|
|
|
+ this.thisObject = thisObject;
|
|
|
+}
|
|
|
+
|
|
|
+PrefObserver.prototype = {
|
|
|
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|
|
+ observe: function(subject, topic, data) {
|
|
|
+ if (typeof this.callback == "function") {
|
|
|
+ if (this.thisObject)
|
|
|
+ this.callback.call(this.thisObject);
|
|
|
+ else
|
|
|
+ this.callback();
|
|
|
+ }
|
|
|
+ else // typeof this.callback == "object" (nsIObserver)
|
|
|
+ this.callback.observe(subject, topic, data);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
function isArray(val) {
|
|
|
// We can't check for |val.constructor == Array| here, since the value
|
|
|
// might be from a different context whose Array constructor is not the same
|