Bug 1252871 - Add a utility for managing extension preferences r?aswan
MozReview-Commit-ID: FZT2R3SadA1
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -47,18 +47,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
- "resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "require",
"resource://devtools/shared/Loader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/ExtensionContent.jsm");
@@ -77,16 +75,17 @@ let schemaURLs = new Set();
if (!AppConstants.RELEASE_OR_BETA) {
schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
}
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
BaseContext,
EventEmitter,
+ ExtensionPreferences,
SchemaAPIManager,
LocaleData,
instanceOf,
LocalAPIImplementation,
flushJarCache,
} = ExtensionUtils;
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
@@ -562,52 +561,32 @@ ParentAPIManager.init();
// All moz-extension URIs use a machine-specific UUID rather than the
// extension's own ID in the host component. This makes it more
// difficult for web pages to detect whether a user has a given add-on
// installed (by trying to load a moz-extension URI referring to a
// web_accessible_resource from the extension). UUIDMap.get()
// returns the UUID for a given add-on ID.
var UUIDMap = {
- _read() {
- let pref = Preferences.get(UUID_MAP_PREF, "{}");
- try {
- return JSON.parse(pref);
- } catch (e) {
- Cu.reportError(`Error parsing ${UUID_MAP_PREF}.`);
- return {};
+ get(id, create = true) {
+ if (ExtensionPreferences.has(id, UUID_MAP_PREF)) {
+ return ExtensionPreferences.get(id, UUID_MAP_PREF);
+ } else if (create) {
+ // Create a new UUID and strip off the outer curly brackets.
+ let uuid = uuidGen.generateUUID().number.slice(1, -1);
+ ExtensionPreferences.set(id, UUID_MAP_PREF, uuid);
+ return uuid;
}
- },
-
- _write(map) {
- Preferences.set(UUID_MAP_PREF, JSON.stringify(map));
- },
-
- get(id, create = true) {
- let map = this._read();
-
- if (id in map) {
- return map[id];
- }
-
- let uuid = null;
- if (create) {
- uuid = uuidGen.generateUUID().number;
- uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
-
- map[id] = uuid;
- this._write(map);
- }
- return uuid;
+ return null;
},
remove(id) {
- let map = this._read();
- delete map[id];
- this._write(map);
+ if (ExtensionPreferences.has(id, UUID_MAP_PREF)) {
+ ExtensionPreferences.delete(id, UUID_MAP_PREF);
+ }
},
};
// This is the old interface that UUIDMap replaced, to be removed when
// the references listed in bug 1291399 are updated.
/* exported getExtensionUUID */
function getExtensionUUID(id) {
return UUIDMap.get(id, true);
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -894,16 +894,75 @@ LocaleData.prototype = {
get uiLocale() {
// Return the browser locale, but convert it to a Chrome-style
// locale code.
return Locale.getLocale().replace(/-/g, "_");
},
};
+/**
+ * Utility for managing extension preferences.
+ *
+ * For each preference, a map is stored associating
+ * extension IDs to preferences.
+ */
+class ExtensionPreferences {
+ /**
+ * Retrieves the preferences for the specified extension id.
+ *
+ * @param {string} id The ID of the extension.
+ * @param {string} pref The preference to look up.
+ * @returns {*} the JSON serializable preferences stored for the extension.
+ */
+ static get(id, pref) {
+ let value = Preferences.get(pref, "[]");
+ let map = new Map(JSON.parse(value));
+ return map.get(id);
+ }
+
+ /**
+ * Checks if preference exists for the specified extension id.
+ *
+ * @param {string} id The ID of the extension.
+ * @param {string} pref The preference to check.
+ * @returns {boolean} true if the extension has preferences stored.
+ */
+ static has(id, pref) {
+ let value = Preferences.get(pref, "[]");
+ let map = new Map(JSON.parse(value));
+ return map.has(id);
+ }
+
+ /**
+ * Stores JSON serializable preferences for the specified extension id.
+ *
+ * @param {string} id The ID of the extension.
+ * @param {string} pref The preference to update.
+ * @param {*} prefs The JSON serializable preferences to store.
+ */
+ static set(id, pref, prefs) {
+ let map = new Map(JSON.parse(Preferences.get(pref, "[]")));
+ map.set(id, prefs);
+ Preferences.set(pref, JSON.stringify([...map]));
+ }
+
+ /**
+ * Deletes the preferences for the specified extension id.
+ *
+ * @param {string} id The ID of the extension.
+ * @param {string} pref The preference to delete.
+ */
+ static delete(id, pref) {
+ let map = new Map(JSON.parse(Preferences.get(pref, "[]")));
+ map.delete(id);
+ Preferences.set(pref, JSON.stringify([...map]));
+ }
+}
+
// This is a generic class for managing event listeners. Example usage:
//
// new EventManager(context, "api.subAPI", fire => {
// let listener = (...) => {
// // Fire any listeners registered with addListener.
// fire(arg1, arg2);
// };
// // Register the listener.
@@ -2113,16 +2172,17 @@ this.ExtensionUtils = {
runSafeSync,
runSafeSyncWithoutClone,
runSafeWithoutClone,
stylesheetMap,
BaseContext,
DefaultWeakMap,
EventEmitter,
EventManager,
+ ExtensionPreferences,
IconDetails,
LocalAPIImplementation,
LocaleData,
Messenger,
PlatformInfo,
SchemaAPIInterface,
SingletonEventManager,
SpreadArgs,