2007-03-22 10:30:00 -07:00
|
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
#
|
|
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
# the License. You may obtain a copy of the License at
|
|
|
|
# http://www.mozilla.org/MPL/
|
|
|
|
#
|
|
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
# for the specific language governing rights and limitations under the
|
|
|
|
# License.
|
|
|
|
#
|
|
|
|
# The Original Code is Google Safe Browsing.
|
|
|
|
#
|
|
|
|
# The Initial Developer of the Original Code is Google Inc.
|
|
|
|
# Portions created by the Initial Developer are Copyright (C) 2006
|
|
|
|
# the Initial Developer. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s):
|
|
|
|
# Fritz Schneider <fritz@google.com> (original author)
|
|
|
|
#
|
|
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
# the provisions above, a recipient may use your version of this file under
|
|
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
#
|
|
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
|
|
|
|
|
|
|
|
// 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();
|
2007-06-02 09:59:52 -07:00
|
|
|
// 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
|
2007-03-22 10:30:00 -07:00
|
|
|
//
|
|
|
|
// function observe(prefThatChanged) {
|
2007-06-02 09:59:52 -07:00
|
|
|
// dump("Pref changed: " + prefThatChanged);
|
2007-03-22 10:30:00 -07:00
|
|
|
// };
|
|
|
|
//
|
|
|
|
// p.addObserver("somepref", observe);
|
2007-06-02 09:59:52 -07:00
|
|
|
// p.setPref("somepref", true); // dumps
|
2007-03-22 10:30:00 -07:00
|
|
|
// 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
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
// non-existent 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])
|
2007-06-10 16:59:20 -07:00
|
|
|
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 */);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2007-06-10 16:59:20 -07:00
|
|
|
var ix = this.observers_[which].callbacks.indexOf(callback);
|
|
|
|
G_Assert(this, ix != -1, "Tried to unregister a nonexistant observer");
|
|
|
|
this.observers_[which].callbacks.splice(ix, 1);
|
|
|
|
var observer = this.observers_[which].observers.splice(ix, 1)[0];
|
2007-03-22 10:30:00 -07:00
|
|
|
this.prefs_.removeObserver(which, observer);
|
|
|
|
}
|
|
|
|
|
2007-07-09 13:53:01 -07:00
|
|
|
/**
|
|
|
|
* Remove all preference observers registered through this object.
|
|
|
|
*/
|
|
|
|
G_Preferences.prototype.removeAllObservers = function() {
|
|
|
|
for (var which in this.observers_) {
|
|
|
|
for each (var observer in this.observers_[which].observers) {
|
|
|
|
this.prefs_.removeObserver(which, observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.observers_ = {};
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
function observe(prefChanged) {
|
|
|
|
G_Assert(z, prefChanged == testPref, "observer broken");
|
|
|
|
observeCount++;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Test setting, getting, and observing
|
|
|
|
p.addObserver(testPref, observe);
|
2007-06-02 14:50:58 -07:00
|
|
|
p.setPref(testPref, true);
|
|
|
|
G_Assert(z, p.getPref(testPref), "get or set broken");
|
2007-03-22 10:30:00 -07:00
|
|
|
G_Assert(z, observeCount == 1, "observer adding not working");
|
|
|
|
|
|
|
|
p.removeObserver(testPref, observe);
|
|
|
|
|
2007-06-02 14:50:58 -07:00
|
|
|
p.setPref(testPref, false);
|
2007-03-22 10:30:00 -07:00
|
|
|
G_Assert(z, observeCount == 1, "observer removal not working");
|
2007-06-02 14:50:58 -07:00
|
|
|
G_Assert(z, !p.getPref(testPref), "get broken");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Remember to clean up the prefs we've set, and test removing prefs
|
|
|
|
// while we're at it
|
|
|
|
p.clearPref(noSuchPref);
|
2007-06-02 14:50:58 -07:00
|
|
|
G_Assert(z, !p.getPref(noSuchPref, false), "clear broken");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
p.clearPref(testPref);
|
|
|
|
|
|
|
|
G_Debug(z, "PASSED");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|