Bug 1381297 - Store the installDate of an extension as a number in the extension-settings.json file, r?aswan
MozReview-Commit-ID: 1P7hy23Yyk6
--- a/toolkit/components/extensions/ExtensionSettingsStore.jsm
+++ b/toolkit/components/extensions/ExtensionSettingsStore.jsm
@@ -18,17 +18,17 @@
*
* {
* type: { // The type of settings being stored in this object, i.e., prefs.
* key: { // The unique key for the setting.
* initialValue, // The initial value of the setting.
* precedenceList: [
* {
* id, // The id of the extension requesting the setting.
- * installDate, // The install date of the extension.
+ * installDate, // The install date of the extension, stored as a number.
* value, // The value of the setting requested by the extension.
* enabled // Whether the setting is currently enabled.
* }
* ],
* },
* key: {
* // ...
* }
@@ -48,25 +48,52 @@ Cu.import("resource://gre/modules/Servic
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
const JSON_FILE_NAME = "extension-settings.json";
+const JSON_FILE_VERSION = 2;
const STORE_PATH = OS.Path.join(Services.dirsvc.get("ProfD", Ci.nsIFile).path, JSON_FILE_NAME);
let _store;
+// Test-only method to force reloading of the JSON file, by removing the
+// cached file from memory.
+function clearFileFromCache() {
+ let finalizePromise = _store.finalize();
+ _store = null;
+ return finalizePromise;
+}
+
+// Processes the JSON data when read from disk to convert string dates into numbers.
+function dataPostProcessor(json) {
+ if (json.version !== JSON_FILE_VERSION) {
+ for (let storeType in json) {
+ for (let setting in json[storeType]) {
+ for (let extData of json[storeType][setting].precedenceList) {
+ if (typeof extData.installDate != "number") {
+ extData.installDate = new Date(extData.installDate).valueOf();
+ }
+ }
+ }
+ }
+ json.version = JSON_FILE_VERSION;
+ }
+ return json;
+}
+
// Get the internal settings store, which is persisted in a JSON file.
function getStore(type) {
if (!_store) {
let initStore = new JSONFile({
path: STORE_PATH,
+ dataPostProcessor,
});
initStore.ensureDataReady();
_store = initStore;
}
// Ensure a property exists for the given type.
if (!_store.data[type]) {
_store.data[type] = {};
@@ -227,17 +254,18 @@ this.ExtensionSettingsStore = {
};
}
let keyInfo = store.data[type][key];
// Check for this item in the precedenceList.
let foundIndex = keyInfo.precedenceList.findIndex(item => item.id == id);
if (foundIndex === -1) {
// No item for this extension, so add a new one.
let addon = await AddonManager.getAddonByID(id);
- keyInfo.precedenceList.push({id, installDate: addon.installDate, value, enabled: true});
+ keyInfo.precedenceList.push(
+ {id, installDate: addon.installDate.valueOf(), value, enabled: true});
} else {
// Item already exists or this extension, so update it.
keyInfo.precedenceList[foundIndex].value = value;
}
// Sort the list.
keyInfo.precedenceList.sort(precedenceComparator);
@@ -396,13 +424,26 @@ this.ExtensionSettingsStore = {
}
let topItem = enabledItems[0];
if (topItem.id == id) {
return "controlled_by_this_extension";
}
let addon = await AddonManager.getAddonByID(id);
- return topItem.installDate > addon.installDate ?
+ return topItem.installDate > addon.installDate.valueOf() ?
"controlled_by_other_extensions" :
"controllable_by_this_extension";
},
+
+ /**
+ * Test-only method to force reloading of the JSON file.
+ *
+ * Note that this method simply clears the local variable that stores the
+ * file, so the next time the file is accessed it will be reloaded.
+ *
+ * @returns {Promise}
+ * A promise that resolves once the settings store has been cleared.
+ */
+ _reloadFile() {
+ return clearFileFromCache();
+ },
};
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
@@ -118,16 +118,19 @@ add_task(async function test_settings_st
"getSetting returns correct item with more than one item in the list.");
let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(extensions[extensionIndex], TEST_TYPE, key);
equal(
levelOfControl,
"controlled_by_other_extensions",
"getLevelOfControl returns correct levelOfControl when another extension is in control.");
}
+ // Reload the settings store to emulate a browser restart.
+ await ExtensionSettingsStore._reloadFile();
+
// Add a setting for the newest extension.
for (let key of KEY_LIST) {
let extensionIndex = 2;
let itemToAdd = ITEMS[key][extensionIndex];
let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(extensions[extensionIndex], TEST_TYPE, key);
equal(
levelOfControl,
"controllable_by_this_extension",