forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			276 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| # 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 /* 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
 | 
