diff options
Diffstat (limited to 'toolkit/components/url-classifier/content/moz/preferences.js')
-rw-r--r-- | toolkit/components/url-classifier/content/moz/preferences.js | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/content/moz/preferences.js b/toolkit/components/url-classifier/content/moz/preferences.js new file mode 100644 index 000000000..30105ab34 --- /dev/null +++ b/toolkit/components/url-classifier/content/moz/preferences.js @@ -0,0 +1,276 @@ +# 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/. + + +// Class for manipulating preferences. Aside from wrapping the pref +// service, useful functionality includes: +// +// - abstracting prefobserving so that you can observe preferences +// without implementing nsIObserver +// +// - getters that return a default value when the pref doesn't exist +// (instead of throwing) +// +// - get-and-set getters +// +// Example: +// +// var p = new PROT_Preferences(); +// dump(p.getPref("some-true-pref")); // shows true +// dump(p.getPref("no-such-pref", true)); // shows true +// dump(p.getPref("no-such-pref", null)); // shows null +// +// function observe(prefThatChanged) { +// dump("Pref changed: " + prefThatChanged); +// }; +// +// p.addObserver("somepref", observe); +// p.setPref("somepref", true); // dumps +// p.removeObserver("somepref", observe); +// +// TODO: should probably have the prefobserver pass in the new and old +// values + +// TODO(tc): Maybe remove this class and just call natively since we're no +// longer an extension. + +/** + * A class that wraps the preferences service. + * + * @param opt_startPoint A starting point on the prefs tree to resolve + * names passed to setPref and getPref. + * + * @param opt_useDefaultPranch Set to true to work against the default + * preferences tree instead of the profile one. + * + * @constructor + */ +this.G_Preferences = +function G_Preferences(opt_startPoint, opt_getDefaultBranch) { + this.debugZone = "prefs"; + this.observers_ = {}; + this.getDefaultBranch_ = !!opt_getDefaultBranch; + + this.startPoint_ = opt_startPoint || null; +} + +G_Preferences.setterMap_ = { "string": "setCharPref", + "boolean": "setBoolPref", + "number": "setIntPref" }; + +G_Preferences.getterMap_ = {}; +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref"; +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref"; +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref"; + +G_Preferences.prototype.__defineGetter__('prefs_', function() { + var prefs; + var prefSvc = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService); + + if (this.getDefaultBranch_) { + prefs = prefSvc.getDefaultBranch(this.startPoint_); + } else { + prefs = prefSvc.getBranch(this.startPoint_); + } + + // QI to prefs in case we want to add observers + prefs.QueryInterface(Ci.nsIPrefBranchInternal); + return prefs; +}); + +/** + * Stores a key/value in a user preference. Valid types for val are string, + * boolean, and number. Complex values are not yet supported (but feel free to + * add them!). + */ +G_Preferences.prototype.setPref = function(key, val) { + var datatype = typeof(val); + + if (datatype == "number" && (val % 1 != 0)) { + throw new Error("Cannot store non-integer numbers in preferences."); + } + + var meth = G_Preferences.setterMap_[datatype]; + + if (!meth) { + throw new Error("Pref datatype {" + datatype + "} not supported."); + } + + return this.prefs_[meth](key, val); +} + +/** + * Retrieves a user preference. Valid types for the value are the same as for + * setPref. If the preference is not found, opt_default will be returned + * instead. + */ +G_Preferences.prototype.getPref = function(key, opt_default) { + var type = this.prefs_.getPrefType(key); + + // zero means that the specified pref didn't exist + if (type == Ci.nsIPrefBranch.PREF_INVALID) { + return opt_default; + } + + var meth = G_Preferences.getterMap_[type]; + + if (!meth) { + throw new Error("Pref datatype {" + type + "} not supported."); + } + + // If a pref has been cleared, it will have a valid type but won't + // be gettable, so this will throw. + try { + return this.prefs_[meth](key); + } catch(e) { + return opt_default; + } +} + +/** + * Delete a preference. + * + * @param which Name of preference to obliterate + */ +G_Preferences.prototype.clearPref = function(which) { + try { + // This throws if the pref doesn't exist, which is fine because a + // nonexistent pref is cleared + this.prefs_.clearUserPref(which); + } catch(e) {} +} + +/** + * Add an observer for a given pref. + * + * @param which String containing the pref to listen to + * @param callback Function to be called when the pref changes. This + * function will receive a single argument, a string + * holding the preference name that changed + */ +G_Preferences.prototype.addObserver = function(which, callback) { + // Need to store the observer we create so we can eventually unregister it + if (!this.observers_[which]) + this.observers_[which] = { callbacks: [], observers: [] }; + + /* only add an observer if the callback hasn't been registered yet */ + if (this.observers_[which].callbacks.indexOf(callback) == -1) { + var observer = new G_PreferenceObserver(callback); + this.observers_[which].callbacks.push(callback); + this.observers_[which].observers.push(observer); + this.prefs_.addObserver(which, observer, false /* strong reference */); + } +} + +/** + * Remove an observer for a given pref. + * + * @param which String containing the pref to stop listening to + * @param callback Function to remove as an observer + */ +G_Preferences.prototype.removeObserver = function(which, callback) { + var ix = this.observers_[which].callbacks.indexOf(callback); + G_Assert(this, ix != -1, "Tried to unregister a nonexistent observer"); + this.observers_[which].callbacks.splice(ix, 1); + var observer = this.observers_[which].observers.splice(ix, 1)[0]; + this.prefs_.removeObserver(which, observer); +} + +/** + * Remove all preference observers registered through this object. + */ +G_Preferences.prototype.removeAllObservers = function() { + for (var which in this.observers_) { + for (var observer of this.observers_[which].observers) { + this.prefs_.removeObserver(which, observer); + } + } + this.observers_ = {}; +} + +/** + * Helper class that knows how to observe preference changes and + * invoke a callback when they do + * + * @constructor + * @param callback Function to call when the preference changes + */ +this.G_PreferenceObserver = +function G_PreferenceObserver(callback) { + this.debugZone = "prefobserver"; + this.callback_ = callback; +} + +/** + * Invoked by the pref system when a preference changes. Passes the + * message along to the callback. + * + * @param subject The nsIPrefBranch that changed + * @param topic String "nsPref:changed" (aka + * NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it + * live???) + * @param data Name of the pref that changed + */ +G_PreferenceObserver.prototype.observe = function(subject, topic, data) { + G_Debug(this, "Observed pref change: " + data); + this.callback_(data); +} + +/** + * XPCOM cruft + * + * @param iid Interface id of the interface the caller wants + */ +G_PreferenceObserver.prototype.QueryInterface = function(iid) { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIObserver) || + iid.equals(Ci.nsISupportsWeakReference)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; +} + +#ifdef DEBUG +// UNITTESTS +this.TEST_G_Preferences = function TEST_G_Preferences() { + if (G_GDEBUG) { + var z = "preferences UNITTEST"; + G_debugService.enableZone(z); + G_Debug(z, "Starting"); + + var p = new G_Preferences(); + + var testPref = "test-preferences-unittest"; + var noSuchPref = "test-preferences-unittest-aypabtu"; + + // Used to test observing + var observeCount = 0; + let observe = function (prefChanged) { + G_Assert(z, prefChanged == testPref, "observer broken"); + observeCount++; + }; + + // Test setting, getting, and observing + p.addObserver(testPref, observe); + p.setPref(testPref, true); + G_Assert(z, p.getPref(testPref), "get or set broken"); + G_Assert(z, observeCount == 1, "observer adding not working"); + + p.removeObserver(testPref, observe); + + p.setPref(testPref, false); + G_Assert(z, observeCount == 1, "observer removal not working"); + G_Assert(z, !p.getPref(testPref), "get broken"); + + // Remember to clean up the prefs we've set, and test removing prefs + // while we're at it + p.clearPref(noSuchPref); + G_Assert(z, !p.getPref(noSuchPref, false), "clear broken"); + + p.clearPref(testPref); + + G_Debug(z, "PASSED"); + } +} +#endif |