summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/addrbook/modules/AddrBookDirectory.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/addrbook/modules/AddrBookDirectory.jsm')
-rw-r--r--comm/mailnews/addrbook/modules/AddrBookDirectory.jsm817
1 files changed, 817 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/modules/AddrBookDirectory.jsm b/comm/mailnews/addrbook/modules/AddrBookDirectory.jsm
new file mode 100644
index 0000000000..b35f35b147
--- /dev/null
+++ b/comm/mailnews/addrbook/modules/AddrBookDirectory.jsm
@@ -0,0 +1,817 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["AddrBookDirectory"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+const lazy = {};
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ AddrBookCard: "resource:///modules/AddrBookCard.jsm",
+ AddrBookMailingList: "resource:///modules/AddrBookMailingList.jsm",
+ BANISHED_PROPERTIES: "resource:///modules/VCardUtils.jsm",
+ compareAddressBooks: "resource:///modules/AddrBookUtils.jsm",
+ newUID: "resource:///modules/AddrBookUtils.jsm",
+ VCardProperties: "resource:///modules/VCardUtils.jsm",
+});
+
+/**
+ * Abstract base class implementing nsIAbDirectory.
+ *
+ * @abstract
+ * @implements {nsIAbDirectory}
+ */
+class AddrBookDirectory {
+ QueryInterface = ChromeUtils.generateQI(["nsIAbDirectory"]);
+
+ constructor() {
+ this._uid = null;
+ this._dirName = null;
+ }
+
+ _initialized = false;
+ init(uri) {
+ if (this._initialized) {
+ throw new Components.Exception(
+ `Directory already initialized: ${uri}`,
+ Cr.NS_ERROR_ALREADY_INITIALIZED
+ );
+ }
+
+ // If this._readOnly is true, the user is prevented from making changes to
+ // the contacts. Subclasses may override this (for example to sync with a
+ // server) by setting this._overrideReadOnly to true, but must clear it
+ // before yielding to another thread (e.g. awaiting a Promise).
+
+ if (this._dirPrefId) {
+ XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "_readOnly",
+ `${this.dirPrefId}.readOnly`,
+ false
+ );
+ }
+
+ this._initialized = true;
+ }
+ async cleanUp() {
+ if (!this._initialized) {
+ throw new Components.Exception(
+ "Directory not initialized",
+ Cr.NS_ERROR_NOT_INITIALIZED
+ );
+ }
+ }
+
+ get _prefBranch() {
+ if (this.__prefBranch) {
+ return this.__prefBranch;
+ }
+ if (!this._dirPrefId) {
+ throw Components.Exception("No dirPrefId!", Cr.NS_ERROR_NOT_AVAILABLE);
+ }
+ return (this.__prefBranch = Services.prefs.getBranch(
+ `${this._dirPrefId}.`
+ ));
+ }
+ /** @abstract */
+ get lists() {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement lists getter.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ get cards() {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement cards getter.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+
+ getCard(uid) {
+ let card = new lazy.AddrBookCard();
+ card.directoryUID = this.UID;
+ card._uid = uid;
+ card._properties = this.loadCardProperties(uid);
+ card._isGoogleCardDAV = this._isGoogleCardDAV;
+ return card.QueryInterface(Ci.nsIAbCard);
+ }
+ /** @abstract */
+ loadCardProperties(uid) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement loadCardProperties.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ saveCardProperties(uid, properties) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement saveCardProperties.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ deleteCard(uid) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement deleteCard.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ saveList(list) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement saveList.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ deleteList(uid) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement deleteList.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /**
+ * Create a Map of the properties to record when saving `card`, including
+ * any changes we want to make just before saving.
+ *
+ * @param {nsIAbCard} card
+ * @param {?string} uid
+ * @returns {Map<string, string>}
+ */
+ prepareToSaveCard(card, uid) {
+ let propertyMap = new Map(
+ Array.from(card.properties, p => [p.name, p.value])
+ );
+ let newProperties = new Map();
+
+ // Get a VCardProperties object for the card.
+ let vCardProperties;
+ if (card.supportsVCard) {
+ vCardProperties = card.vCardProperties;
+ } else {
+ vCardProperties = lazy.VCardProperties.fromPropertyMap(propertyMap);
+ }
+
+ if (uid) {
+ // Force the UID to be as passed.
+ vCardProperties.clearValues("uid");
+ vCardProperties.addValue("uid", uid);
+ } else if (vCardProperties.getFirstValue("uid") != card.UID) {
+ vCardProperties.clearValues("uid");
+ vCardProperties.addValue("uid", card.UID);
+ }
+
+ // Collect only the properties we intend to keep.
+ for (let [name, value] of propertyMap) {
+ if (lazy.BANISHED_PROPERTIES.includes(name)) {
+ continue;
+ }
+ if (value !== null && value !== undefined && value !== "") {
+ newProperties.set(name, value);
+ }
+ }
+
+ // Add the vCard and the properties from it we want to cache.
+ newProperties.set("_vCard", vCardProperties.toVCard());
+
+ let displayName = vCardProperties.getFirstValue("fn");
+ newProperties.set("DisplayName", displayName || "");
+
+ let flatten = value => {
+ if (Array.isArray(value)) {
+ return value.join(" ");
+ }
+ return value;
+ };
+
+ let name = vCardProperties.getFirstValue("n");
+ if (Array.isArray(name)) {
+ newProperties.set("FirstName", flatten(name[1]));
+ newProperties.set("LastName", flatten(name[0]));
+ }
+
+ let email = vCardProperties.getAllValuesSorted("email");
+ if (email[0]) {
+ newProperties.set("PrimaryEmail", email[0]);
+ }
+ if (email[1]) {
+ newProperties.set("SecondEmail", email[1]);
+ }
+
+ let nickname = vCardProperties.getFirstValue("nickname");
+ if (nickname) {
+ newProperties.set("NickName", flatten(nickname));
+ }
+
+ // Always set the last modified date.
+ newProperties.set("LastModifiedDate", "" + Math.floor(Date.now() / 1000));
+ return newProperties;
+ }
+
+ /* nsIAbDirectory */
+
+ get readOnly() {
+ return this._readOnly;
+ }
+ get isRemote() {
+ return false;
+ }
+ get isSecure() {
+ return false;
+ }
+ get propertiesChromeURI() {
+ return "chrome://messenger/content/addressbook/abAddressBookNameDialog.xhtml";
+ }
+ get dirPrefId() {
+ return this._dirPrefId;
+ }
+ get dirName() {
+ if (this._dirName === null) {
+ this._dirName = this.getLocalizedStringValue("description", "");
+ }
+ return this._dirName;
+ }
+ set dirName(value) {
+ this.setLocalizedStringValue("description", value);
+ this._dirName = value;
+ Services.obs.notifyObservers(this, "addrbook-directory-updated", "DirName");
+ }
+ get dirType() {
+ return Ci.nsIAbManager.JS_DIRECTORY_TYPE;
+ }
+ get fileName() {
+ return this._fileName;
+ }
+ get UID() {
+ if (!this._uid) {
+ if (this._prefBranch.getPrefType("uid") == Services.prefs.PREF_STRING) {
+ this._uid = this._prefBranch.getStringPref("uid");
+ } else {
+ this._uid = lazy.newUID();
+ this._prefBranch.setStringPref("uid", this._uid);
+ }
+ }
+ return this._uid;
+ }
+ get URI() {
+ return this._uri;
+ }
+ get position() {
+ return this._prefBranch.getIntPref("position", 1);
+ }
+ get childNodes() {
+ let lists = Array.from(
+ this.lists.values(),
+ list =>
+ new lazy.AddrBookMailingList(
+ list.uid,
+ this,
+ list.name,
+ list.nickName,
+ list.description
+ ).asDirectory
+ );
+ lists.sort(lazy.compareAddressBooks);
+ return lists;
+ }
+ /** @abstract */
+ get childCardCount() {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement childCardCount getter.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ get childCards() {
+ let results = Array.from(
+ this.lists.values(),
+ list =>
+ new lazy.AddrBookMailingList(
+ list.uid,
+ this,
+ list.name,
+ list.nickName,
+ list.description
+ ).asCard
+ ).concat(Array.from(this.cards.keys(), this.getCard, this));
+
+ return results;
+ }
+ get supportsMailingLists() {
+ return true;
+ }
+
+ search(query, string, listener) {
+ if (!listener) {
+ return;
+ }
+ if (!query) {
+ listener.onSearchFinished(Cr.NS_ERROR_FAILURE, true, null, "");
+ return;
+ }
+ if (query[0] == "?") {
+ query = query.substring(1);
+ }
+
+ let results = Array.from(
+ this.lists.values(),
+ list =>
+ new lazy.AddrBookMailingList(
+ list.uid,
+ this,
+ list.name,
+ list.nickName,
+ list.description
+ ).asCard
+ ).concat(Array.from(this.cards.keys(), this.getCard, this));
+
+ // Process the query string into a tree of conditions to match.
+ let lispRegexp = /^\((and|or|not|([^\)]*)(\)+))/;
+ let index = 0;
+ let rootQuery = { children: [], op: "or" };
+ let currentQuery = rootQuery;
+
+ while (true) {
+ let match = lispRegexp.exec(query.substring(index));
+ if (!match) {
+ break;
+ }
+ index += match[0].length;
+
+ if (["and", "or", "not"].includes(match[1])) {
+ // For the opening bracket, step down a level.
+ let child = {
+ parent: currentQuery,
+ children: [],
+ op: match[1],
+ };
+ currentQuery.children.push(child);
+ currentQuery = child;
+ } else {
+ let [name, condition, value] = match[2].split(",");
+ currentQuery.children.push({
+ name,
+ condition,
+ value: decodeURIComponent(value).toLowerCase(),
+ });
+
+ // For each closing bracket except the first, step up a level.
+ for (let i = match[3].length - 1; i > 0; i--) {
+ currentQuery = currentQuery.parent;
+ }
+ }
+ }
+
+ results = results.filter(card => {
+ let properties;
+ if (card.isMailList) {
+ properties = new Map([
+ ["DisplayName", card.displayName],
+ ["NickName", card.getProperty("NickName", "")],
+ ["Notes", card.getProperty("Notes", "")],
+ ]);
+ } else if (card._properties.has("_vCard")) {
+ try {
+ properties = card.vCardProperties.toPropertyMap();
+ } catch (ex) {
+ // Parsing failed. Skip the vCard and just use the other properties.
+ console.error(ex);
+ properties = new Map();
+ }
+ for (let [key, value] of card._properties) {
+ if (!properties.has(key)) {
+ properties.set(key, value);
+ }
+ }
+ } else {
+ properties = card._properties;
+ }
+ let matches = b => {
+ if ("condition" in b) {
+ let { name, condition, value } = b;
+ if (name == "IsMailList" && condition == "=") {
+ return card.isMailList == (value == "true");
+ }
+ let cardValue = properties.get(name);
+ if (!cardValue) {
+ return condition == "!ex";
+ }
+ if (condition == "ex") {
+ return true;
+ }
+
+ cardValue = cardValue.toLowerCase();
+ switch (condition) {
+ case "=":
+ return cardValue == value;
+ case "!=":
+ return cardValue != value;
+ case "lt":
+ return cardValue < value;
+ case "gt":
+ return cardValue > value;
+ case "bw":
+ return cardValue.startsWith(value);
+ case "ew":
+ return cardValue.endsWith(value);
+ case "c":
+ return cardValue.includes(value);
+ case "!c":
+ return !cardValue.includes(value);
+ case "~=":
+ case "regex":
+ default:
+ return false;
+ }
+ }
+ if (b.op == "or") {
+ return b.children.some(bb => matches(bb));
+ }
+ if (b.op == "and") {
+ return b.children.every(bb => matches(bb));
+ }
+ if (b.op == "not") {
+ return !matches(b.children[0]);
+ }
+ return false;
+ };
+
+ return matches(rootQuery);
+ }, this);
+
+ for (let card of results) {
+ listener.onSearchFoundCard(card);
+ }
+ listener.onSearchFinished(Cr.NS_OK, true, null, "");
+ }
+ generateName(generateFormat, bundle) {
+ return this.dirName;
+ }
+ cardForEmailAddress(emailAddress) {
+ if (!emailAddress) {
+ return null;
+ }
+
+ // Check the properties. We copy the first two addresses to properties for
+ // this purpose, so it should be fast.
+ let card = this.getCardFromProperty("PrimaryEmail", emailAddress, false);
+ if (card) {
+ return card;
+ }
+ card = this.getCardFromProperty("SecondEmail", emailAddress, false);
+ if (card) {
+ return card;
+ }
+
+ // Nothing so far? Go through all the cards checking all of the addresses.
+ // This could be slow.
+ emailAddress = emailAddress.toLowerCase();
+ for (let [uid, properties] of this.cards) {
+ let vCard = properties.get("_vCard");
+ // If the vCard string doesn't include the email address, the parsed
+ // vCard won't include it either, so don't waste time parsing it.
+ if (!vCard?.toLowerCase().includes(emailAddress)) {
+ continue;
+ }
+ card = this.getCard(uid);
+ if (card.emailAddresses.some(e => e.toLowerCase() == emailAddress)) {
+ return card;
+ }
+ }
+
+ return null;
+ }
+ /** @abstract */
+ getCardFromProperty(property, value, caseSensitive) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement getCardFromProperty.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ /** @abstract */
+ getCardsFromProperty(property, value, caseSensitive) {
+ throw new Components.Exception(
+ `${this.constructor.name} does not implement getCardsFromProperty.`,
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ getMailListFromName(name) {
+ for (let list of this.lists.values()) {
+ if (list.name.toLowerCase() == name.toLowerCase()) {
+ return new lazy.AddrBookMailingList(
+ list.uid,
+ this,
+ list.name,
+ list.nickName,
+ list.description
+ ).asDirectory;
+ }
+ }
+ return null;
+ }
+ deleteDirectory(directory) {
+ if (this._readOnly) {
+ throw new Components.Exception(
+ "Directory is read-only",
+ Cr.NS_ERROR_FAILURE
+ );
+ }
+
+ let list = this.lists.get(directory.UID);
+ list = new lazy.AddrBookMailingList(
+ list.uid,
+ this,
+ list.name,
+ list.nickName,
+ list.description
+ );
+
+ this.deleteList(directory.UID);
+
+ Services.obs.notifyObservers(
+ list.asDirectory,
+ "addrbook-list-deleted",
+ this.UID
+ );
+ }
+ hasCard(card) {
+ return this.lists.has(card.UID) || this.cards.has(card.UID);
+ }
+ hasDirectory(dir) {
+ return this.lists.has(dir.UID);
+ }
+ hasMailListWithName(name) {
+ return this.getMailListFromName(name) != null;
+ }
+ addCard(card) {
+ return this.dropCard(card, false);
+ }
+ modifyCard(card) {
+ if (this._readOnly && !this._overrideReadOnly) {
+ throw new Components.Exception(
+ "Directory is read-only",
+ Cr.NS_ERROR_FAILURE
+ );
+ }
+
+ let oldProperties = this.loadCardProperties(card.UID);
+ let newProperties = this.prepareToSaveCard(card);
+
+ let allProperties = new Set(oldProperties.keys());
+ for (let key of newProperties.keys()) {
+ allProperties.add(key);
+ }
+
+ if (this.hasOwnProperty("cards")) {
+ this.cards.set(card.UID, newProperties);
+ }
+ this.saveCardProperties(card.UID, newProperties);
+
+ let changeData = {};
+ for (let name of allProperties) {
+ if (name == "LastModifiedDate") {
+ continue;
+ }
+
+ let oldValue = oldProperties.get(name) || null;
+ let newValue = newProperties.get(name) || null;
+ if (oldValue != newValue) {
+ changeData[name] = { oldValue, newValue };
+ }
+ }
+
+ // Increment this preference if one or both of these properties change.
+ // This will cause the UI to throw away cached values.
+ if ("DisplayName" in changeData || "PreferDisplayName" in changeData) {
+ Services.prefs.setIntPref(
+ "mail.displayname.version",
+ Services.prefs.getIntPref("mail.displayname.version", 0) + 1
+ );
+ }
+
+ // Send the card as it is in this directory, not as passed to this function.
+ let newCard = this.getCard(card.UID);
+ Services.obs.notifyObservers(newCard, "addrbook-contact-updated", this.UID);
+
+ Services.obs.notifyObservers(
+ newCard,
+ "addrbook-contact-properties-updated",
+ JSON.stringify(changeData)
+ );
+
+ // Return the card, even though the interface says not to, because
+ // subclasses may want it.
+ return newCard;
+ }
+ deleteCards(cards) {
+ if (this._readOnly && !this._overrideReadOnly) {
+ throw new Components.Exception(
+ "Directory is read-only",
+ Cr.NS_ERROR_FAILURE
+ );
+ }
+
+ if (cards === null) {
+ throw Components.Exception("", Cr.NS_ERROR_INVALID_POINTER);
+ }
+
+ let updateDisplayNameVersion = false;
+ for (let card of cards) {
+ updateDisplayNameVersion = updateDisplayNameVersion || card.displayName;
+ // TODO: delete photo if there is one
+ this.deleteCard(card.UID);
+ if (this.hasOwnProperty("cards")) {
+ this.cards.delete(card.UID);
+ }
+ }
+
+ // Increment this preference if one or more cards has a display name.
+ // This will cause the UI to throw away cached values.
+ if (updateDisplayNameVersion) {
+ Services.prefs.setIntPref(
+ "mail.displayname.version",
+ Services.prefs.getIntPref("mail.displayname.version", 0) + 1
+ );
+ }
+
+ for (let card of cards) {
+ Services.obs.notifyObservers(card, "addrbook-contact-deleted", this.UID);
+ card.directoryUID = null;
+ }
+
+ // We could just delete all non-existent cards from list_cards, but a
+ // notification should be fired for each one. Let the list handle that.
+ for (let list of this.childNodes) {
+ list.deleteCards(cards);
+ }
+ }
+ dropCard(card, needToCopyCard) {
+ if (this._readOnly && !this._overrideReadOnly) {
+ throw new Components.Exception(
+ "Directory is read-only",
+ Cr.NS_ERROR_FAILURE
+ );
+ }
+
+ if (!card.UID) {
+ throw new Error("Card must have a UID to be added to this directory.");
+ }
+
+ let uid = needToCopyCard ? lazy.newUID() : card.UID;
+ let newProperties = this.prepareToSaveCard(card, uid);
+ if (card.directoryUID && card.directoryUID != this._uid) {
+ // These properties belong to a different directory. Don't keep them.
+ newProperties.delete("_etag");
+ newProperties.delete("_href");
+ }
+
+ if (this.hasOwnProperty("cards")) {
+ this.cards.set(uid, newProperties);
+ }
+ this.saveCardProperties(uid, newProperties);
+
+ // Increment this preference if the card has a display name.
+ // This will cause the UI to throw away cached values.
+ if (card.displayName) {
+ Services.prefs.setIntPref(
+ "mail.displayname.version",
+ Services.prefs.getIntPref("mail.displayname.version", 0) + 1
+ );
+ }
+
+ let newCard = this.getCard(uid);
+ Services.obs.notifyObservers(newCard, "addrbook-contact-created", this.UID);
+ return newCard;
+ }
+ useForAutocomplete(identityKey) {
+ return (
+ Services.prefs.getBoolPref("mail.enable_autocomplete") &&
+ this.getBoolValue("enable_autocomplete", true)
+ );
+ }
+ addMailList(list) {
+ if (this._readOnly) {
+ throw new Components.Exception(
+ "Directory is read-only",
+ Cr.NS_ERROR_FAILURE
+ );
+ }
+
+ if (!list.isMailList) {
+ throw Components.Exception(
+ "Can't add; not a mail list",
+ Cr.NS_ERROR_UNEXPECTED
+ );
+ }
+
+ // Check if the new name is empty.
+ if (!list.dirName) {
+ throw new Components.Exception(
+ `Mail list name must be set; list.dirName=${list.dirName}`,
+ Cr.NS_ERROR_ILLEGAL_VALUE
+ );
+ }
+
+ // Check if the new name contains 2 spaces.
+ if (list.dirName.match(" ")) {
+ throw new Components.Exception(
+ `Invalid mail list name: ${list.dirName}`,
+ Cr.NS_ERROR_ILLEGAL_VALUE
+ );
+ }
+
+ // Check if the new name contains the following special characters.
+ for (let char of ',;"<>') {
+ if (list.dirName.includes(char)) {
+ throw new Components.Exception(
+ `Invalid mail list name: ${list.dirName}`,
+ Cr.NS_ERROR_ILLEGAL_VALUE
+ );
+ }
+ }
+
+ let newList = new lazy.AddrBookMailingList(
+ lazy.newUID(),
+ this,
+ list.dirName || "",
+ list.listNickName || "",
+ list.description || ""
+ );
+ this.saveList(newList);
+
+ let newListDirectory = newList.asDirectory;
+ Services.obs.notifyObservers(
+ newListDirectory,
+ "addrbook-list-created",
+ this.UID
+ );
+ return newListDirectory;
+ }
+ editMailListToDatabase(listCard) {
+ // Deliberately not implemented, this isn't a mailing list.
+ throw Components.Exception(
+ "editMailListToDatabase not relevant here",
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ copyMailList(srcList) {
+ // Deliberately not implemented, this isn't a mailing list.
+ throw Components.Exception(
+ "copyMailList not relevant here",
+ Cr.NS_ERROR_NOT_IMPLEMENTED
+ );
+ }
+ getIntValue(name, defaultValue) {
+ return this._prefBranch
+ ? this._prefBranch.getIntPref(name, defaultValue)
+ : defaultValue;
+ }
+ getBoolValue(name, defaultValue) {
+ return this._prefBranch
+ ? this._prefBranch.getBoolPref(name, defaultValue)
+ : defaultValue;
+ }
+ getStringValue(name, defaultValue) {
+ return this._prefBranch
+ ? this._prefBranch.getStringPref(name, defaultValue)
+ : defaultValue;
+ }
+ getLocalizedStringValue(name, defaultValue) {
+ if (!this._prefBranch) {
+ return defaultValue;
+ }
+ if (this._prefBranch.getPrefType(name) == Ci.nsIPrefBranch.PREF_INVALID) {
+ return defaultValue;
+ }
+ try {
+ return this._prefBranch.getComplexValue(name, Ci.nsIPrefLocalizedString)
+ .data;
+ } catch (e) {
+ // getComplexValue doesn't work with autoconfig.
+ return this._prefBranch.getStringPref(name);
+ }
+ }
+ setIntValue(name, value) {
+ this._prefBranch.setIntPref(name, value);
+ }
+ setBoolValue(name, value) {
+ this._prefBranch.setBoolPref(name, value);
+ }
+ setStringValue(name, value) {
+ this._prefBranch.setStringPref(name, value);
+ }
+ setLocalizedStringValue(name, value) {
+ let valueLocal = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(
+ Ci.nsIPrefLocalizedString
+ );
+ valueLocal.data = value;
+ this._prefBranch.setComplexValue(
+ name,
+ Ci.nsIPrefLocalizedString,
+ valueLocal
+ );
+ }
+}