Files
palemoon27/toolkit/modules/Preferences.jsm
roytam1 17915ae762 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1205941 - Make TimerFirings logging output post-processible with fix_linux_stack.py. r=glandium. (042e3968a2)
- Bug 1203427 (part 6) - Add link to MDN docs about TimerFirings logging. r=me. (19967a22e7)
- missing bit of Bug 1178890 (b421ab56a1)
- Bug 1188719 - Show login fill context menu on username field. r=MattN (6107196364)
- Bug 1192081 - Changed createFromPasswordField to also create FormLikes from username fields. r=MattN (e4fa58f8b1)
- Bug 1168707 - Improve logging of FormLike objects with a toJSON method and add/improve some logging. r=dolske (ec852081e5)
- Bug 1159538 - Allow per-site recipes to adjust the username/password field detection for onUsernameInput. r=MattN (1c43e116ce)
- Bug 1211780 - Don't try to use oldPasswordField if it's null. r=dolske (0b4bb1c723)
- missing bits of 1120129 (78ee8f8ea9)
- Bug 1173688 - Don't show the Sync promo for https://accounts.firefox.com password doorhangers. r=markh (39cc54fe93)
- Bug 1187529 - Factored viewPasswords function from pageinfo/security.js to LoginHelper.jsm. r=MattN (cdffa966e8)
- Bug 1094821 - Make it possible for an application to load extra themes into LightweightThemeManager.usedThemes;r=Gijs (4feb0c572f)
- Bug 1171146 - Display a learn more link when refusing to install an unsigned add-on. r=dtownsend (478028a558)
- Bug 1170162: Use a support page for signing warnings intended for users. r=dao (cebab74ae2)
- Bug 1207497 - Part 1: Remove use of expression closure from toolkit/, exept tests. r=Gijs (4cf4abbf3d)
- bits of 1151507 (9fdd321f10)
- Bug 1172710: Breaking an add-on's signature doesn't show a notification on next startup. r=dao (9073ba4d46)
- Bug 1191008: Display the unsigned notification on the front-most window. r=dao (8dbefb4b3a)
- Bug 1191421: Don't notify users about unsigned experiments that have been disabled. r=dao (2c0c892d4c)
- Bug 1195034: Only show messages about add-ons being disabled if signing requirements are on. r=dao (76a9df56d2)
- Bug 1194784: Sideloaded add-ons without full signing don't display an appropriate warning in the add-ons manager. r=dao (ce43e17c65)
- Bug 1207497 - Part 2: Remove use of expression closure from tests in toolkit/. r=Gijs (6c4517c20e)
- Bug 1207497 - Part 3: Fix wrong replacement in debug print in toolkit/mozapps/downloads/tests/unit/test_lowMinutes.js. r=me DONTBUILD (5311950d45)
- Bug 1151267 - app update telemetry for UPDATE_CANNOT_APPLY_* is reversed. r=spohl (f26ff5efab)
- bits of 1137447 (b5c86c9f43)
- bug 1171649 - Implement arm/iOS support in JS JITs. r=jandem (0eb06f1d3d)
- Bug 1205708: Check if validation failed before reporting helper thread failure in Odin; r=luke (0bd8b70919)
- missing bit of Bug 1112627: Remove redundant inline specifier in SIMD (76cea80b8c)
- Bug 1189059: Replace setObjectMetadataCallback with enableObjectMetadataCallback, fix callers. r=fitzgen (053ae86af2)
- Bug 1125412 - Draw a graph of memory usage, r=terrence (4ac21380a4)
- Bug 1147985 - Avoid blank space when heap size graph is unavailable, r=terrence (9b48d4d435)
- Bug 1170372 - Skip js1_5/Regress/regress-312588.js on SM(cgc) builds due to timeouts. (5298485837)
- Bug 1160149 - Skip basic/testManyVars.js on SM(cgc) builds for frequent timeouts. (562cfc2713)
- Bug 1198549 - Switch from | to $ as the preferred separator token (due to operator|), r=me\ (cc6fdb0697)
- Bug 963738 - Handle Arrays in the analysis, r=terrence (589b285306)
- Bug 1209696 - Check the return value of fopen, r=terrence (8c2378f3f9)
- Bug 1197941 - Allow getline() to malloc its own buffer to avoid intermittent crashes, r=shu (e37b934fcc)
- Bug 1180985 - Implement a JS GDB pretty-printer for JS::GCCellPtr. r=sfink (8848723b3a)
- Bug 1180984 - JS GDB pretty-printers: Support Python 3. r=sfink (ae4c76014d)
- Bug 1198628 - IonMonkey: ARM: Redefine FloatRegisters::Code and use it in the right way. r=nbp (d0d608b1cc)
- Bug 1198145 - guard calls to getInst(). r=me (bb8c4e2e4b)
2022-09-19 13:45:05 +08:00

427 lines
14 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/. */
this.EXPORTED_SYMBOLS = ["Preferences"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// The minimum and maximum integers that can be set as preferences.
// The range of valid values is narrower than the range of valid JS values
// because the native preferences code treats integers as NSPR PRInt32s,
// which are 32-bit signed integers on all platforms.
const MAX_INT = 0x7FFFFFFF; // Math.pow(2, 31) - 1
const MIN_INT = -0x80000000;
this.Preferences =
function Preferences(args) {
this._cachedPrefBranch = null;
if (isObject(args)) {
if (args.branch)
this._branchStr = args.branch;
if (args.defaultBranch)
this._defaultBranch = args.defaultBranch;
if (args.privacyContext)
this._privacyContext = args.privacyContext;
}
else if (args)
this._branchStr = args;
};
/**
* Get the value of a pref, if any; otherwise return the default value.
*
* @param prefName {String|Array}
* the pref to get, or an array of prefs to get
*
* @param defaultValue
* the default value, if any, for prefs that don't have one
*
* @param valueType
* the XPCOM interface of the pref's complex value type, if any
*
* @returns the value of the pref, if any; otherwise the default value
*/
Preferences.get = function(prefName, defaultValue, valueType = Ci.nsISupportsString) {
if (Array.isArray(prefName))
return prefName.map(v => this.get(v, defaultValue));
return this._get(prefName, defaultValue, valueType);
};
Preferences._get = function(prefName, defaultValue, valueType) {
switch (this._prefBranch.getPrefType(prefName)) {
case Ci.nsIPrefBranch.PREF_STRING:
return this._prefBranch.getComplexValue(prefName, valueType).data;
case Ci.nsIPrefBranch.PREF_INT:
return this._prefBranch.getIntPref(prefName);
case Ci.nsIPrefBranch.PREF_BOOL:
return this._prefBranch.getBoolPref(prefName);
case Ci.nsIPrefBranch.PREF_INVALID:
return defaultValue;
default:
// This should never happen.
throw "Error getting pref " + prefName + "; its value's type is " +
this._prefBranch.getPrefType(prefName) + ", which I don't " +
"know how to handle.";
}
};
/**
* Set a preference to a value.
*
* You can set multiple prefs by passing an object as the only parameter.
* In that case, this method will treat the properties of the object
* as preferences to set, where each property name is the name of a pref
* and its corresponding property value is the value of the pref.
*
* @param prefName {String|Object}
* the name of the pref to set; or an object containing a set
* of prefs to set
*
* @param prefValue {String|Number|Boolean}
* the value to which to set the pref
*
* Note: Preferences cannot store non-integer numbers or numbers outside
* the signed 32-bit range -(2^31-1) to 2^31-1, If you have such a number,
* store it as a string by calling toString() on the number before passing
* it to this method, i.e.:
* Preferences.set("pi", 3.14159.toString())
* Preferences.set("big", Math.pow(2, 31).toString()).
*/
Preferences.set = function(prefName, prefValue) {
if (isObject(prefName)) {
for (let [name, value] in Iterator(prefName))
this.set(name, value);
return;
}
this._set(prefName, prefValue);
};
Preferences._set = function(prefName, prefValue) {
let prefType;
if (typeof prefValue != "undefined" && prefValue != null)
prefType = prefValue.constructor.name;
switch (prefType) {
case "String":
{
let str = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
str.data = prefValue;
this._prefBranch.setComplexValue(prefName, Ci.nsISupportsString, str);
}
break;
case "Number":
// We throw if the number is outside the range, since the result
// will never be what the consumer wanted to store, but we only warn
// if the number is non-integer, since the consumer might not mind
// the loss of precision.
if (prefValue > MAX_INT || prefValue < MIN_INT)
throw("you cannot set the " + prefName + " pref to the number " +
prefValue + ", as number pref values must be in the signed " +
"32-bit integer range -(2^31-1) to 2^31-1. To store numbers " +
"outside that range, store them as strings.");
this._prefBranch.setIntPref(prefName, prefValue);
if (prefValue % 1 != 0)
Cu.reportError("Warning: setting the " + prefName + " pref to the " +
"non-integer number " + prefValue + " converted it " +
"to the integer number " + this.get(prefName) +
"; to retain fractional precision, store non-integer " +
"numbers as strings.");
break;
case "Boolean":
this._prefBranch.setBoolPref(prefName, prefValue);
break;
default:
throw "can't set pref " + prefName + " to value '" + prefValue +
"'; it isn't a String, Number, or Boolean";
}
};
/**
* Whether or not the given pref has a value. This is different from isSet
* because it returns true whether the value of the pref is a default value
* or a user-set value, while isSet only returns true if the value
* is a user-set value.
*
* @param prefName {String|Array}
* the pref to check, or an array of prefs to check
*
* @returns {Boolean|Array}
* whether or not the pref has a value; or, if the caller provided
* an array of pref names, an array of booleans indicating whether
* or not the prefs have values
*/
Preferences.has = function(prefName) {
if (Array.isArray(prefName))
return prefName.map(this.has, this);
return (this._prefBranch.getPrefType(prefName) != Ci.nsIPrefBranch.PREF_INVALID);
};
/**
* Whether or not the given pref has a user-set value. This is different
* from |has| because it returns true only if the value of the pref is a user-
* set value, while |has| returns true if the value of the pref is a default
* value or a user-set value.
*
* @param prefName {String|Array}
* the pref to check, or an array of prefs to check
*
* @returns {Boolean|Array}
* whether or not the pref has a user-set value; or, if the caller
* provided an array of pref names, an array of booleans indicating
* whether or not the prefs have user-set values
*/
Preferences.isSet = function(prefName) {
if (Array.isArray(prefName))
return prefName.map(this.isSet, this);
return (this.has(prefName) && this._prefBranch.prefHasUserValue(prefName));
},
/**
* Whether or not the given pref has a user-set value. Use isSet instead,
* which is equivalent.
* @deprecated
*/
Preferences.modified = function(prefName) { return this.isSet(prefName) },
Preferences.reset = function(prefName) {
if (Array.isArray(prefName)) {
prefName.map(v => this.reset(v));
return;
}
this._prefBranch.clearUserPref(prefName);
};
/**
* Lock a pref so it can't be changed.
*
* @param prefName {String|Array}
* the pref to lock, or an array of prefs to lock
*/
Preferences.lock = function(prefName) {
if (Array.isArray(prefName))
prefName.map(this.lock, this);
this._prefBranch.lockPref(prefName);
};
/**
* Unlock a pref so it can be changed.
*
* @param prefName {String|Array}
* the pref to lock, or an array of prefs to lock
*/
Preferences.unlock = function(prefName) {
if (Array.isArray(prefName))
prefName.map(this.unlock, this);
this._prefBranch.unlockPref(prefName);
};
/**
* Whether or not the given pref is locked against changes.
*
* @param prefName {String|Array}
* the pref to check, or an array of prefs to check
*
* @returns {Boolean|Array}
* whether or not the pref has a user-set value; or, if the caller
* provided an array of pref names, an array of booleans indicating
* whether or not the prefs have user-set values
*/
Preferences.locked = function(prefName) {
if (Array.isArray(prefName))
return prefName.map(this.locked, this);
return this._prefBranch.prefIsLocked(prefName);
};
/**
* Start observing a pref.
*
* The callback can be a function or any object that implements nsIObserver.
* When the callback is a function and thisObject is provided, it gets called
* as a method of thisObject.
*
* @param prefName {String}
* the name of the pref to observe
*
* @param callback {Function|Object}
* the code to notify when the pref changes;
*
* @param thisObject {Object} [optional]
* the object to use as |this| when calling a Function callback;
*
* @returns the wrapped observer
*/
Preferences.observe = function(prefName, callback, thisObject) {
let fullPrefName = this._branchStr + (prefName || "");
let observer = new PrefObserver(fullPrefName, callback, thisObject);
Preferences._prefBranch.addObserver(fullPrefName, observer, true);
observers.push(observer);
return observer;
};
/**
* Stop observing a pref.
*
* You must call this method with the same prefName, callback, and thisObject
* with which you originally registered the observer. However, you don't have
* to call this method on the same exact instance of Preferences; you can call
* it on any instance. For example, the following code first starts and then
* stops observing the "foo.bar.baz" preference:
*
* let observer = function() {...};
* Preferences.observe("foo.bar.baz", observer);
* new Preferences("foo.bar.").ignore("baz", observer);
*
* @param prefName {String}
* the name of the pref being observed
*
* @param callback {Function|Object}
* the code being notified when the pref changes
*
* @param thisObject {Object} [optional]
* the object being used as |this| when calling a Function callback
*/
Preferences.ignore = function(prefName, callback, thisObject) {
let fullPrefName = this._branchStr + (prefName || "");
// This seems fairly inefficient, but I'm not sure how much better we can
// make it. We could index by fullBranch, but we can't index by callback
// or thisObject, as far as I know, since the keys to JavaScript hashes
// (a.k.a. objects) can apparently only be primitive values.
let [observer] = observers.filter(v => v.prefName == fullPrefName &&
v.callback == callback &&
v.thisObject == thisObject);
if (observer) {
Preferences._prefBranch.removeObserver(fullPrefName, observer);
observers.splice(observers.indexOf(observer), 1);
}
};
Preferences.resetBranch = function(prefBranch = "") {
try {
this._prefBranch.resetBranch(prefBranch);
}
catch(ex) {
// The current implementation of nsIPrefBranch in Mozilla
// doesn't implement resetBranch, so we do it ourselves.
if (ex.result == Cr.NS_ERROR_NOT_IMPLEMENTED)
this.reset(this._prefBranch.getChildList(prefBranch, []));
else
throw ex;
}
},
/**
* A string identifying the branch of the preferences tree to which this
* instance provides access.
* @private
*/
Preferences._branchStr = "";
/**
* The cached preferences branch object this instance encapsulates, or null.
* Do not use! Use _prefBranch below instead.
* @private
*/
Preferences._cachedPrefBranch = null;
/**
* The preferences branch object for this instance.
* @private
*/
Object.defineProperty(Preferences, "_prefBranch",
{
get: function _prefBranch() {
if (!this._cachedPrefBranch) {
let prefSvc = Services.prefs;
this._cachedPrefBranch = this._defaultBranch ?
prefSvc.getDefaultBranch(this._branchStr) :
prefSvc.getBranch(this._branchStr);
}
return this._cachedPrefBranch;
},
enumerable: true,
configurable: true
});
// Constructor-based access (Preferences.get(...) and set) is preferred over
// instance-based access (new Preferences().get(...) and set) and when using the
// root preferences branch, as it's desirable not to allocate the extra object.
// But both forms are acceptable.
Preferences.prototype = Preferences;
/**
* A cache of pref observers.
*
* We use this to remove observers when a caller calls Preferences::ignore.
*
* All Preferences instances share this object, because we want callers to be
* able to remove an observer using a different Preferences object than the one
* with which they added it. That means we have to identify the observers
* in this object by their complete pref name, not just their name relative to
* the root branch of the Preferences object with which they were created.
*/
var observers = [];
function PrefObserver(prefName, callback, thisObject) {
this.prefName = prefName;
this.callback = callback;
this.thisObject = thisObject;
}
PrefObserver.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe: function(subject, topic, data) {
// The pref service only observes whole branches, but we only observe
// individual preferences, so we check here that the pref that changed
// is the exact one we're observing (and not some sub-pref on the branch).
if (data.indexOf(this.prefName) != 0)
return;
if (typeof this.callback == "function") {
let prefValue = Preferences.get(data);
if (this.thisObject)
this.callback.call(this.thisObject, prefValue);
else
this.callback(prefValue);
}
else // typeof this.callback == "object" (nsIObserver)
this.callback.observe(subject, topic, data);
}
};
function isObject(val) {
// We can't check for |val.constructor == Object| here, since the value
// might be from a different context whose Object constructor is not the same
// as ours, so instead we match based on the name of the constructor.
return (typeof val != "undefined" && val != null && typeof val == "object" &&
val.constructor.name == "Object");
}