1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/url-classifier/content/moz/preferences.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,274 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 + 1.9 +// Class for manipulating preferences. Aside from wrapping the pref 1.10 +// service, useful functionality includes: 1.11 +// 1.12 +// - abstracting prefobserving so that you can observe preferences 1.13 +// without implementing nsIObserver 1.14 +// 1.15 +// - getters that return a default value when the pref doesn't exist 1.16 +// (instead of throwing) 1.17 +// 1.18 +// - get-and-set getters 1.19 +// 1.20 +// Example: 1.21 +// 1.22 +// var p = new PROT_Preferences(); 1.23 +// dump(p.getPref("some-true-pref")); // shows true 1.24 +// dump(p.getPref("no-such-pref", true)); // shows true 1.25 +// dump(p.getPref("no-such-pref", null)); // shows null 1.26 +// 1.27 +// function observe(prefThatChanged) { 1.28 +// dump("Pref changed: " + prefThatChanged); 1.29 +// }; 1.30 +// 1.31 +// p.addObserver("somepref", observe); 1.32 +// p.setPref("somepref", true); // dumps 1.33 +// p.removeObserver("somepref", observe); 1.34 +// 1.35 +// TODO: should probably have the prefobserver pass in the new and old 1.36 +// values 1.37 + 1.38 +// TODO(tc): Maybe remove this class and just call natively since we're no 1.39 +// longer an extension. 1.40 + 1.41 +/** 1.42 + * A class that wraps the preferences service. 1.43 + * 1.44 + * @param opt_startPoint A starting point on the prefs tree to resolve 1.45 + * names passed to setPref and getPref. 1.46 + * 1.47 + * @param opt_useDefaultPranch Set to true to work against the default 1.48 + * preferences tree instead of the profile one. 1.49 + * 1.50 + * @constructor 1.51 + */ 1.52 +function G_Preferences(opt_startPoint, opt_getDefaultBranch) { 1.53 + this.debugZone = "prefs"; 1.54 + this.observers_ = {}; 1.55 + this.getDefaultBranch_ = !!opt_getDefaultBranch; 1.56 + 1.57 + this.startPoint_ = opt_startPoint || null; 1.58 +} 1.59 + 1.60 +G_Preferences.setterMap_ = { "string": "setCharPref", 1.61 + "boolean": "setBoolPref", 1.62 + "number": "setIntPref" }; 1.63 + 1.64 +G_Preferences.getterMap_ = {}; 1.65 +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref"; 1.66 +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref"; 1.67 +G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref"; 1.68 + 1.69 +G_Preferences.prototype.__defineGetter__('prefs_', function() { 1.70 + var prefs; 1.71 + var prefSvc = Cc["@mozilla.org/preferences-service;1"] 1.72 + .getService(Ci.nsIPrefService); 1.73 + 1.74 + if (this.getDefaultBranch_) { 1.75 + prefs = prefSvc.getDefaultBranch(this.startPoint_); 1.76 + } else { 1.77 + prefs = prefSvc.getBranch(this.startPoint_); 1.78 + } 1.79 + 1.80 + // QI to prefs in case we want to add observers 1.81 + prefs.QueryInterface(Ci.nsIPrefBranchInternal); 1.82 + return prefs; 1.83 +}); 1.84 + 1.85 +/** 1.86 + * Stores a key/value in a user preference. Valid types for val are string, 1.87 + * boolean, and number. Complex values are not yet supported (but feel free to 1.88 + * add them!). 1.89 + */ 1.90 +G_Preferences.prototype.setPref = function(key, val) { 1.91 + var datatype = typeof(val); 1.92 + 1.93 + if (datatype == "number" && (val % 1 != 0)) { 1.94 + throw new Error("Cannot store non-integer numbers in preferences."); 1.95 + } 1.96 + 1.97 + var meth = G_Preferences.setterMap_[datatype]; 1.98 + 1.99 + if (!meth) { 1.100 + throw new Error("Pref datatype {" + datatype + "} not supported."); 1.101 + } 1.102 + 1.103 + return this.prefs_[meth](key, val); 1.104 +} 1.105 + 1.106 +/** 1.107 + * Retrieves a user preference. Valid types for the value are the same as for 1.108 + * setPref. If the preference is not found, opt_default will be returned 1.109 + * instead. 1.110 + */ 1.111 +G_Preferences.prototype.getPref = function(key, opt_default) { 1.112 + var type = this.prefs_.getPrefType(key); 1.113 + 1.114 + // zero means that the specified pref didn't exist 1.115 + if (type == Ci.nsIPrefBranch.PREF_INVALID) { 1.116 + return opt_default; 1.117 + } 1.118 + 1.119 + var meth = G_Preferences.getterMap_[type]; 1.120 + 1.121 + if (!meth) { 1.122 + throw new Error("Pref datatype {" + type + "} not supported."); 1.123 + } 1.124 + 1.125 + // If a pref has been cleared, it will have a valid type but won't 1.126 + // be gettable, so this will throw. 1.127 + try { 1.128 + return this.prefs_[meth](key); 1.129 + } catch(e) { 1.130 + return opt_default; 1.131 + } 1.132 +} 1.133 + 1.134 +/** 1.135 + * Delete a preference. 1.136 + * 1.137 + * @param which Name of preference to obliterate 1.138 + */ 1.139 +G_Preferences.prototype.clearPref = function(which) { 1.140 + try { 1.141 + // This throws if the pref doesn't exist, which is fine because a 1.142 + // nonexistent pref is cleared 1.143 + this.prefs_.clearUserPref(which); 1.144 + } catch(e) {} 1.145 +} 1.146 + 1.147 +/** 1.148 + * Add an observer for a given pref. 1.149 + * 1.150 + * @param which String containing the pref to listen to 1.151 + * @param callback Function to be called when the pref changes. This 1.152 + * function will receive a single argument, a string 1.153 + * holding the preference name that changed 1.154 + */ 1.155 +G_Preferences.prototype.addObserver = function(which, callback) { 1.156 + // Need to store the observer we create so we can eventually unregister it 1.157 + if (!this.observers_[which]) 1.158 + this.observers_[which] = { callbacks: [], observers: [] }; 1.159 + 1.160 + /* only add an observer if the callback hasn't been registered yet */ 1.161 + if (this.observers_[which].callbacks.indexOf(callback) == -1) { 1.162 + var observer = new G_PreferenceObserver(callback); 1.163 + this.observers_[which].callbacks.push(callback); 1.164 + this.observers_[which].observers.push(observer); 1.165 + this.prefs_.addObserver(which, observer, false /* strong reference */); 1.166 + } 1.167 +} 1.168 + 1.169 +/** 1.170 + * Remove an observer for a given pref. 1.171 + * 1.172 + * @param which String containing the pref to stop listening to 1.173 + * @param callback Function to remove as an observer 1.174 + */ 1.175 +G_Preferences.prototype.removeObserver = function(which, callback) { 1.176 + var ix = this.observers_[which].callbacks.indexOf(callback); 1.177 + G_Assert(this, ix != -1, "Tried to unregister a nonexistent observer"); 1.178 + this.observers_[which].callbacks.splice(ix, 1); 1.179 + var observer = this.observers_[which].observers.splice(ix, 1)[0]; 1.180 + this.prefs_.removeObserver(which, observer); 1.181 +} 1.182 + 1.183 +/** 1.184 + * Remove all preference observers registered through this object. 1.185 + */ 1.186 +G_Preferences.prototype.removeAllObservers = function() { 1.187 + for (var which in this.observers_) { 1.188 + for each (var observer in this.observers_[which].observers) { 1.189 + this.prefs_.removeObserver(which, observer); 1.190 + } 1.191 + } 1.192 + this.observers_ = {}; 1.193 +} 1.194 + 1.195 +/** 1.196 + * Helper class that knows how to observe preference changes and 1.197 + * invoke a callback when they do 1.198 + * 1.199 + * @constructor 1.200 + * @param callback Function to call when the preference changes 1.201 + */ 1.202 +function G_PreferenceObserver(callback) { 1.203 + this.debugZone = "prefobserver"; 1.204 + this.callback_ = callback; 1.205 +} 1.206 + 1.207 +/** 1.208 + * Invoked by the pref system when a preference changes. Passes the 1.209 + * message along to the callback. 1.210 + * 1.211 + * @param subject The nsIPrefBranch that changed 1.212 + * @param topic String "nsPref:changed" (aka 1.213 + * NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it 1.214 + * live???) 1.215 + * @param data Name of the pref that changed 1.216 + */ 1.217 +G_PreferenceObserver.prototype.observe = function(subject, topic, data) { 1.218 + G_Debug(this, "Observed pref change: " + data); 1.219 + this.callback_(data); 1.220 +} 1.221 + 1.222 +/** 1.223 + * XPCOM cruft 1.224 + * 1.225 + * @param iid Interface id of the interface the caller wants 1.226 + */ 1.227 +G_PreferenceObserver.prototype.QueryInterface = function(iid) { 1.228 + if (iid.equals(Ci.nsISupports) || 1.229 + iid.equals(Ci.nsIObserver) || 1.230 + iid.equals(Ci.nsISupportsWeakReference)) 1.231 + return this; 1.232 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.233 +} 1.234 + 1.235 +#ifdef DEBUG 1.236 +// UNITTESTS 1.237 +function TEST_G_Preferences() { 1.238 + if (G_GDEBUG) { 1.239 + var z = "preferences UNITTEST"; 1.240 + G_debugService.enableZone(z); 1.241 + G_Debug(z, "Starting"); 1.242 + 1.243 + var p = new G_Preferences(); 1.244 + 1.245 + var testPref = "test-preferences-unittest"; 1.246 + var noSuchPref = "test-preferences-unittest-aypabtu"; 1.247 + 1.248 + // Used to test observing 1.249 + var observeCount = 0; 1.250 + function observe(prefChanged) { 1.251 + G_Assert(z, prefChanged == testPref, "observer broken"); 1.252 + observeCount++; 1.253 + }; 1.254 + 1.255 + // Test setting, getting, and observing 1.256 + p.addObserver(testPref, observe); 1.257 + p.setPref(testPref, true); 1.258 + G_Assert(z, p.getPref(testPref), "get or set broken"); 1.259 + G_Assert(z, observeCount == 1, "observer adding not working"); 1.260 + 1.261 + p.removeObserver(testPref, observe); 1.262 + 1.263 + p.setPref(testPref, false); 1.264 + G_Assert(z, observeCount == 1, "observer removal not working"); 1.265 + G_Assert(z, !p.getPref(testPref), "get broken"); 1.266 + 1.267 + // Remember to clean up the prefs we've set, and test removing prefs 1.268 + // while we're at it 1.269 + p.clearPref(noSuchPref); 1.270 + G_Assert(z, !p.getPref(noSuchPref, false), "clear broken"); 1.271 + 1.272 + p.clearPref(testPref); 1.273 + 1.274 + G_Debug(z, "PASSED"); 1.275 + } 1.276 +} 1.277 +#endif