mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1040158: Allow overriding an add-ons multiprocessCompatible flag in the update manifest for an add-on. r=Unfocused
This commit is contained in:
parent
5f7e590a4b
commit
39091d9406
@ -248,6 +248,13 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
|
||||
return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true));
|
||||
}
|
||||
|
||||
function getBooleanProperty(aDs, aSource, aProperty) {
|
||||
let propValue = aDs.GetTarget(aSource, EM_R(aProperty), true);
|
||||
if (!propValue)
|
||||
return undefined;
|
||||
return getValue(propValue) == "true";
|
||||
}
|
||||
|
||||
function getRequiredProperty(aDs, aSource, aProperty) {
|
||||
let value = getProperty(aDs, aSource, aProperty);
|
||||
if (!value)
|
||||
@ -351,10 +358,11 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
|
||||
let result = {
|
||||
id: aId,
|
||||
version: version,
|
||||
multiprocessCompatible: getBooleanProperty(ds, item, "multiprocessCompatible"),
|
||||
updateURL: getProperty(ds, targetApp, "updateLink"),
|
||||
updateHash: getProperty(ds, targetApp, "updateHash"),
|
||||
updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
|
||||
strictCompatibility: getProperty(ds, targetApp, "strictCompatibility") == "true",
|
||||
strictCompatibility: !!getBooleanProperty(ds, targetApp, "strictCompatibility"),
|
||||
targetApplications: [appEntry]
|
||||
};
|
||||
|
||||
|
@ -6440,6 +6440,8 @@ AddonInternal.prototype = {
|
||||
}
|
||||
});
|
||||
});
|
||||
if (aUpdate.multiprocessCompatible !== undefined)
|
||||
this.multiprocessCompatible = aUpdate.multiprocessCompatible;
|
||||
this.appDisabled = !isUsableAddon(this);
|
||||
},
|
||||
|
||||
@ -6599,7 +6601,7 @@ function AddonWrapper(aAddon) {
|
||||
"providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
|
||||
"softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
|
||||
"strictCompatibility", "compatibilityOverrides", "updateURL",
|
||||
"getDataDirectory"].forEach(function(aProp) {
|
||||
"getDataDirectory", "multiprocessCompatible"].forEach(function(aProp) {
|
||||
this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
|
||||
}, this);
|
||||
|
||||
|
@ -352,6 +352,11 @@ function DBAddonInternalPrototype()
|
||||
}
|
||||
});
|
||||
});
|
||||
if (aUpdate.multiprocessCompatible !== undefined &&
|
||||
aUpdate.multiprocessCompatible != this.multiprocessCompatible) {
|
||||
this.multiprocessCompatible = aUpdate.multiprocessCompatible;
|
||||
XPIDatabase.saveChanges();
|
||||
}
|
||||
XPIProvider.updateAddonDisabledState(this);
|
||||
};
|
||||
|
||||
|
@ -174,18 +174,21 @@ function do_get_addon(aName) {
|
||||
}
|
||||
|
||||
function do_get_addon_hash(aName, aAlgorithm) {
|
||||
let file = do_get_addon(aName);
|
||||
return do_get_file_hash(file);
|
||||
}
|
||||
|
||||
function do_get_file_hash(aFile, aAlgorithm) {
|
||||
if (!aAlgorithm)
|
||||
aAlgorithm = "sha1";
|
||||
|
||||
let file = do_get_addon(aName);
|
||||
|
||||
let crypto = AM_Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(AM_Ci.nsICryptoHash);
|
||||
crypto.initWithString(aAlgorithm);
|
||||
let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(AM_Ci.nsIFileInputStream);
|
||||
fis.init(file, -1, -1, false);
|
||||
crypto.updateFromStream(fis, file.fileSize);
|
||||
fis.init(aFile, -1, -1, false);
|
||||
crypto.updateFromStream(fis, aFile.fileSize);
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode)
|
||||
@ -508,7 +511,8 @@ function loadAddonsList() {
|
||||
|
||||
gAddonsList = {
|
||||
extensions: [],
|
||||
themes: []
|
||||
themes: [],
|
||||
mpIncompatible: new Set()
|
||||
};
|
||||
|
||||
if (!gExtensionsINI.exists())
|
||||
@ -519,6 +523,11 @@ function loadAddonsList() {
|
||||
var parser = factory.createINIParser(gExtensionsINI);
|
||||
gAddonsList.extensions = readDirectories("ExtensionDirs");
|
||||
gAddonsList.themes = readDirectories("ThemeDirs");
|
||||
var keys = parser.getKeys("MultiprocessIncompatibleExtensions");
|
||||
while (keys.hasMore()) {
|
||||
let id = parser.getString("MultiprocessIncompatibleExtensions", keys.getNext());
|
||||
gAddonsList.mpIncompatible.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
function isItemInAddonsList(aType, aDir, aId) {
|
||||
@ -538,6 +547,10 @@ function isItemInAddonsList(aType, aDir, aId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isItemMarkedMPIncompatible(aId) {
|
||||
return gAddonsList.mpIncompatible.has(aId);
|
||||
}
|
||||
|
||||
function isThemeInAddonsList(aDir, aId) {
|
||||
return isItemInAddonsList("themes", aDir, aId);
|
||||
}
|
||||
@ -588,6 +601,54 @@ function writeLocaleStrings(aData) {
|
||||
return rdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an update.rdf structure as a string using for the update data passed.
|
||||
*
|
||||
* @param aData
|
||||
* The update data as a JS object. Each property name is an add-on ID,
|
||||
* the property value is an array of each version of the add-on. Each
|
||||
* array value is a JS object containing the data for the version, at
|
||||
* minimum a "version" and "targetApplications" property should be
|
||||
* included to create a functional update manifest.
|
||||
* @return the update.rdf structure as a string.
|
||||
*/
|
||||
function createUpdateRDF(aData) {
|
||||
var rdf = '<?xml version="1.0"?>\n';
|
||||
rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
|
||||
' xmlns:em="http://www.mozilla.org/2004/em-rdf#">\n';
|
||||
|
||||
for (let addon in aData) {
|
||||
rdf += ' <Description about="urn:mozilla:extension:' + escapeXML(addon) + '"><em:updates><Seq>\n';
|
||||
|
||||
for (let versionData of aData[addon]) {
|
||||
rdf += ' <li><Description>\n';
|
||||
|
||||
for (let prop of ["version", "multiprocessCompatible"]) {
|
||||
if (prop in versionData)
|
||||
rdf += " <em:" + prop + ">" + escapeXML(versionData[prop]) + "</em:" + prop + ">\n";
|
||||
}
|
||||
|
||||
if ("targetApplications" in versionData) {
|
||||
for (let app of versionData.targetApplications) {
|
||||
rdf += " <em:targetApplication><Description>\n";
|
||||
for (let prop of ["id", "minVersion", "maxVersion", "updateLink", "updateHash"]) {
|
||||
if (prop in app)
|
||||
rdf += " <em:" + prop + ">" + escapeXML(app[prop]) + "</em:" + prop + ">\n";
|
||||
}
|
||||
rdf += " </Description></em:targetApplication>\n";
|
||||
}
|
||||
}
|
||||
|
||||
rdf += ' </Description></li>\n';
|
||||
}
|
||||
|
||||
rdf += ' </Seq></em:updates></Description>\n'
|
||||
}
|
||||
rdf += "</RDF>\n";
|
||||
|
||||
return rdf;
|
||||
}
|
||||
|
||||
function createInstallRDF(aData) {
|
||||
var rdf = '<?xml version="1.0"?>\n';
|
||||
rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
|
||||
@ -596,7 +657,7 @@ function createInstallRDF(aData) {
|
||||
|
||||
["id", "version", "type", "internalName", "updateURL", "updateKey",
|
||||
"optionsURL", "optionsType", "aboutURL", "iconURL", "icon64URL",
|
||||
"skinnable", "bootstrap", "strictCompatibility"].forEach(function(aProp) {
|
||||
"skinnable", "bootstrap", "strictCompatibility", "multiprocessCompatible"].forEach(function(aProp) {
|
||||
if (aProp in aData)
|
||||
rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
|
||||
});
|
||||
@ -728,25 +789,65 @@ function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
|
||||
function writeInstallRDFToXPI(aData, aDir, aId, aExtraFile) {
|
||||
var id = aId ? aId : aData.id
|
||||
|
||||
var dir = aDir.clone();
|
||||
if (!aDir.exists())
|
||||
aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
if (!dir.exists())
|
||||
dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
dir.append(id + ".xpi");
|
||||
var file = aDir.clone();
|
||||
file.append(id + ".xpi");
|
||||
writeInstallRDFToXPIFile(aData, file, aExtraFile);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an install.rdf manifest into an XPI file using the properties passed
|
||||
* in a JS object. The objects should contain a property for each property to
|
||||
* appear in the RDF. The object may contain an array of objects with id,
|
||||
* minVersion and maxVersion in the targetApplications property to give target
|
||||
* application compatibility.
|
||||
*
|
||||
* @param aData
|
||||
* The object holding data about the add-on
|
||||
* @param aFile
|
||||
* The XPI file to write to. Any existing file will be overwritten
|
||||
* @param aExtraFile
|
||||
* An optional dummy file to create in the extension
|
||||
*/
|
||||
function writeInstallRDFToXPIFile(aData, aFile, aExtraFile) {
|
||||
var rdf = createInstallRDF(aData);
|
||||
var stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(AM_Ci.nsIStringInputStream);
|
||||
stream.setData(rdf, -1);
|
||||
var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
|
||||
createInstance(AM_Ci.nsIZipWriter);
|
||||
zipW.open(dir, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
|
||||
zipW.open(aFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
|
||||
zipW.addEntryStream("install.rdf", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
|
||||
stream, false);
|
||||
if (aExtraFile)
|
||||
zipW.addEntryStream(aExtraFile, 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
|
||||
stream, false);
|
||||
zipW.close();
|
||||
return dir;
|
||||
}
|
||||
|
||||
let temp_xpis = [];
|
||||
/**
|
||||
* Creates an XPI file for some manifest data in the temporary directory and
|
||||
* returns the nsIFile for it. The file will be deleted when the test completes.
|
||||
*
|
||||
* @param aData
|
||||
* The object holding data about the add-on
|
||||
* @return A file pointing to the created XPI file
|
||||
*/
|
||||
function createTempXPIFile(aData) {
|
||||
var file = gTmpD.clone();
|
||||
file.append("foo.xpi");
|
||||
do {
|
||||
file.leafName = Math.floor(Math.random() * 1000000) + ".xpi";
|
||||
} while (file.exists());
|
||||
|
||||
temp_xpis.push(file);
|
||||
writeInstallRDFToXPIFile(aData, file);
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1163,6 +1264,12 @@ function completeAllInstalls(aInstalls, aCallback) {
|
||||
});
|
||||
}
|
||||
|
||||
function promiseCompleteAllInstalls(aInstalls) {
|
||||
return new Promise(resolve => {
|
||||
completeAllInstalls(aInstalls, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to install an array of files and call a callback after the
|
||||
* installs are completed.
|
||||
@ -1415,6 +1522,11 @@ do_register_cleanup(function addon_cleanup() {
|
||||
if (timer)
|
||||
timer.cancel();
|
||||
|
||||
for (let file of temp_xpis) {
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
}
|
||||
|
||||
// Check that the temporary directory is empty
|
||||
var dirEntries = gTmpD.directoryEntries
|
||||
.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
|
||||
@ -1613,3 +1725,27 @@ function promiseAddonsByIDs(list) {
|
||||
function promiseAddonByID(aId) {
|
||||
return new Promise((resolve, reject) => AddonManager.getAddonByID(aId, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will be resolved when an add-on update check is
|
||||
* complete. The value resolved will be an AddonInstall if a new version was
|
||||
* found.
|
||||
*/
|
||||
function promiseFindAddonUpdates(addon, reason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
|
||||
return new Promise((resolve, reject) => {
|
||||
addon.findUpdates({
|
||||
install: null,
|
||||
|
||||
onUpdateAvailable: function(addon, install) {
|
||||
this.install = install;
|
||||
},
|
||||
|
||||
onUpdateFinished: function(addon, error) {
|
||||
if (error == AddonManager.UPDATE_STATUS_NO_ERROR)
|
||||
resolve(this.install);
|
||||
else
|
||||
reject(error);
|
||||
}
|
||||
}, reason);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
var gServer;
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
|
||||
|
||||
function build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible) {
|
||||
return function* () {
|
||||
dump("Running test" +
|
||||
" multiprocessCompatible: " + multiprocessCompatible +
|
||||
" bootstrap: " + bootstrap +
|
||||
" updateMultiprocessCompatible: " + updateMultiprocessCompatible +
|
||||
"\n");
|
||||
|
||||
let addonData = {
|
||||
id: "addon@tests.mozilla.org",
|
||||
name: "Test Add-on",
|
||||
version: "1.0",
|
||||
multiprocessCompatible,
|
||||
bootstrap,
|
||||
updateURL: "http://localhost:" + gPort + "/updaterdf",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "1"
|
||||
}]
|
||||
}
|
||||
|
||||
gServer.registerPathHandler("/updaterdf", function(request, response) {
|
||||
let updateData = {};
|
||||
updateData[addonData.id] = [{
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "1"
|
||||
}]
|
||||
}];
|
||||
|
||||
if (updateMultiprocessCompatible !== undefined) {
|
||||
updateData[addonData.id][0].multiprocessCompatible = updateMultiprocessCompatible;
|
||||
}
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.write(createUpdateRDF(updateData));
|
||||
});
|
||||
|
||||
let expectedMPC = updateMultiprocessCompatible === undefined ?
|
||||
multiprocessCompatible :
|
||||
updateMultiprocessCompatible;
|
||||
|
||||
let xpifile = createTempXPIFile(addonData);
|
||||
let install = yield new Promise(resolve => AddonManager.getInstallForFile(xpifile, resolve));
|
||||
do_check_eq(install.addon.multiprocessCompatible, multiprocessCompatible);
|
||||
yield promiseCompleteAllInstalls([install]);
|
||||
|
||||
if (!bootstrap) {
|
||||
yield promiseRestartManager();
|
||||
do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
|
||||
do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
|
||||
}
|
||||
|
||||
let addon = yield promiseAddonByID(addonData.id);
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.multiprocessCompatible, multiprocessCompatible);
|
||||
|
||||
yield promiseFindAddonUpdates(addon);
|
||||
|
||||
// Should have applied the compatibility change
|
||||
do_check_eq(addon.multiprocessCompatible, expectedMPC);
|
||||
yield promiseRestartManager();
|
||||
|
||||
addon = yield promiseAddonByID(addonData.id);
|
||||
// Should have persisted the compatibility change
|
||||
do_check_eq(addon.multiprocessCompatible, expectedMPC);
|
||||
if (!bootstrap) {
|
||||
do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
|
||||
do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
|
||||
}
|
||||
|
||||
addon.uninstall();
|
||||
yield promiseRestartManager();
|
||||
|
||||
gServer.registerPathHandler("/updaterdf", null);
|
||||
}
|
||||
}
|
||||
|
||||
/* Builds a set of tests to run the same steps for every combination of:
|
||||
* The add-on being restartless
|
||||
* The initial add-on supporting multiprocess
|
||||
* The update saying the add-on should or should not support multiprocess (or not say anything at all)
|
||||
*/
|
||||
for (let bootstrap of [false, true]) {
|
||||
for (let multiprocessCompatible of [false, true]) {
|
||||
for (let updateMultiprocessCompatible of [undefined, false, true]) {
|
||||
add_task(build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
|
||||
startupManager();
|
||||
|
||||
// Create and configure the HTTP server.
|
||||
gServer = new HttpServer();
|
||||
gServer.registerDirectory("/data/", gTmpD);
|
||||
gServer.start(-1);
|
||||
gPort = gServer.identity.primaryPort;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function end_test() {
|
||||
gServer.stop(do_test_finished);
|
||||
}
|
@ -220,6 +220,7 @@ requesttimeoutfactor = 2
|
||||
[test_migrate5.js]
|
||||
[test_migrateAddonRepository.js]
|
||||
[test_migrate_max_version.js]
|
||||
[test_multiprocessCompatible.js]
|
||||
[test_no_addons.js]
|
||||
[test_onPropertyChanged_appDisabled.js]
|
||||
[test_permissions.js]
|
||||
|
@ -25,5 +25,4 @@ run-if = appname == "firefox"
|
||||
[test_XPIcancel.js]
|
||||
[test_XPIStates.js]
|
||||
|
||||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
Loading…
Reference in New Issue
Block a user