mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 40c48d65e382 (bug 853388)
This commit is contained in:
parent
7f5ce688b5
commit
d9d1ab46ea
@ -3218,8 +3218,9 @@ var XPIProvider = {
|
|||||||
// If the database needs to be updated then open it and then update it
|
// If the database needs to be updated then open it and then update it
|
||||||
// from the filesystem
|
// from the filesystem
|
||||||
if (updateDatabase || hasPendingChanges) {
|
if (updateDatabase || hasPendingChanges) {
|
||||||
XPIDatabase.syncLoadDB(false);
|
|
||||||
try {
|
try {
|
||||||
|
XPIDatabase.openConnection(false, true);
|
||||||
|
|
||||||
extensionListChanged = this.processFileChanges(state, manifests,
|
extensionListChanged = this.processFileChanges(state, manifests,
|
||||||
aAppChanged,
|
aAppChanged,
|
||||||
aOldAppVersion,
|
aOldAppVersion,
|
||||||
|
@ -20,8 +20,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
|
|||||||
"resource://gre/modules/DeferredSave.jsm");
|
"resource://gre/modules/DeferredSave.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
"resource://gre/modules/Promise.jsm");
|
"resource://gre/modules/Promise.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
|
||||||
"resource://gre/modules/osfile.jsm");
|
|
||||||
|
|
||||||
["LOG", "WARN", "ERROR"].forEach(function(aName) {
|
["LOG", "WARN", "ERROR"].forEach(function(aName) {
|
||||||
Object.defineProperty(this, aName, {
|
Object.defineProperty(this, aName, {
|
||||||
@ -160,20 +158,6 @@ function getRepositoryAddon(aAddon, aCallback) {
|
|||||||
AddonRepository.getCachedAddonByID(aAddon.id, completeAddon);
|
AddonRepository.getCachedAddonByID(aAddon.id, completeAddon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap an API-supplied function in an exception handler to make it safe to call
|
|
||||||
*/
|
|
||||||
function safeCallback(aCallback) {
|
|
||||||
return function(...aArgs) {
|
|
||||||
try {
|
|
||||||
aCallback.apply(null, aArgs);
|
|
||||||
}
|
|
||||||
catch(ex) {
|
|
||||||
WARN("XPI Database callback failed", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper method to asynchronously call a function on an array
|
* A helper method to asynchronously call a function on an array
|
||||||
* of objects, calling a callback when function(x) has been gathered
|
* of objects, calling a callback when function(x) has been gathered
|
||||||
@ -391,32 +375,6 @@ DBAddonInternal.prototype = {
|
|||||||
|
|
||||||
DBAddonInternal.prototype.__proto__ = AddonInternal.prototype;
|
DBAddonInternal.prototype.__proto__ = AddonInternal.prototype;
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal interface: find an addon from an already loaded addonDB
|
|
||||||
*/
|
|
||||||
function _findAddon(addonDB, aFilter) {
|
|
||||||
for (let [, addon] of addonDB) {
|
|
||||||
if (aFilter(addon)) {
|
|
||||||
return addon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal interface to get a filtered list of addons from a loaded addonDB
|
|
||||||
*/
|
|
||||||
function _filterDB(addonDB, aFilter) {
|
|
||||||
let addonList = [];
|
|
||||||
for (let [, addon] of addonDB) {
|
|
||||||
if (aFilter(addon)) {
|
|
||||||
addonList.push(addon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addonList;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.XPIDatabase = {
|
this.XPIDatabase = {
|
||||||
// true if the database connection has been opened
|
// true if the database connection has been opened
|
||||||
initialized: false,
|
initialized: false,
|
||||||
@ -452,12 +410,6 @@ this.XPIDatabase = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._deferredSave) {
|
|
||||||
this._deferredSave = new DeferredSave(this.jsonFile.path,
|
|
||||||
() => JSON.stringify(this),
|
|
||||||
ASYNC_SAVE_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
let promise = this._deferredSave.saveChanges();
|
let promise = this._deferredSave.saveChanges();
|
||||||
if (!this._schemaVersionSet) {
|
if (!this._schemaVersionSet) {
|
||||||
this._schemaVersionSet = true;
|
this._schemaVersionSet = true;
|
||||||
@ -477,8 +429,8 @@ this.XPIDatabase = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
flush: function() {
|
flush: function() {
|
||||||
// handle the "in memory only" and "saveChanges never called" cases
|
// handle the "in memory only" case
|
||||||
if (!this._deferredSave) {
|
if (this.lockedDatabase) {
|
||||||
let done = Promise.defer();
|
let done = Promise.defer();
|
||||||
done.resolve(0);
|
done.resolve(0);
|
||||||
return done.promise;
|
return done.promise;
|
||||||
@ -487,17 +439,19 @@ this.XPIDatabase = {
|
|||||||
return this._deferredSave.flush();
|
return this._deferredSave.flush();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get _deferredSave() {
|
||||||
|
delete this._deferredSave;
|
||||||
|
return this._deferredSave =
|
||||||
|
new DeferredSave(this.jsonFile.path, () => JSON.stringify(this),
|
||||||
|
ASYNC_SAVE_DELAY_MS);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the current internal state of the XPI addon database to JSON
|
* Converts the current internal state of the XPI addon database to JSON
|
||||||
*/
|
*/
|
||||||
toJSON: function() {
|
toJSON: function() {
|
||||||
if (!this.addonDB) {
|
|
||||||
// We never loaded the database?
|
|
||||||
throw new Error("Attempt to save database without loading it first");
|
|
||||||
}
|
|
||||||
|
|
||||||
let addons = [];
|
let addons = [];
|
||||||
for (let [, addon] of this.addonDB) {
|
for (let [key, addon] of this.addonDB) {
|
||||||
addons.push(addon);
|
addons.push(addon);
|
||||||
}
|
}
|
||||||
let toSave = {
|
let toSave = {
|
||||||
@ -535,7 +489,7 @@ this.XPIDatabase = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously opens and reads the database file, upgrading from old
|
* Opens and reads the database file, upgrading from old
|
||||||
* databases or making a new DB if needed.
|
* databases or making a new DB if needed.
|
||||||
*
|
*
|
||||||
* The possibilities, in order of priority, are:
|
* The possibilities, in order of priority, are:
|
||||||
@ -552,8 +506,10 @@ this.XPIDatabase = {
|
|||||||
* from the install locations if the database needs to be rebuilt.
|
* from the install locations if the database needs to be rebuilt.
|
||||||
* (if false, caller is XPIProvider.checkForChanges() which will rebuild)
|
* (if false, caller is XPIProvider.checkForChanges() which will rebuild)
|
||||||
*/
|
*/
|
||||||
syncLoadDB: function XPIDB_syncLoadDB(aRebuildOnError) {
|
openConnection: function XPIDB_openConnection(aRebuildOnError, aForceOpen) {
|
||||||
// XXX TELEMETRY report synchronous opens (startup time) vs. delayed opens
|
// XXX TELEMETRY report opens with aRebuildOnError true (which implies delayed open)
|
||||||
|
// vs. aRebuildOnError false (DB loaded during startup)
|
||||||
|
delete this.addonDB;
|
||||||
this.migrateData = null;
|
this.migrateData = null;
|
||||||
let fstream = null;
|
let fstream = null;
|
||||||
let data = "";
|
let data = "";
|
||||||
@ -574,10 +530,42 @@ this.XPIDatabase = {
|
|||||||
data += str.value;
|
data += str.value;
|
||||||
} while (read != 0);
|
} while (read != 0);
|
||||||
}
|
}
|
||||||
this.parseDB(data, aRebuildOnError);
|
// dump("Loaded JSON:\n" + data + "\n");
|
||||||
|
let inputAddons = JSON.parse(data);
|
||||||
|
// Now do some sanity checks on our JSON db
|
||||||
|
if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
|
||||||
|
// Content of JSON file is bad, need to rebuild from scratch
|
||||||
|
ERROR("bad JSON file contents");
|
||||||
|
this.rebuildDatabase(aRebuildOnError);
|
||||||
|
}
|
||||||
|
if (inputAddons.schemaVersion != DB_SCHEMA) {
|
||||||
|
// Handle mismatched JSON schema version. For now, we assume
|
||||||
|
// compatibility for JSON data, though we throw away any fields we
|
||||||
|
// don't know about
|
||||||
|
// XXX preserve unknown fields during save/restore
|
||||||
|
LOG("JSON schema mismatch: expected " + DB_SCHEMA +
|
||||||
|
", actual " + inputAddons.schemaVersion);
|
||||||
|
}
|
||||||
|
// If we got here, we probably have good data
|
||||||
|
// Make AddonInternal instances from the loaded data and save them
|
||||||
|
let addonDB = new Map();
|
||||||
|
inputAddons.addons.forEach(function(loadedAddon) {
|
||||||
|
let newAddon = new DBAddonInternal(loadedAddon);
|
||||||
|
addonDB.set(newAddon._key, newAddon);
|
||||||
|
});
|
||||||
|
this.addonDB = addonDB;
|
||||||
|
LOG("Successfully read XPI database");
|
||||||
|
this.initialized = true;
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
ERROR("Failed to load XPI JSON data from profile", e);
|
// If we catch and log a SyntaxError from the JSON
|
||||||
|
// parser, the xpcshell test harness fails the test for us: bug 870828
|
||||||
|
if (e.name == "SyntaxError") {
|
||||||
|
ERROR("Syntax error parsing saved XPI JSON data");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ERROR("Failed to load XPI JSON data from profile", e);
|
||||||
|
}
|
||||||
this.rebuildDatabase(aRebuildOnError);
|
this.rebuildDatabase(aRebuildOnError);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -587,151 +575,46 @@ this.XPIDatabase = {
|
|||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
|
if (e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
|
||||||
this.upgradeDB(aRebuildOnError);
|
try {
|
||||||
|
let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
|
||||||
|
if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
|
||||||
|
// we should have an older SQLITE database
|
||||||
|
this.migrateData = this.getMigrateDataFromSQLITE();
|
||||||
|
}
|
||||||
|
// else we've upgraded before but the JSON file is gone, fall through
|
||||||
|
// and rebuild from scratch
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
// No schema version pref means either a really old upgrade (RDF) or
|
||||||
|
// a new profile
|
||||||
|
this.migrateData = this.getMigrateDataFromRDF();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rebuildDatabase(aRebuildOnError);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.rebuildUnreadableDB(e, aRebuildOnError);
|
WARN("Extensions database " + this.jsonFile.path +
|
||||||
|
" exists but is not readable; rebuilding in memory", e);
|
||||||
|
// XXX open question - if we can overwrite at save time, should we, or should we
|
||||||
|
// leave the locked database in case we can recover from it next time we start up?
|
||||||
|
// The old code made one attempt to remove the locked file before it rebuilt in memory
|
||||||
|
this.lockedDatabase = true;
|
||||||
|
// XXX TELEMETRY report when this happens?
|
||||||
|
this.rebuildDatabase(aRebuildOnError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (fstream)
|
if (fstream)
|
||||||
fstream.close();
|
fstream.close();
|
||||||
}
|
}
|
||||||
// If an async load was also in progress, resolve that promise with our DB;
|
|
||||||
// otherwise create a resolved promise
|
|
||||||
if (this._dbPromise)
|
|
||||||
this._dbPromise.resolve(this.addonDB);
|
|
||||||
else
|
|
||||||
this._dbPromise = Promise.resolve(this.addonDB);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
return;
|
||||||
* Parse loaded data, reconstructing the database if the loaded data is not valid
|
|
||||||
* @param aRebuildOnError
|
// XXX what about aForceOpen? Appears to handle the case of "don't open DB file if there aren't any extensions"?
|
||||||
* If true, synchronously reconstruct the database from installed add-ons
|
if (!aForceOpen && !this.dbfileExists) {
|
||||||
*/
|
this.connection = null;
|
||||||
parseDB: function(aData, aRebuildOnError) {
|
return;
|
||||||
try {
|
|
||||||
// dump("Loaded JSON:\n" + aData + "\n");
|
|
||||||
let inputAddons = JSON.parse(aData);
|
|
||||||
// Now do some sanity checks on our JSON db
|
|
||||||
if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
|
|
||||||
// Content of JSON file is bad, need to rebuild from scratch
|
|
||||||
ERROR("bad JSON file contents");
|
|
||||||
this.rebuildDatabase(aRebuildOnError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (inputAddons.schemaVersion != DB_SCHEMA) {
|
|
||||||
// Handle mismatched JSON schema version. For now, we assume
|
|
||||||
// compatibility for JSON data, though we throw away any fields we
|
|
||||||
// don't know about
|
|
||||||
// XXX preserve unknown fields during save/restore
|
|
||||||
LOG("JSON schema mismatch: expected " + DB_SCHEMA +
|
|
||||||
", actual " + inputAddons.schemaVersion);
|
|
||||||
}
|
|
||||||
// If we got here, we probably have good data
|
|
||||||
// Make AddonInternal instances from the loaded data and save them
|
|
||||||
let addonDB = new Map();
|
|
||||||
inputAddons.addons.forEach(function(loadedAddon) {
|
|
||||||
let newAddon = new DBAddonInternal(loadedAddon);
|
|
||||||
addonDB.set(newAddon._key, newAddon);
|
|
||||||
});
|
|
||||||
this.addonDB = addonDB;
|
|
||||||
LOG("Successfully read XPI database");
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
}
|
||||||
catch(e) {
|
|
||||||
// If we catch and log a SyntaxError from the JSON
|
|
||||||
// parser, the xpcshell test harness fails the test for us: bug 870828
|
|
||||||
if (e.name == "SyntaxError") {
|
|
||||||
ERROR("Syntax error parsing saved XPI JSON data");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ERROR("Failed to load XPI JSON data from profile", e);
|
|
||||||
}
|
|
||||||
this.rebuildDatabase(aRebuildOnError);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upgrade database from earlier (sqlite or RDF) version if available
|
|
||||||
*/
|
|
||||||
upgradeDB: function(aRebuildOnError) {
|
|
||||||
try {
|
|
||||||
let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
|
|
||||||
if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
|
|
||||||
// we should have an older SQLITE database
|
|
||||||
this.migrateData = this.getMigrateDataFromSQLITE();
|
|
||||||
}
|
|
||||||
// else we've upgraded before but the JSON file is gone, fall through
|
|
||||||
// and rebuild from scratch
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
// No schema version pref means either a really old upgrade (RDF) or
|
|
||||||
// a new profile
|
|
||||||
this.migrateData = this.getMigrateDataFromRDF();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rebuildDatabase(aRebuildOnError);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconstruct when the DB file exists but is unreadable
|
|
||||||
* (for example because read permission is denied
|
|
||||||
*/
|
|
||||||
rebuildUnreadableDB: function(aError, aRebuildOnError) {
|
|
||||||
WARN("Extensions database " + this.jsonFile.path +
|
|
||||||
" exists but is not readable; rebuilding in memory", aError);
|
|
||||||
// XXX open question - if we can overwrite at save time, should we, or should we
|
|
||||||
// leave the locked database in case we can recover from it next time we start up?
|
|
||||||
// The old code made one attempt to remove the locked file before it rebuilt in memory
|
|
||||||
this.lockedDatabase = true;
|
|
||||||
// XXX TELEMETRY report when this happens?
|
|
||||||
this.rebuildDatabase(aRebuildOnError);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open and read the XPI database asynchronously, upgrading if
|
|
||||||
* necessary. If any DB load operation fails, we need to
|
|
||||||
* synchronously rebuild the DB from the installed extensions.
|
|
||||||
*
|
|
||||||
* @return Promise<Map> resolves to the Map of loaded JSON data stored
|
|
||||||
* in this.addonDB; never rejects.
|
|
||||||
*/
|
|
||||||
asyncLoadDB: function XPIDB_asyncLoadDB(aDBCallback) {
|
|
||||||
// Already started (and possibly finished) loading
|
|
||||||
if (this._dbPromise) {
|
|
||||||
return this._dbPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG("Starting async load of XPI database " + this.jsonFile.path);
|
|
||||||
return this._dbPromise = OS.File.read(this.jsonFile.path).then(
|
|
||||||
byteArray => {
|
|
||||||
if (this._addonDB) {
|
|
||||||
LOG("Synchronous load completed while waiting for async load");
|
|
||||||
return this.addonDB;
|
|
||||||
}
|
|
||||||
LOG("Finished async read of XPI database, parsing...");
|
|
||||||
let decoder = new TextDecoder();
|
|
||||||
let data = decoder.decode(byteArray);
|
|
||||||
this.parseDB(data, true);
|
|
||||||
return this.addonDB;
|
|
||||||
})
|
|
||||||
.then(null,
|
|
||||||
error => {
|
|
||||||
if (this._addonDB) {
|
|
||||||
LOG("Synchronous load completed while waiting for async load");
|
|
||||||
return this.addonDB;
|
|
||||||
}
|
|
||||||
if (error.becauseNoSuchFile) {
|
|
||||||
this.upgradeDB(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// it's there but unreadable
|
|
||||||
this.rebuildUnreadableDB(error, true);
|
|
||||||
}
|
|
||||||
return this.addonDB;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -766,13 +649,21 @@ this.XPIDatabase = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy getter for the addons database
|
||||||
|
*/
|
||||||
|
get addonDB() {
|
||||||
|
this.openConnection(true);
|
||||||
|
return this.addonDB;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the list of file descriptors of active extension directories or XPI
|
* Gets the list of file descriptors of active extension directories or XPI
|
||||||
* files from the add-ons list. This must be loaded from disk since the
|
* files from the add-ons list. This must be loaded from disk since the
|
||||||
* directory service gives no easy way to get both directly. This list doesn't
|
* directory service gives no easy way to get both directly. This list doesn't
|
||||||
* include themes as preferences already say which theme is currently active
|
* include themes as preferences already say which theme is currently active
|
||||||
*
|
*
|
||||||
* @return an array of persistent descriptors for the directories
|
* @return an array of persisitent descriptors for the directories
|
||||||
*/
|
*/
|
||||||
getActiveBundles: function XPIDB_getActiveBundles() {
|
getActiveBundles: function XPIDB_getActiveBundles() {
|
||||||
let bundles = [];
|
let bundles = [];
|
||||||
@ -991,11 +882,27 @@ this.XPIDatabase = {
|
|||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
.then(count => {
|
.then(count => {
|
||||||
// Clear out the cached addons data loaded from JSON
|
// Clear out the cached addons data loaded from JSON and recreate
|
||||||
|
// the getter to allow database re-loads during testing.
|
||||||
delete this.addonDB;
|
delete this.addonDB;
|
||||||
delete this._dbPromise;
|
Object.defineProperty(this, "addonDB", {
|
||||||
|
get: function addonsGetter() {
|
||||||
|
this.openConnection(true);
|
||||||
|
return this.addonDB;
|
||||||
|
},
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
// same for the deferred save
|
// same for the deferred save
|
||||||
delete this._deferredSave;
|
delete this._deferredSave;
|
||||||
|
Object.defineProperty(this, "_deferredSave", {
|
||||||
|
set: function deferredSaveGetter() {
|
||||||
|
delete this._deferredSave;
|
||||||
|
return this._deferredSave =
|
||||||
|
new DeferredSave(this.jsonFile.path, this.formJSON.bind(this),
|
||||||
|
ASYNC_SAVE_DELAY_MS);
|
||||||
|
},
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
// re-enable the schema version setter
|
// re-enable the schema version setter
|
||||||
delete this._schemaVersionSet;
|
delete this._schemaVersionSet;
|
||||||
|
|
||||||
@ -1013,8 +920,7 @@ this.XPIDatabase = {
|
|||||||
* Return a list of all install locations known about by the database. This
|
* Return a list of all install locations known about by the database. This
|
||||||
* is often a a subset of the total install locations when not all have
|
* is often a a subset of the total install locations when not all have
|
||||||
* installed add-ons, occasionally a superset when an install location no
|
* installed add-ons, occasionally a superset when an install location no
|
||||||
* longer exists. Only called from XPIProvider.processFileChanges, when
|
* longer exists.
|
||||||
* the database should already be loaded.
|
|
||||||
*
|
*
|
||||||
* @return a Set of names of install locations
|
* @return a Set of names of install locations
|
||||||
*/
|
*/
|
||||||
@ -1030,62 +936,61 @@ this.XPIDatabase = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously list all addons that match the filter function
|
* List all addons that match the filter function
|
||||||
* @param aFilter
|
* @param aFilter
|
||||||
* Function that takes an addon instance and returns
|
* Function that takes an addon instance and returns
|
||||||
* true if that addon should be included in the selected array
|
* true if that addon should be included in the selected array
|
||||||
* @param aCallback
|
* @return an array of DBAddonInternals
|
||||||
* Called back with an array of addons matching aFilter
|
|
||||||
* or an empty array if none match
|
|
||||||
*/
|
*/
|
||||||
getAddonList: function(aFilter, aCallback) {
|
_listAddons: function XPIDB_listAddons(aFilter) {
|
||||||
this.asyncLoadDB().then(
|
if (!this.addonDB)
|
||||||
addonDB => {
|
return [];
|
||||||
let addonList = _filterDB(addonDB, aFilter);
|
|
||||||
asyncMap(addonList, getRepositoryAddon, safeCallback(aCallback));
|
let addonList = [];
|
||||||
})
|
for (let [key, addon] of this.addonDB) {
|
||||||
.then(null,
|
if (aFilter(addon)) {
|
||||||
error => {
|
addonList.push(addon);
|
||||||
ERROR("getAddonList failed", e);
|
}
|
||||||
safeCallback(aCallback)([]);
|
}
|
||||||
});
|
|
||||||
|
return addonList;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Possibly asynchronously) get the first addon that matches the filter function
|
* Find the first addon that matches the filter function
|
||||||
* @param aFilter
|
* @param aFilter
|
||||||
* Function that takes an addon instance and returns
|
* Function that takes an addon instance and returns
|
||||||
* true if that addon should be selected
|
* true if that addon should be selected
|
||||||
* @param aCallback
|
* @return The first DBAddonInternal for which the filter returns true
|
||||||
* Called back with the addon, or null if no matching addon is found
|
|
||||||
*/
|
*/
|
||||||
getAddon: function(aFilter, aCallback) {
|
_findAddon: function XPIDB_findAddon(aFilter) {
|
||||||
return this.asyncLoadDB().then(
|
if (!this.addonDB)
|
||||||
addonDB => {
|
return null;
|
||||||
getRepositoryAddon(_findAddon(addonDB, aFilter), safeCallback(aCallback));
|
|
||||||
})
|
for (let [key, addon] of this.addonDB) {
|
||||||
.then(null,
|
if (aFilter(addon)) {
|
||||||
error => {
|
return addon;
|
||||||
ERROR("getAddon failed", e);
|
}
|
||||||
safeCallback(aCallback)(null);
|
}
|
||||||
});
|
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously reads all the add-ons in a particular install location.
|
* Synchronously reads all the add-ons in a particular install location.
|
||||||
* Always called with the addon database already loaded.
|
|
||||||
*
|
*
|
||||||
* @param aLocation
|
* @param aLocation
|
||||||
* The name of the install location
|
* The name of the install location
|
||||||
* @return an array of DBAddonInternals
|
* @return an array of DBAddonInternals
|
||||||
*/
|
*/
|
||||||
getAddonsInLocation: function XPIDB_getAddonsInLocation(aLocation) {
|
getAddonsInLocation: function XPIDB_getAddonsInLocation(aLocation) {
|
||||||
return _filterDB(this.addonDB, aAddon => (aAddon.location == aLocation));
|
return this._listAddons(function inLocation(aAddon) {return (aAddon.location == aLocation);});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously gets an add-on with a particular ID in a particular
|
* Asynchronously gets an add-on with a particular ID in a particular
|
||||||
* install location.
|
* install location.
|
||||||
|
* XXX IRVING sync for now
|
||||||
*
|
*
|
||||||
* @param aId
|
* @param aId
|
||||||
* The ID of the add-on to retrieve
|
* The ID of the add-on to retrieve
|
||||||
@ -1095,13 +1000,12 @@ this.XPIDatabase = {
|
|||||||
* A callback to pass the DBAddonInternal to
|
* A callback to pass the DBAddonInternal to
|
||||||
*/
|
*/
|
||||||
getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) {
|
getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) {
|
||||||
this.asyncLoadDB().then(
|
getRepositoryAddon(this.addonDB.get(aLocation + ":" + aId), aCallback);
|
||||||
addonDB => getRepositoryAddon(addonDB.get(aLocation + ":" + aId),
|
|
||||||
safeCallback(aCallback)));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously gets the add-on with the specified ID that is visible.
|
* Asynchronously gets the add-on with an ID that is visible.
|
||||||
|
* XXX IRVING sync
|
||||||
*
|
*
|
||||||
* @param aId
|
* @param aId
|
||||||
* The ID of the add-on to retrieve
|
* The ID of the add-on to retrieve
|
||||||
@ -1109,12 +1013,13 @@ this.XPIDatabase = {
|
|||||||
* A callback to pass the DBAddonInternal to
|
* A callback to pass the DBAddonInternal to
|
||||||
*/
|
*/
|
||||||
getVisibleAddonForID: function XPIDB_getVisibleAddonForID(aId, aCallback) {
|
getVisibleAddonForID: function XPIDB_getVisibleAddonForID(aId, aCallback) {
|
||||||
this.getAddon(aAddon => ((aAddon.id == aId) && aAddon.visible),
|
let addon = this._findAddon(function visibleID(aAddon) {return ((aAddon.id == aId) && aAddon.visible)});
|
||||||
aCallback);
|
getRepositoryAddon(addon, aCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously gets the visible add-ons, optionally restricting by type.
|
* Asynchronously gets the visible add-ons, optionally restricting by type.
|
||||||
|
* XXX IRVING sync
|
||||||
*
|
*
|
||||||
* @param aTypes
|
* @param aTypes
|
||||||
* An array of types to include or null to include all types
|
* An array of types to include or null to include all types
|
||||||
@ -1122,10 +1027,10 @@ this.XPIDatabase = {
|
|||||||
* A callback to pass the array of DBAddonInternals to
|
* A callback to pass the array of DBAddonInternals to
|
||||||
*/
|
*/
|
||||||
getVisibleAddons: function XPIDB_getVisibleAddons(aTypes, aCallback) {
|
getVisibleAddons: function XPIDB_getVisibleAddons(aTypes, aCallback) {
|
||||||
this.getAddonList(aAddon => (aAddon.visible &&
|
let addons = this._listAddons(function visibleType(aAddon) {
|
||||||
(!aTypes || (aTypes.length == 0) ||
|
return (aAddon.visible && (!aTypes || (aTypes.length == 0) || (aTypes.indexOf(aAddon.type) > -1)))
|
||||||
(aTypes.indexOf(aAddon.type) > -1))),
|
});
|
||||||
aCallback);
|
asyncMap(addons, getRepositoryAddon, aCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1136,14 +1041,7 @@ this.XPIDatabase = {
|
|||||||
* @return an array of DBAddonInternals
|
* @return an array of DBAddonInternals
|
||||||
*/
|
*/
|
||||||
getAddonsByType: function XPIDB_getAddonsByType(aType) {
|
getAddonsByType: function XPIDB_getAddonsByType(aType) {
|
||||||
if (!this.addonDB) {
|
return this._listAddons(function byType(aAddon) { return aAddon.type == aType; });
|
||||||
// jank-tastic! Must synchronously load DB if the theme switches from
|
|
||||||
// an XPI theme to a lightweight theme before the DB has loaded,
|
|
||||||
// because we're called from sync XPIProvider.addonChanged
|
|
||||||
WARN("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
|
|
||||||
this.syncLoadDB(true);
|
|
||||||
}
|
|
||||||
return _filterDB(this.addonDB, aAddon => (aAddon.type == aType));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1154,20 +1052,14 @@ this.XPIDatabase = {
|
|||||||
* @return a DBAddonInternal
|
* @return a DBAddonInternal
|
||||||
*/
|
*/
|
||||||
getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
|
getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
|
||||||
if (!this.addonDB) {
|
return this._findAddon(function visibleInternalName(aAddon) {
|
||||||
// This may be called when the DB hasn't otherwise been loaded
|
return (aAddon.visible && (aAddon.internalName == aInternalName));
|
||||||
// XXX TELEMETRY
|
});
|
||||||
WARN("Synchronous load of XPI database due to getVisibleAddonForInternalName");
|
|
||||||
this.syncLoadDB(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _findAddon(this.addonDB,
|
|
||||||
aAddon => aAddon.visible &&
|
|
||||||
(aAddon.internalName == aInternalName));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously gets all add-ons with pending operations.
|
* Asynchronously gets all add-ons with pending operations.
|
||||||
|
* XXX IRVING sync
|
||||||
*
|
*
|
||||||
* @param aTypes
|
* @param aTypes
|
||||||
* The types of add-ons to retrieve or null to get all types
|
* The types of add-ons to retrieve or null to get all types
|
||||||
@ -1177,19 +1069,21 @@ this.XPIDatabase = {
|
|||||||
getVisibleAddonsWithPendingOperations:
|
getVisibleAddonsWithPendingOperations:
|
||||||
function XPIDB_getVisibleAddonsWithPendingOperations(aTypes, aCallback) {
|
function XPIDB_getVisibleAddonsWithPendingOperations(aTypes, aCallback) {
|
||||||
|
|
||||||
this.getAddonList(
|
let addons = this._listAddons(function visibleType(aAddon) {
|
||||||
aAddon => (aAddon.visible &&
|
return (aAddon.visible &&
|
||||||
(aAddon.pendingUninstall ||
|
(aAddon.pendingUninstall ||
|
||||||
// Logic here is tricky. If we're active but either
|
// Logic here is tricky. If we're active but either
|
||||||
// disabled flag is set, we're pending disable; if we're not
|
// disabled flag is set, we're pending disable; if we're not
|
||||||
// active and neither disabled flag is set, we're pending enable
|
// active and neither disabled flag is set, we're pending enable
|
||||||
(aAddon.active == (aAddon.userDisabled || aAddon.appDisabled))) &&
|
(aAddon.active == (aAddon.userDisabled || aAddon.appDisabled))) &&
|
||||||
(!aTypes || (aTypes.length == 0) || (aTypes.indexOf(aAddon.type) > -1))),
|
(!aTypes || (aTypes.length == 0) || (aTypes.indexOf(aAddon.type) > -1)))
|
||||||
aCallback);
|
});
|
||||||
|
asyncMap(addons, getRepositoryAddon, aCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously get an add-on by its Sync GUID.
|
* Asynchronously get an add-on by its Sync GUID.
|
||||||
|
* XXX IRVING sync
|
||||||
*
|
*
|
||||||
* @param aGUID
|
* @param aGUID
|
||||||
* Sync GUID of add-on to fetch
|
* Sync GUID of add-on to fetch
|
||||||
@ -1199,23 +1093,17 @@ this.XPIDatabase = {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
getAddonBySyncGUID: function XPIDB_getAddonBySyncGUID(aGUID, aCallback) {
|
getAddonBySyncGUID: function XPIDB_getAddonBySyncGUID(aGUID, aCallback) {
|
||||||
this.getAddon(aAddon => aAddon.syncGUID == aGUID,
|
let addon = this._findAddon(function bySyncGUID(aAddon) { return aAddon.syncGUID == aGUID; });
|
||||||
aCallback);
|
getRepositoryAddon(addon, aCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously gets all add-ons in the database.
|
* Synchronously gets all add-ons in the database.
|
||||||
* This is only called from the preference observer for the default
|
|
||||||
* compatibility version preference, so we can return an empty list if
|
|
||||||
* we haven't loaded the database yet.
|
|
||||||
*
|
*
|
||||||
* @return an array of DBAddonInternals
|
* @return an array of DBAddonInternals
|
||||||
*/
|
*/
|
||||||
getAddons: function XPIDB_getAddons() {
|
getAddons: function XPIDB_getAddons() {
|
||||||
if (!this.addonDB) {
|
return this._listAddons(function(aAddon) {return true;});
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return _filterDB(this.addonDB, aAddon => true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1228,10 +1116,12 @@ this.XPIDatabase = {
|
|||||||
* @return The DBAddonInternal that was added to the database
|
* @return The DBAddonInternal that was added to the database
|
||||||
*/
|
*/
|
||||||
addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) {
|
addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) {
|
||||||
if (!this.addonDB) {
|
// If there is no DB yet then forcibly create one
|
||||||
// XXX telemetry. Should never happen on platforms that have a default theme
|
// XXX IRVING I don't think this will work as expected because the addonDB
|
||||||
this.syncLoadDB(false);
|
// getter will kick in. Might not matter because of the way the new DB
|
||||||
}
|
// creates itself.
|
||||||
|
if (!this.addonDB)
|
||||||
|
this.openConnection(false, true);
|
||||||
|
|
||||||
let newAddon = new DBAddonInternal(aAddon);
|
let newAddon = new DBAddonInternal(aAddon);
|
||||||
newAddon.descriptor = aDescriptor;
|
newAddon.descriptor = aDescriptor;
|
||||||
@ -1292,7 +1182,7 @@ this.XPIDatabase = {
|
|||||||
*/
|
*/
|
||||||
makeAddonVisible: function XPIDB_makeAddonVisible(aAddon) {
|
makeAddonVisible: function XPIDB_makeAddonVisible(aAddon) {
|
||||||
LOG("Make addon " + aAddon._key + " visible");
|
LOG("Make addon " + aAddon._key + " visible");
|
||||||
for (let [, otherAddon] of this.addonDB) {
|
for (let [key, otherAddon] of this.addonDB) {
|
||||||
if ((otherAddon.id == aAddon.id) && (otherAddon._key != aAddon._key)) {
|
if ((otherAddon.id == aAddon.id) && (otherAddon._key != aAddon._key)) {
|
||||||
LOG("Hide addon " + otherAddon._key);
|
LOG("Hide addon " + otherAddon._key);
|
||||||
otherAddon.visible = false;
|
otherAddon.visible = false;
|
||||||
@ -1319,7 +1209,6 @@ this.XPIDatabase = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously sets the Sync GUID for an add-on.
|
* Synchronously sets the Sync GUID for an add-on.
|
||||||
* Only called when the database is already loaded.
|
|
||||||
*
|
*
|
||||||
* @param aAddon
|
* @param aAddon
|
||||||
* The DBAddonInternal being updated
|
* The DBAddonInternal being updated
|
||||||
@ -1332,7 +1221,7 @@ this.XPIDatabase = {
|
|||||||
function excludeSyncGUID(otherAddon) {
|
function excludeSyncGUID(otherAddon) {
|
||||||
return (otherAddon._key != aAddon._key) && (otherAddon.syncGUID == aGUID);
|
return (otherAddon._key != aAddon._key) && (otherAddon.syncGUID == aGUID);
|
||||||
}
|
}
|
||||||
let otherAddon = _findAddon(this.addonDB, excludeSyncGUID);
|
let otherAddon = this._findAddon(excludeSyncGUID);
|
||||||
if (otherAddon) {
|
if (otherAddon) {
|
||||||
throw new Error("Addon sync GUID conflict for addon " + aAddon._key +
|
throw new Error("Addon sync GUID conflict for addon " + aAddon._key +
|
||||||
": " + otherAddon._key + " already has GUID " + aGUID);
|
": " + otherAddon._key + " already has GUID " + aGUID);
|
||||||
@ -1375,7 +1264,7 @@ this.XPIDatabase = {
|
|||||||
// XXX IRVING this may get called during XPI-utils shutdown
|
// XXX IRVING this may get called during XPI-utils shutdown
|
||||||
// XXX need to make sure PREF_PENDING_OPERATIONS handling is clean
|
// XXX need to make sure PREF_PENDING_OPERATIONS handling is clean
|
||||||
LOG("Updating add-on states");
|
LOG("Updating add-on states");
|
||||||
for (let [, addon] of this.addonDB) {
|
for (let [key, addon] of this.addonDB) {
|
||||||
let newActive = (addon.visible && !addon.userDisabled &&
|
let newActive = (addon.visible && !addon.userDisabled &&
|
||||||
!addon.softDisabled && !addon.appDisabled &&
|
!addon.softDisabled && !addon.appDisabled &&
|
||||||
!addon.pendingUninstall);
|
!addon.pendingUninstall);
|
||||||
@ -1390,10 +1279,6 @@ this.XPIDatabase = {
|
|||||||
* Writes out the XPI add-ons list for the platform to read.
|
* Writes out the XPI add-ons list for the platform to read.
|
||||||
*/
|
*/
|
||||||
writeAddonsList: function XPIDB_writeAddonsList() {
|
writeAddonsList: function XPIDB_writeAddonsList() {
|
||||||
if (!this.addonDB) {
|
|
||||||
// Unusual condition, force the DB to load
|
|
||||||
this.syncLoadDB(true);
|
|
||||||
}
|
|
||||||
Services.appinfo.invalidateCachesOnRestart();
|
Services.appinfo.invalidateCachesOnRestart();
|
||||||
|
|
||||||
let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
|
let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
|
||||||
@ -1403,9 +1288,9 @@ this.XPIDatabase = {
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
let fullCount = 0;
|
let fullCount = 0;
|
||||||
|
|
||||||
let activeAddons = _filterDB(
|
let activeAddons = this._listAddons(function active(aAddon) {
|
||||||
this.addonDB,
|
return aAddon.active && !aAddon.bootstrap && (aAddon.type != "theme");
|
||||||
aAddon => aAddon.active && !aAddon.bootstrap && (aAddon.type != "theme"));
|
});
|
||||||
|
|
||||||
for (let row of activeAddons) {
|
for (let row of activeAddons) {
|
||||||
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
||||||
@ -1425,13 +1310,12 @@ this.XPIDatabase = {
|
|||||||
|
|
||||||
let themes = [];
|
let themes = [];
|
||||||
if (dssEnabled) {
|
if (dssEnabled) {
|
||||||
themes = _filterDB(this.addonDB, aAddon => aAddon.type == "theme");
|
themes = this._listAddons(function isTheme(aAddon){ return aAddon.type == "theme"; });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let activeTheme = _findAddon(
|
let activeTheme = this._findAddon(function isSelected(aAddon) {
|
||||||
this.addonDB,
|
return ((aAddon.type == "theme") && (aAddon.internalName == XPIProvider.selectedSkin));
|
||||||
aAddon => (aAddon.type == "theme") &&
|
});
|
||||||
(aAddon.internalName == XPIProvider.selectedSkin));
|
|
||||||
if (activeTheme) {
|
if (activeTheme) {
|
||||||
themes.push(activeTheme);
|
themes.push(activeTheme);
|
||||||
}
|
}
|
||||||
|
@ -45,26 +45,25 @@ function run_test() {
|
|||||||
|
|
||||||
let internal_ids = {};
|
let internal_ids = {};
|
||||||
|
|
||||||
let a = ["addon1@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"];
|
[["addon1@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"]
|
||||||
stmt.params.id = a[0];
|
].forEach(function(a) {
|
||||||
stmt.params.location = a[1];
|
stmt.params.id = a[0];
|
||||||
stmt.params.version = a[2];
|
stmt.params.location = a[1];
|
||||||
stmt.params.active = a[3];
|
stmt.params.version = a[2];
|
||||||
stmt.params.userDisabled = a[4];
|
stmt.params.active = a[3];
|
||||||
stmt.params.installDate = a[5];
|
stmt.params.userDisabled = a[4];
|
||||||
stmt.execute();
|
stmt.params.installDate = a[5];
|
||||||
internal_ids[a[0]] = db.lastInsertRowID;
|
stmt.execute();
|
||||||
|
internal_ids[a[0]] = db.lastInsertRowID;
|
||||||
|
});
|
||||||
stmt.finalize();
|
stmt.finalize();
|
||||||
|
|
||||||
db.schemaVersion = 14;
|
db.schemaVersion = 15;
|
||||||
Services.prefs.setIntPref("extensions.databaseSchema", 14);
|
Services.prefs.setIntPref("extensions.databaseSchema", 14);
|
||||||
db.close();
|
db.close();
|
||||||
|
|
||||||
startupManager();
|
startupManager();
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
add_test(function before_rebuild() {
|
|
||||||
AddonManager.getAddonByID("addon1@tests.mozilla.org",
|
AddonManager.getAddonByID("addon1@tests.mozilla.org",
|
||||||
function check_before_rebuild (a1) {
|
function check_before_rebuild (a1) {
|
||||||
// First check that it migrated OK once
|
// First check that it migrated OK once
|
||||||
@ -78,7 +77,7 @@ add_test(function before_rebuild() {
|
|||||||
|
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// now shut down, remove the JSON database,
|
// now shut down, remove the JSON database,
|
||||||
// start up again, and make sure the data didn't migrate this time
|
// start up again, and make sure the data didn't migrate this time
|
||||||
|
Loading…
Reference in New Issue
Block a user