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

changeset 0
6474c204b198
     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

mercurial