michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: // The minimum and maximum integers that can be set as preferences. michael@0: // The range of valid values is narrower than the range of valid JS values michael@0: // because the native preferences code treats integers as NSPR PRInt32s, michael@0: // which are 32-bit signed integers on all platforms. michael@0: const MAX_INT = 0x7FFFFFFF; michael@0: const MIN_INT = -0x80000000; michael@0: michael@0: const {Cc,Ci,Cr} = require("chrome"); michael@0: michael@0: const prefService = Cc["@mozilla.org/preferences-service;1"]. michael@0: getService(Ci.nsIPrefService); michael@0: const prefSvc = prefService.getBranch(null); michael@0: const defaultBranch = prefService.getDefaultBranch(null); michael@0: michael@0: function Branch(branchName) { michael@0: function getPrefKeys() { michael@0: return keys(branchName).map(function(key) { michael@0: return key.replace(branchName, ""); michael@0: }); michael@0: } michael@0: michael@0: return Proxy.create({ michael@0: get: function(receiver, pref) { michael@0: return get(branchName + pref); michael@0: }, michael@0: set: function(receiver, pref, val) { michael@0: set(branchName + pref, val); michael@0: }, michael@0: delete: function(pref) { michael@0: reset(branchName + pref); michael@0: return true; michael@0: }, michael@0: has: function hasPrefKey(pref) { michael@0: return has(branchName + pref) michael@0: }, michael@0: getPropertyDescriptor: function(name) { michael@0: return { michael@0: value: get(branchName + name) michael@0: }; michael@0: }, michael@0: enumerate: getPrefKeys, michael@0: keys: getPrefKeys michael@0: }, Branch.prototype); michael@0: } michael@0: michael@0: function get(name, defaultValue) { michael@0: switch (prefSvc.getPrefType(name)) { michael@0: case Ci.nsIPrefBranch.PREF_STRING: michael@0: return prefSvc.getComplexValue(name, Ci.nsISupportsString).data; michael@0: michael@0: case Ci.nsIPrefBranch.PREF_INT: michael@0: return prefSvc.getIntPref(name); michael@0: michael@0: case Ci.nsIPrefBranch.PREF_BOOL: michael@0: return prefSvc.getBoolPref(name); michael@0: michael@0: case Ci.nsIPrefBranch.PREF_INVALID: michael@0: return defaultValue; michael@0: michael@0: default: michael@0: // This should never happen. michael@0: throw new Error("Error getting pref " + name + michael@0: "; its value's type is " + michael@0: prefSvc.getPrefType(name) + michael@0: ", which I don't know " + michael@0: "how to handle."); michael@0: } michael@0: } michael@0: exports.get = get; michael@0: michael@0: function set(name, value) { michael@0: var prefType; michael@0: if (typeof value != "undefined" && value != null) michael@0: prefType = value.constructor.name; michael@0: michael@0: switch (prefType) { michael@0: case "String": michael@0: { michael@0: var string = Cc["@mozilla.org/supports-string;1"]. michael@0: createInstance(Ci.nsISupportsString); michael@0: string.data = value; michael@0: prefSvc.setComplexValue(name, Ci.nsISupportsString, string); michael@0: } michael@0: break; michael@0: michael@0: case "Number": michael@0: // We throw if the number is outside the range or not an integer, since michael@0: // the result will not be what the consumer wanted to store. michael@0: if (value > MAX_INT || value < MIN_INT) michael@0: throw new Error("you cannot set the " + name + michael@0: " pref to the number " + value + michael@0: ", as number pref values must be in the signed " + michael@0: "32-bit integer range -(2^31) to 2^31-1. " + michael@0: "To store numbers outside that range, store " + michael@0: "them as strings."); michael@0: if (value % 1 != 0) michael@0: throw new Error("cannot store non-integer number: " + value); michael@0: prefSvc.setIntPref(name, value); michael@0: break; michael@0: michael@0: case "Boolean": michael@0: prefSvc.setBoolPref(name, value); michael@0: break; michael@0: michael@0: default: michael@0: throw new Error("can't set pref " + name + " to value '" + value + michael@0: "'; it isn't a string, integer, or boolean"); michael@0: } michael@0: } michael@0: exports.set = set; michael@0: michael@0: function has(name) { michael@0: return (prefSvc.getPrefType(name) != Ci.nsIPrefBranch.PREF_INVALID); michael@0: } michael@0: exports.has = has; michael@0: michael@0: function keys(root) { michael@0: return prefSvc.getChildList(root); michael@0: } michael@0: exports.keys = keys; michael@0: michael@0: function isSet(name) { michael@0: return (has(name) && prefSvc.prefHasUserValue(name)); michael@0: } michael@0: exports.isSet = isSet; michael@0: michael@0: function reset(name) { michael@0: try { michael@0: prefSvc.clearUserPref(name); michael@0: } michael@0: catch (e) { michael@0: // The pref service throws NS_ERROR_UNEXPECTED when the caller tries michael@0: // to reset a pref that doesn't exist or is already set to its default michael@0: // value. This interface fails silently in those cases, so callers michael@0: // can unconditionally reset a pref without having to check if it needs michael@0: // resetting first or trap exceptions after the fact. It passes through michael@0: // other exceptions, however, so callers know about them, since we don't michael@0: // know what other exceptions might be thrown and what they might mean. michael@0: if (e.result != Cr.NS_ERROR_UNEXPECTED) { michael@0: throw e; michael@0: } michael@0: } michael@0: } michael@0: exports.reset = reset; michael@0: michael@0: function getLocalized(name, defaultValue) { michael@0: let value = null; michael@0: try { michael@0: value = prefSvc.getComplexValue(name, Ci.nsIPrefLocalizedString).data; michael@0: } michael@0: finally { michael@0: return value || defaultValue; michael@0: } michael@0: } michael@0: exports.getLocalized = getLocalized; michael@0: michael@0: function setLocalized(name, value) { michael@0: // We can't use `prefs.set` here as we have to use `getDefaultBranch` michael@0: // (instead of `getBranch`) in order to have `mIsDefault` set to true, here: michael@0: // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#233 michael@0: // Otherwise, we do not enter into this expected condition: michael@0: // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#244 michael@0: defaultBranch.setCharPref(name, value); michael@0: } michael@0: exports.setLocalized = setLocalized; michael@0: michael@0: exports.Branch = Branch; michael@0: