toolkit/components/url-classifier/content/moz/preferences.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5
michael@0 6 // Class for manipulating preferences. Aside from wrapping the pref
michael@0 7 // service, useful functionality includes:
michael@0 8 //
michael@0 9 // - abstracting prefobserving so that you can observe preferences
michael@0 10 // without implementing nsIObserver
michael@0 11 //
michael@0 12 // - getters that return a default value when the pref doesn't exist
michael@0 13 // (instead of throwing)
michael@0 14 //
michael@0 15 // - get-and-set getters
michael@0 16 //
michael@0 17 // Example:
michael@0 18 //
michael@0 19 // var p = new PROT_Preferences();
michael@0 20 // dump(p.getPref("some-true-pref")); // shows true
michael@0 21 // dump(p.getPref("no-such-pref", true)); // shows true
michael@0 22 // dump(p.getPref("no-such-pref", null)); // shows null
michael@0 23 //
michael@0 24 // function observe(prefThatChanged) {
michael@0 25 // dump("Pref changed: " + prefThatChanged);
michael@0 26 // };
michael@0 27 //
michael@0 28 // p.addObserver("somepref", observe);
michael@0 29 // p.setPref("somepref", true); // dumps
michael@0 30 // p.removeObserver("somepref", observe);
michael@0 31 //
michael@0 32 // TODO: should probably have the prefobserver pass in the new and old
michael@0 33 // values
michael@0 34
michael@0 35 // TODO(tc): Maybe remove this class and just call natively since we're no
michael@0 36 // longer an extension.
michael@0 37
michael@0 38 /**
michael@0 39 * A class that wraps the preferences service.
michael@0 40 *
michael@0 41 * @param opt_startPoint A starting point on the prefs tree to resolve
michael@0 42 * names passed to setPref and getPref.
michael@0 43 *
michael@0 44 * @param opt_useDefaultPranch Set to true to work against the default
michael@0 45 * preferences tree instead of the profile one.
michael@0 46 *
michael@0 47 * @constructor
michael@0 48 */
michael@0 49 function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
michael@0 50 this.debugZone = "prefs";
michael@0 51 this.observers_ = {};
michael@0 52 this.getDefaultBranch_ = !!opt_getDefaultBranch;
michael@0 53
michael@0 54 this.startPoint_ = opt_startPoint || null;
michael@0 55 }
michael@0 56
michael@0 57 G_Preferences.setterMap_ = { "string": "setCharPref",
michael@0 58 "boolean": "setBoolPref",
michael@0 59 "number": "setIntPref" };
michael@0 60
michael@0 61 G_Preferences.getterMap_ = {};
michael@0 62 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
michael@0 63 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
michael@0 64 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
michael@0 65
michael@0 66 G_Preferences.prototype.__defineGetter__('prefs_', function() {
michael@0 67 var prefs;
michael@0 68 var prefSvc = Cc["@mozilla.org/preferences-service;1"]
michael@0 69 .getService(Ci.nsIPrefService);
michael@0 70
michael@0 71 if (this.getDefaultBranch_) {
michael@0 72 prefs = prefSvc.getDefaultBranch(this.startPoint_);
michael@0 73 } else {
michael@0 74 prefs = prefSvc.getBranch(this.startPoint_);
michael@0 75 }
michael@0 76
michael@0 77 // QI to prefs in case we want to add observers
michael@0 78 prefs.QueryInterface(Ci.nsIPrefBranchInternal);
michael@0 79 return prefs;
michael@0 80 });
michael@0 81
michael@0 82 /**
michael@0 83 * Stores a key/value in a user preference. Valid types for val are string,
michael@0 84 * boolean, and number. Complex values are not yet supported (but feel free to
michael@0 85 * add them!).
michael@0 86 */
michael@0 87 G_Preferences.prototype.setPref = function(key, val) {
michael@0 88 var datatype = typeof(val);
michael@0 89
michael@0 90 if (datatype == "number" && (val % 1 != 0)) {
michael@0 91 throw new Error("Cannot store non-integer numbers in preferences.");
michael@0 92 }
michael@0 93
michael@0 94 var meth = G_Preferences.setterMap_[datatype];
michael@0 95
michael@0 96 if (!meth) {
michael@0 97 throw new Error("Pref datatype {" + datatype + "} not supported.");
michael@0 98 }
michael@0 99
michael@0 100 return this.prefs_[meth](key, val);
michael@0 101 }
michael@0 102
michael@0 103 /**
michael@0 104 * Retrieves a user preference. Valid types for the value are the same as for
michael@0 105 * setPref. If the preference is not found, opt_default will be returned
michael@0 106 * instead.
michael@0 107 */
michael@0 108 G_Preferences.prototype.getPref = function(key, opt_default) {
michael@0 109 var type = this.prefs_.getPrefType(key);
michael@0 110
michael@0 111 // zero means that the specified pref didn't exist
michael@0 112 if (type == Ci.nsIPrefBranch.PREF_INVALID) {
michael@0 113 return opt_default;
michael@0 114 }
michael@0 115
michael@0 116 var meth = G_Preferences.getterMap_[type];
michael@0 117
michael@0 118 if (!meth) {
michael@0 119 throw new Error("Pref datatype {" + type + "} not supported.");
michael@0 120 }
michael@0 121
michael@0 122 // If a pref has been cleared, it will have a valid type but won't
michael@0 123 // be gettable, so this will throw.
michael@0 124 try {
michael@0 125 return this.prefs_[meth](key);
michael@0 126 } catch(e) {
michael@0 127 return opt_default;
michael@0 128 }
michael@0 129 }
michael@0 130
michael@0 131 /**
michael@0 132 * Delete a preference.
michael@0 133 *
michael@0 134 * @param which Name of preference to obliterate
michael@0 135 */
michael@0 136 G_Preferences.prototype.clearPref = function(which) {
michael@0 137 try {
michael@0 138 // This throws if the pref doesn't exist, which is fine because a
michael@0 139 // nonexistent pref is cleared
michael@0 140 this.prefs_.clearUserPref(which);
michael@0 141 } catch(e) {}
michael@0 142 }
michael@0 143
michael@0 144 /**
michael@0 145 * Add an observer for a given pref.
michael@0 146 *
michael@0 147 * @param which String containing the pref to listen to
michael@0 148 * @param callback Function to be called when the pref changes. This
michael@0 149 * function will receive a single argument, a string
michael@0 150 * holding the preference name that changed
michael@0 151 */
michael@0 152 G_Preferences.prototype.addObserver = function(which, callback) {
michael@0 153 // Need to store the observer we create so we can eventually unregister it
michael@0 154 if (!this.observers_[which])
michael@0 155 this.observers_[which] = { callbacks: [], observers: [] };
michael@0 156
michael@0 157 /* only add an observer if the callback hasn't been registered yet */
michael@0 158 if (this.observers_[which].callbacks.indexOf(callback) == -1) {
michael@0 159 var observer = new G_PreferenceObserver(callback);
michael@0 160 this.observers_[which].callbacks.push(callback);
michael@0 161 this.observers_[which].observers.push(observer);
michael@0 162 this.prefs_.addObserver(which, observer, false /* strong reference */);
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166 /**
michael@0 167 * Remove an observer for a given pref.
michael@0 168 *
michael@0 169 * @param which String containing the pref to stop listening to
michael@0 170 * @param callback Function to remove as an observer
michael@0 171 */
michael@0 172 G_Preferences.prototype.removeObserver = function(which, callback) {
michael@0 173 var ix = this.observers_[which].callbacks.indexOf(callback);
michael@0 174 G_Assert(this, ix != -1, "Tried to unregister a nonexistent observer");
michael@0 175 this.observers_[which].callbacks.splice(ix, 1);
michael@0 176 var observer = this.observers_[which].observers.splice(ix, 1)[0];
michael@0 177 this.prefs_.removeObserver(which, observer);
michael@0 178 }
michael@0 179
michael@0 180 /**
michael@0 181 * Remove all preference observers registered through this object.
michael@0 182 */
michael@0 183 G_Preferences.prototype.removeAllObservers = function() {
michael@0 184 for (var which in this.observers_) {
michael@0 185 for each (var observer in this.observers_[which].observers) {
michael@0 186 this.prefs_.removeObserver(which, observer);
michael@0 187 }
michael@0 188 }
michael@0 189 this.observers_ = {};
michael@0 190 }
michael@0 191
michael@0 192 /**
michael@0 193 * Helper class that knows how to observe preference changes and
michael@0 194 * invoke a callback when they do
michael@0 195 *
michael@0 196 * @constructor
michael@0 197 * @param callback Function to call when the preference changes
michael@0 198 */
michael@0 199 function G_PreferenceObserver(callback) {
michael@0 200 this.debugZone = "prefobserver";
michael@0 201 this.callback_ = callback;
michael@0 202 }
michael@0 203
michael@0 204 /**
michael@0 205 * Invoked by the pref system when a preference changes. Passes the
michael@0 206 * message along to the callback.
michael@0 207 *
michael@0 208 * @param subject The nsIPrefBranch that changed
michael@0 209 * @param topic String "nsPref:changed" (aka
michael@0 210 * NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
michael@0 211 * live???)
michael@0 212 * @param data Name of the pref that changed
michael@0 213 */
michael@0 214 G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
michael@0 215 G_Debug(this, "Observed pref change: " + data);
michael@0 216 this.callback_(data);
michael@0 217 }
michael@0 218
michael@0 219 /**
michael@0 220 * XPCOM cruft
michael@0 221 *
michael@0 222 * @param iid Interface id of the interface the caller wants
michael@0 223 */
michael@0 224 G_PreferenceObserver.prototype.QueryInterface = function(iid) {
michael@0 225 if (iid.equals(Ci.nsISupports) ||
michael@0 226 iid.equals(Ci.nsIObserver) ||
michael@0 227 iid.equals(Ci.nsISupportsWeakReference))
michael@0 228 return this;
michael@0 229 throw Components.results.NS_ERROR_NO_INTERFACE;
michael@0 230 }
michael@0 231
michael@0 232 #ifdef DEBUG
michael@0 233 // UNITTESTS
michael@0 234 function TEST_G_Preferences() {
michael@0 235 if (G_GDEBUG) {
michael@0 236 var z = "preferences UNITTEST";
michael@0 237 G_debugService.enableZone(z);
michael@0 238 G_Debug(z, "Starting");
michael@0 239
michael@0 240 var p = new G_Preferences();
michael@0 241
michael@0 242 var testPref = "test-preferences-unittest";
michael@0 243 var noSuchPref = "test-preferences-unittest-aypabtu";
michael@0 244
michael@0 245 // Used to test observing
michael@0 246 var observeCount = 0;
michael@0 247 function observe(prefChanged) {
michael@0 248 G_Assert(z, prefChanged == testPref, "observer broken");
michael@0 249 observeCount++;
michael@0 250 };
michael@0 251
michael@0 252 // Test setting, getting, and observing
michael@0 253 p.addObserver(testPref, observe);
michael@0 254 p.setPref(testPref, true);
michael@0 255 G_Assert(z, p.getPref(testPref), "get or set broken");
michael@0 256 G_Assert(z, observeCount == 1, "observer adding not working");
michael@0 257
michael@0 258 p.removeObserver(testPref, observe);
michael@0 259
michael@0 260 p.setPref(testPref, false);
michael@0 261 G_Assert(z, observeCount == 1, "observer removal not working");
michael@0 262 G_Assert(z, !p.getPref(testPref), "get broken");
michael@0 263
michael@0 264 // Remember to clean up the prefs we've set, and test removing prefs
michael@0 265 // while we're at it
michael@0 266 p.clearPref(noSuchPref);
michael@0 267 G_Assert(z, !p.getPref(noSuchPref, false), "clear broken");
michael@0 268
michael@0 269 p.clearPref(testPref);
michael@0 270
michael@0 271 G_Debug(z, "PASSED");
michael@0 272 }
michael@0 273 }
michael@0 274 #endif

mercurial