mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
943 lines
28 KiB
JavaScript
943 lines
28 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
*/
|
|
|
|
const AM_Cc = Components.classes;
|
|
const AM_Ci = Components.interfaces;
|
|
|
|
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
|
|
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
|
|
|
|
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
|
Components.utils.import("resource://gre/modules/AddonRepository.jsm");
|
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
|
|
|
var gInternalManager = null;
|
|
var gAppInfo = null;
|
|
var gAddonsList;
|
|
|
|
function createAppInfo(id, name, version, platformVersion) {
|
|
gAppInfo = {
|
|
// nsIXULAppInfo
|
|
vendor: "Mozilla",
|
|
name: name,
|
|
ID: id,
|
|
version: version,
|
|
appBuildID: "2007010101",
|
|
platformVersion: platformVersion,
|
|
platformBuildID: "2007010101",
|
|
|
|
// nsIXULRuntime
|
|
inSafeMode: false,
|
|
logConsoleErrors: true,
|
|
OS: "XPCShell",
|
|
XPCOMABI: "noarch-spidermonkey",
|
|
invalidateCachesOnRestart: function invalidateCachesOnRestart() {
|
|
// Do nothing
|
|
},
|
|
|
|
// nsICrashReporter
|
|
annotations: {},
|
|
|
|
annotateCrashReport: function(key, data) {
|
|
this.annotations[key] = data;
|
|
},
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIXULAppInfo,
|
|
AM_Ci.nsIXULRuntime,
|
|
AM_Ci.nsICrashReporter,
|
|
AM_Ci.nsISupports])
|
|
};
|
|
|
|
var XULAppInfoFactory = {
|
|
createInstance: function (outer, iid) {
|
|
if (outer != null)
|
|
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
|
return gAppInfo.QueryInterface(iid);
|
|
}
|
|
};
|
|
var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
|
|
registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
|
|
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
|
|
}
|
|
|
|
/**
|
|
* Tests that an add-on does appear in the crash report annotations, if
|
|
* crash reporting is enabled. The test will fail if the add-on is not in the
|
|
* annotation.
|
|
* @param aId
|
|
* The ID of the add-on
|
|
* @param aVersion
|
|
* The version of the add-on
|
|
*/
|
|
function do_check_in_crash_annotation(aId, aVersion) {
|
|
if (!("nsICrashReporter" in AM_Ci))
|
|
return;
|
|
|
|
if (!("Add-ons" in gAppInfo.annotations)) {
|
|
do_check_false(true);
|
|
return;
|
|
}
|
|
|
|
let addons = gAppInfo.annotations["Add-ons"].split(",");
|
|
do_check_false(addons.indexOf(aId + ":" + aVersion) < 0);
|
|
}
|
|
|
|
/**
|
|
* Tests that an add-on does not appear in the crash report annotations, if
|
|
* crash reporting is enabled. The test will fail if the add-on is in the
|
|
* annotation.
|
|
* @param aId
|
|
* The ID of the add-on
|
|
* @param aVersion
|
|
* The version of the add-on
|
|
*/
|
|
function do_check_not_in_crash_annotation(aId, aVersion) {
|
|
if (!("nsICrashReporter" in AM_Ci))
|
|
return;
|
|
|
|
if (!("Add-ons" in gAppInfo.annotations)) {
|
|
do_check_true(true);
|
|
return;
|
|
}
|
|
|
|
let addons = gAppInfo.annotations["Add-ons"].split(",");
|
|
do_check_true(addons.indexOf(aId + ":" + aVersion) < 0);
|
|
}
|
|
|
|
/**
|
|
* Returns a testcase xpi
|
|
*
|
|
* @param aName
|
|
* The name of the testcase (without extension)
|
|
* @return an nsILocalFile pointing to the testcase xpi
|
|
*/
|
|
function do_get_addon(aName) {
|
|
return do_get_file("addons/" + aName + ".xpi");
|
|
}
|
|
|
|
/**
|
|
* Check that an array of actual add-ons is the same as an array of
|
|
* expected add-ons.
|
|
*
|
|
* @param aActualAddons
|
|
* The array of actual add-ons to check.
|
|
* @param aExpectedAddons
|
|
* The array of expected add-ons to check against.
|
|
* @param aProperties
|
|
* An array of properties to check.
|
|
*/
|
|
function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
|
|
do_check_neq(aActualAddons, null);
|
|
do_check_eq(aActualAddons.length, aExpectedAddons.length);
|
|
for (let i = 0; i < aActualAddons.length; i++)
|
|
do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
|
|
}
|
|
|
|
/**
|
|
* Check that the actual add-on is the same as the expected add-on.
|
|
*
|
|
* @param aActualAddon
|
|
* The actual add-on to check.
|
|
* @param aExpectedAddon
|
|
* The expected add-on to check against.
|
|
* @param aProperties
|
|
* An array of properties to check.
|
|
*/
|
|
function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
|
|
do_check_neq(aActualAddon, null);
|
|
|
|
aProperties.forEach(function(aProperty) {
|
|
let actualValue = aActualAddon[aProperty];
|
|
let expectedValue = aExpectedAddon[aProperty];
|
|
|
|
// Check that all undefined expected properties are null on actual add-on
|
|
if (!(aProperty in aExpectedAddon)) {
|
|
if (actualValue !== undefined && actualValue !== null)
|
|
do_throw("Unexpected defined/non-null property for add-on " +
|
|
aExpectedAddon.id + " (addon[" + aProperty + "] = " + actualValue);
|
|
|
|
return;
|
|
}
|
|
|
|
switch (aProperty) {
|
|
case "creator":
|
|
do_check_author(actualValue, expectedValue);
|
|
break;
|
|
|
|
case "developers":
|
|
case "translators":
|
|
case "contributors":
|
|
do_check_eq(actualValue.length, expectedValue.length);
|
|
for (let i = 0; i < actualValue.length; i++)
|
|
do_check_author(actualValue[i], expectedValue[i]);
|
|
break;
|
|
|
|
case "screenshots":
|
|
do_check_eq(actualValue.length, expectedValue.length);
|
|
for (let i = 0; i < actualValue.length; i++)
|
|
do_check_screenshot(actualValue[i], expectedValue[i]);
|
|
break;
|
|
|
|
case "sourceURI":
|
|
do_check_eq(actualValue.spec, expectedValue);
|
|
break;
|
|
|
|
case "updateDate":
|
|
do_check_eq(actualValue.getTime(), expectedValue.getTime());
|
|
break;
|
|
|
|
default:
|
|
if (actualValue !== expectedValue)
|
|
do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
|
|
" (" + actualValue + " === " + expectedValue + ")");
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check that the actual author is the same as the expected author.
|
|
*
|
|
* @param aActual
|
|
* The actual author to check.
|
|
* @param aExpected
|
|
* The expected author to check against.
|
|
*/
|
|
function do_check_author(aActual, aExpected) {
|
|
do_check_eq(aActual.toString(), aExpected.name);
|
|
do_check_eq(aActual.name, aExpected.name);
|
|
do_check_eq(aActual.url, aExpected.url);
|
|
}
|
|
|
|
/**
|
|
* Check that the actual screenshot is the same as the expected screenshot.
|
|
*
|
|
* @param aActual
|
|
* The actual screenshot to check.
|
|
* @param aExpected
|
|
* The expected screenshot to check against.
|
|
*/
|
|
function do_check_screenshot(aActual, aExpected) {
|
|
do_check_eq(aActual.toString(), aExpected.url);
|
|
do_check_eq(aActual.url, aExpected.url);
|
|
do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL);
|
|
do_check_eq(aActual.caption, aExpected.caption);
|
|
}
|
|
|
|
/**
|
|
* Starts up the add-on manager as if it was started by the application.
|
|
*
|
|
* @param aAppChanged
|
|
* An optional boolean parameter to simulate the case where the
|
|
* application has changed version since the last run. If not passed it
|
|
* defaults to true
|
|
*/
|
|
function startupManager(aAppChanged) {
|
|
if (gInternalManager)
|
|
do_throw("Test attempt to startup manager that was already started.");
|
|
|
|
if (aAppChanged || aAppChanged === undefined) {
|
|
var file = gProfD.clone();
|
|
file.append("extensions.ini");
|
|
if (file.exists())
|
|
file.remove(true);
|
|
}
|
|
|
|
gInternalManager = AM_Cc["@mozilla.org/addons/integration;1"].
|
|
getService(AM_Ci.nsIObserver).
|
|
QueryInterface(AM_Ci.nsITimerCallback);
|
|
|
|
gInternalManager.observe(null, "addons-startup", null);
|
|
|
|
// Load the add-ons list as it was after extension registration
|
|
loadAddonsList();
|
|
}
|
|
|
|
/**
|
|
* Restarts the add-on manager as if the host application was restarted.
|
|
*
|
|
* @param aNewVersion
|
|
* An optional new version to use for the application. Passing this
|
|
* will change nsIXULAppInfo.version and make the startup appear as if
|
|
* the application version has changed.
|
|
*/
|
|
function restartManager(aNewVersion) {
|
|
shutdownManager();
|
|
if (aNewVersion) {
|
|
gAppInfo.version = aNewVersion;
|
|
startupManager(true);
|
|
}
|
|
else {
|
|
startupManager(false);
|
|
}
|
|
}
|
|
|
|
function shutdownManager() {
|
|
if (!gInternalManager)
|
|
return;
|
|
|
|
let obs = AM_Cc["@mozilla.org/observer-service;1"].
|
|
getService(AM_Ci.nsIObserverService);
|
|
|
|
let xpiShutdown = false;
|
|
obs.addObserver({
|
|
observe: function(aSubject, aTopic, aData) {
|
|
xpiShutdown = true;
|
|
obs.removeObserver(this, "xpi-provider-shutdown");
|
|
}
|
|
}, "xpi-provider-shutdown", false);
|
|
|
|
let repositoryShutdown = false;
|
|
obs.addObserver({
|
|
observe: function(aSubject, aTopic, aData) {
|
|
repositoryShutdown = true;
|
|
obs.removeObserver(this, "addon-repository-shutdown");
|
|
}
|
|
}, "addon-repository-shutdown", false);
|
|
|
|
obs.notifyObservers(null, "quit-application-granted", null);
|
|
gInternalManager.observe(null, "xpcom-shutdown", null);
|
|
gInternalManager = null;
|
|
|
|
AddonRepository.shutdown();
|
|
|
|
// Load the add-ons list as it was after application shutdown
|
|
loadAddonsList();
|
|
|
|
// Clear any crash report annotations
|
|
gAppInfo.annotations = {};
|
|
|
|
let thr = AM_Cc["@mozilla.org/thread-manager;1"].
|
|
getService(AM_Ci.nsIThreadManager).
|
|
mainThread;
|
|
|
|
// Wait until we observe the shutdown notifications
|
|
while (!repositoryShutdown || !xpiShutdown) {
|
|
if (thr.hasPendingEvents())
|
|
thr.processNextEvent(false);
|
|
}
|
|
}
|
|
|
|
function loadAddonsList() {
|
|
function readDirectories(aSection) {
|
|
var dirs = [];
|
|
var keys = parser.getKeys(aSection);
|
|
while (keys.hasMore()) {
|
|
let descriptor = parser.getString(aSection, keys.getNext());
|
|
try {
|
|
let file = AM_Cc["@mozilla.org/file/local;1"].
|
|
createInstance(AM_Ci.nsILocalFile);
|
|
file.persistentDescriptor = descriptor;
|
|
dirs.push(file);
|
|
}
|
|
catch (e) {
|
|
// Throws if the directory doesn't exist, we can ignore this since the
|
|
// platform will too.
|
|
}
|
|
}
|
|
return dirs;
|
|
}
|
|
|
|
gAddonsList = {
|
|
extensions: [],
|
|
themes: []
|
|
};
|
|
|
|
var file = gProfD.clone();
|
|
file.append("extensions.ini");
|
|
if (!file.exists())
|
|
return;
|
|
|
|
var factory = AM_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
|
|
getService(AM_Ci.nsIINIParserFactory);
|
|
var parser = factory.createINIParser(file);
|
|
gAddonsList.extensions = readDirectories("ExtensionDirs");
|
|
gAddonsList.themes = readDirectories("ThemeDirs");
|
|
}
|
|
|
|
function isItemInAddonsList(aType, aDir, aId) {
|
|
var path = aDir.clone();
|
|
path.append(aId);
|
|
for (var i = 0; i < gAddonsList[aType].length; i++) {
|
|
if (gAddonsList[aType][i].equals(path))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isThemeInAddonsList(aDir, aId) {
|
|
return isItemInAddonsList("themes", aDir, aId);
|
|
}
|
|
|
|
function isExtensionInAddonsList(aDir, aId) {
|
|
return isItemInAddonsList("extensions", aDir, aId);
|
|
}
|
|
|
|
/**
|
|
* Escapes any occurances of &, ", < or > with XML entities.
|
|
*
|
|
* @param str
|
|
* The string to escape
|
|
* @return The escaped string
|
|
*/
|
|
function escapeXML(aStr) {
|
|
return aStr.toString()
|
|
.replace(/&/g, "&")
|
|
.replace(/"/g, """)
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">");
|
|
}
|
|
|
|
function writeLocaleStrings(aData) {
|
|
let rdf = "";
|
|
["name", "description", "creator", "homepageURL"].forEach(function(aProp) {
|
|
if (aProp in aData)
|
|
rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
|
|
});
|
|
|
|
["developer", "translator", "contributor"].forEach(function(aProp) {
|
|
if (aProp in aData) {
|
|
aData[aProp].forEach(function(aValue) {
|
|
rdf += "<em:" + aProp + ">" + escapeXML(aValue) + "</em:" + aProp + ">\n";
|
|
});
|
|
}
|
|
});
|
|
return rdf;
|
|
}
|
|
|
|
/**
|
|
* Writes an install.rdf manifest into a directory using the properties passed
|
|
* in a JS object. The objects should contain a property for each property to
|
|
* appear in the RDFThe 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 aDir
|
|
* The directory to add the install.rdf to
|
|
*/
|
|
function writeInstallRDFToDir(aData, aDir) {
|
|
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';
|
|
rdf += '<Description about="urn:mozilla:install-manifest">\n';
|
|
|
|
["id", "version", "type", "internalName", "updateURL", "updateKey",
|
|
"optionsURL", "aboutURL", "iconURL", "icon64URL",
|
|
"skinnable"].forEach(function(aProp) {
|
|
if (aProp in aData)
|
|
rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
|
|
});
|
|
|
|
rdf += writeLocaleStrings(aData);
|
|
|
|
if ("targetPlatforms" in aData) {
|
|
aData.targetPlatforms.forEach(function(aPlatform) {
|
|
rdf += "<em:targetPlatform>" + escapeXML(aPlatform) + "</em:targetPlatform>\n";
|
|
});
|
|
}
|
|
|
|
if ("targetApplications" in aData) {
|
|
aData.targetApplications.forEach(function(aApp) {
|
|
rdf += "<em:targetApplication><Description>\n";
|
|
["id", "minVersion", "maxVersion"].forEach(function(aProp) {
|
|
if (aProp in aApp)
|
|
rdf += "<em:" + aProp + ">" + escapeXML(aApp[aProp]) + "</em:" + aProp + ">\n";
|
|
});
|
|
rdf += "</Description></em:targetApplication>\n";
|
|
});
|
|
}
|
|
|
|
if ("localized" in aData) {
|
|
aData.localized.forEach(function(aLocalized) {
|
|
rdf += "<em:localized><Description>\n";
|
|
if ("locale" in aLocalized) {
|
|
aLocalized.locale.forEach(function(aLocaleName) {
|
|
rdf += "<em:locale>" + escapeXML(aLocaleName) + "</em:locale>\n";
|
|
});
|
|
}
|
|
rdf += writeLocaleStrings(aLocalized);
|
|
rdf += "</Description></em:localized>\n";
|
|
});
|
|
}
|
|
|
|
rdf += "</Description>\n</RDF>\n";
|
|
|
|
if (!aDir.exists())
|
|
aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
|
var file = aDir.clone();
|
|
file.append("install.rdf");
|
|
if (file.exists())
|
|
file.remove(true);
|
|
var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
|
|
createInstance(AM_Ci.nsIFileOutputStream);
|
|
fos.init(file,
|
|
FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
|
|
FileUtils.PERMS_FILE, 0);
|
|
fos.write(rdf, rdf.length);
|
|
fos.close();
|
|
}
|
|
|
|
function registerDirectory(aKey, aDir) {
|
|
var dirProvider = {
|
|
getFile: function(aProp, aPersistent) {
|
|
aPersistent.value = true;
|
|
if (aProp == aKey)
|
|
return aDir.clone();
|
|
return null;
|
|
},
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIDirectoryServiceProvider,
|
|
AM_Ci.nsISupports])
|
|
};
|
|
Services.dirsvc.registerProvider(dirProvider);
|
|
}
|
|
|
|
var gExpectedEvents = {};
|
|
var gExpectedInstalls = [];
|
|
var gNext = null;
|
|
|
|
function getExpectedEvent(aId) {
|
|
if (!(aId in gExpectedEvents))
|
|
do_throw("Wasn't expecting events for " + aId);
|
|
if (gExpectedEvents[aId].length == 0)
|
|
do_throw("Too many events for " + aId);
|
|
let event = gExpectedEvents[aId].shift();
|
|
if (event instanceof Array)
|
|
return event;
|
|
return [event, true];
|
|
}
|
|
|
|
function getExpectedInstall(aAddon) {
|
|
if (gExpectedInstalls instanceof Array)
|
|
return gExpectedInstalls.shift();
|
|
if (!aAddon || !aAddon.id)
|
|
return gExpectedInstalls["NO_ID"].shift();
|
|
let id = aAddon.id;
|
|
if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array))
|
|
do_throw("Wasn't expecting events for " + id);
|
|
if (gExpectedInstalls[id].length == 0)
|
|
do_throw("Too many events for " + id);
|
|
return gExpectedInstalls[id].shift();
|
|
}
|
|
|
|
const AddonListener = {
|
|
onPropertyChanged: function(aAddon, aProperties) {
|
|
let [event, properties] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onPropertyChanged", event);
|
|
do_check_eq(aProperties.length, properties.length);
|
|
properties.forEach(function(aProperty) {
|
|
// Only test that the expected properties are listed, having additional
|
|
// properties listed is not necessary a problem
|
|
if (aProperties.indexOf(aProperty) != -1)
|
|
ok(false, "Did not see property change for " + aProperty);
|
|
});
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onEnabling: function(aAddon, aRequiresRestart) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onEnabling", event);
|
|
do_check_eq(aRequiresRestart, expectedRestart);
|
|
if (expectedRestart)
|
|
do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
|
|
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onEnabled: function(aAddon) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onEnabled", event);
|
|
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDisabling: function(aAddon, aRequiresRestart) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onDisabling", event);
|
|
do_check_eq(aRequiresRestart, expectedRestart);
|
|
if (expectedRestart)
|
|
do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE));
|
|
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDisabled: function(aAddon) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onDisabled", event);
|
|
do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstalling: function(aAddon, aRequiresRestart) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onInstalling", event);
|
|
do_check_eq(aRequiresRestart, expectedRestart);
|
|
if (expectedRestart)
|
|
do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstalled: function(aAddon) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onInstalled", event);
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onUninstalling: function(aAddon, aRequiresRestart) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onUninstalling", event);
|
|
do_check_eq(aRequiresRestart, expectedRestart);
|
|
if (expectedRestart)
|
|
do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onUninstalled: function(aAddon) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onUninstalled", event);
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onOperationCancelled: function(aAddon) {
|
|
let [event, expectedRestart] = getExpectedEvent(aAddon.id);
|
|
do_check_eq("onOperationCancelled", event);
|
|
return check_test_completed(arguments);
|
|
}
|
|
};
|
|
|
|
const InstallListener = {
|
|
onNewInstall: function(install) {
|
|
if (install.state != AddonManager.STATE_DOWNLOADED &&
|
|
install.state != AddonManager.STATE_AVAILABLE)
|
|
do_throw("Bad install state " + install.state);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onNewInstall", getExpectedInstall());
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDownloadStarted: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onDownloadStarted", getExpectedInstall());
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDownloadEnded: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onDownloadEnded", getExpectedInstall());
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDownloadFailed: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
|
|
do_check_eq("onDownloadFailed", getExpectedInstall());
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onDownloadCancelled: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_CANCELLED);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onDownloadCancelled", getExpectedInstall());
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstallStarted: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_INSTALLING);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onInstallStarted", getExpectedInstall(install.addon));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstallEnded: function(install, newAddon) {
|
|
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onInstallEnded", getExpectedInstall(install.addon));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstallFailed: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
|
|
do_check_eq("onInstallFailed", getExpectedInstall(install.addon));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onInstallCancelled: function(install) {
|
|
do_check_eq(install.state, AddonManager.STATE_CANCELLED);
|
|
do_check_eq(install.error, 0);
|
|
do_check_eq("onInstallCancelled", getExpectedInstall(install.addon));
|
|
return check_test_completed(arguments);
|
|
},
|
|
|
|
onExternalInstall: function(aAddon, existingAddon, aRequiresRestart) {
|
|
do_check_eq("onExternalInstall", getExpectedInstall(aAddon));
|
|
do_check_false(aRequiresRestart);
|
|
return check_test_completed(arguments);
|
|
}
|
|
};
|
|
|
|
function hasFlag(aBits, aFlag) {
|
|
return (aBits & aFlag) != 0;
|
|
}
|
|
|
|
// Just a wrapper around setting the expected events
|
|
function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
|
|
AddonManager.addAddonListener(AddonListener);
|
|
AddonManager.addInstallListener(InstallListener);
|
|
|
|
gExpectedInstalls = aExpectedInstalls;
|
|
gExpectedEvents = aExpectedEvents;
|
|
gNext = aNext;
|
|
}
|
|
|
|
// Checks if all expected events have been seen and if so calls the callback
|
|
function check_test_completed(aArgs) {
|
|
if (!gNext)
|
|
return;
|
|
|
|
if (gExpectedInstalls instanceof Array &&
|
|
gExpectedInstalls.length > 0)
|
|
return;
|
|
else for each (let installList in gExpectedInstalls) {
|
|
if (installList.length > 0)
|
|
return;
|
|
}
|
|
|
|
for (let id in gExpectedEvents) {
|
|
if (gExpectedEvents[id].length > 0)
|
|
return;
|
|
}
|
|
|
|
return gNext.apply(null, aArgs);
|
|
}
|
|
|
|
// Verifies that all the expected events for all add-ons were seen
|
|
function ensure_test_completed() {
|
|
for (let i in gExpectedEvents) {
|
|
if (gExpectedEvents[i].length > 0)
|
|
do_throw("Didn't see all the expected events for " + i);
|
|
}
|
|
gExpectedEvents = {};
|
|
if (gExpectedInstalls)
|
|
do_check_eq(gExpectedInstalls.length, 0);
|
|
}
|
|
|
|
/**
|
|
* A helper method to install an array of AddonInstall to completion and then
|
|
* call a provided callback.
|
|
*
|
|
* @param aInstalls
|
|
* The array of AddonInstalls to install
|
|
* @param aCallback
|
|
* The callback to call when all installs have finished
|
|
*/
|
|
function completeAllInstalls(aInstalls, aCallback) {
|
|
let count = aInstalls.length;
|
|
|
|
if (count == 0) {
|
|
aCallback();
|
|
return;
|
|
}
|
|
|
|
function installCompleted(aInstall) {
|
|
aInstall.removeListener(listener);
|
|
|
|
if (--count == 0)
|
|
aCallback();
|
|
}
|
|
|
|
let listener = {
|
|
onDownloadFailed: installCompleted,
|
|
onDownloadCancelled: installCompleted,
|
|
onInstallFailed: installCompleted,
|
|
onInstallCancelled: installCompleted,
|
|
onInstallEnded: installCompleted
|
|
};
|
|
|
|
aInstalls.forEach(function(aInstall) {
|
|
aInstall.addListener(listener);
|
|
aInstall.install();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* A helper method to install an array of files and call a callback after the
|
|
* installs are completed.
|
|
*
|
|
* @param aFiles
|
|
* The array of files to install
|
|
* @param aCallback
|
|
* The callback to call when all installs have finished
|
|
* @param aIgnoreIncompatible
|
|
* Optional parameter to ignore add-ons that are incompatible in
|
|
* aome way with the application
|
|
*/
|
|
function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
|
|
let count = aFiles.length;
|
|
let installs = [];
|
|
|
|
aFiles.forEach(function(aFile) {
|
|
AddonManager.getInstallForFile(aFile, function(aInstall) {
|
|
if (!aInstall)
|
|
do_throw("No AddonInstall created for " + aFile.path);
|
|
do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
|
|
|
|
if (!aIgnoreIncompatible || !aInstall.addon.appDisabled)
|
|
installs.push(aInstall);
|
|
|
|
if (--count == 0)
|
|
completeAllInstalls(installs, aCallback);
|
|
});
|
|
});
|
|
}
|
|
|
|
if ("nsIWindowsRegKey" in AM_Ci) {
|
|
var MockRegistry = {
|
|
LOCAL_MACHINE: {},
|
|
CURRENT_USER: {},
|
|
|
|
setValue: function(aRoot, aPath, aName, aValue) {
|
|
switch (aRoot) {
|
|
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
|
|
var rootKey = MockRegistry.LOCAL_MACHINE;
|
|
break
|
|
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
|
|
rootKey = MockRegistry.CURRENT_USER;
|
|
break
|
|
}
|
|
|
|
if (!(aPath in rootKey)) {
|
|
rootKey[aPath] = [];
|
|
}
|
|
else {
|
|
for (let i = 0; i < rootKey[aPath].length; i++) {
|
|
if (rootKey[aPath][i].name == aName) {
|
|
if (aValue === null)
|
|
rootKey[aPath].splice(i, 1);
|
|
else
|
|
rootKey[aPath][i].value = aValue;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aValue === null)
|
|
return;
|
|
|
|
rootKey[aPath].push({
|
|
name: aName,
|
|
value: aValue
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This is a mock nsIWindowsRegistry implementation. It only implements the
|
|
* methods that the extension manager requires.
|
|
*/
|
|
function MockWindowsRegKey() {
|
|
}
|
|
|
|
MockWindowsRegKey.prototype = {
|
|
values: null,
|
|
|
|
// --- Overridden nsISupports interface functions ---
|
|
QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIWindowsRegKey]),
|
|
|
|
// --- Overridden nsIWindowsRegKey interface functions ---
|
|
open: function(aRootKey, aRelPath, aMode) {
|
|
switch (aRootKey) {
|
|
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
|
|
var rootKey = MockRegistry.LOCAL_MACHINE;
|
|
break
|
|
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
|
|
rootKey = MockRegistry.CURRENT_USER;
|
|
break
|
|
}
|
|
|
|
if (!(aRelPath in rootKey))
|
|
rootKey[aRelPath] = [];
|
|
this.values = rootKey[aRelPath];
|
|
},
|
|
|
|
close: function() {
|
|
this.values = null;
|
|
},
|
|
|
|
get valueCount() {
|
|
if (!this.values)
|
|
throw Components.results.NS_ERROR_FAILURE;
|
|
return this.values.length;
|
|
},
|
|
|
|
getValueName: function(aIndex) {
|
|
if (!this.values || aIndex >= this.values.length)
|
|
throw Components.results.NS_ERROR_FAILURE;
|
|
return this.values[aIndex].name;
|
|
},
|
|
|
|
readStringValue: function(aName) {
|
|
for (let i = 0; i < this.values.length; i++) {
|
|
if (this.values[i].name == aName)
|
|
return this.values[i].value;
|
|
}
|
|
}
|
|
};
|
|
|
|
var WinRegFactory = {
|
|
createInstance: function(aOuter, aIid) {
|
|
if (aOuter != null)
|
|
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
|
|
|
var key = new MockWindowsRegKey();
|
|
return key.QueryInterface(aIid);
|
|
}
|
|
};
|
|
|
|
var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
|
|
registrar.registerFactory(Components.ID("{0478de5b-0f38-4edb-851d-4c99f1ed8eba}"),
|
|
"Mock Windows Registry Implementation",
|
|
"@mozilla.org/windows-registry-key;1", WinRegFactory);
|
|
}
|
|
|
|
// Get the profile directory for tests to use.
|
|
const gProfD = do_get_profile().QueryInterface(AM_Ci.nsILocalFile);
|
|
|
|
// Enable more extensive EM logging
|
|
Services.prefs.setBoolPref("extensions.logging.enabled", true);
|
|
|
|
// By default only load extensions from the profile install location
|
|
Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_PROFILE);
|
|
|
|
// By default, don't cache add-ons in AddonRepository.jsm
|
|
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
|
|
|
|
// Disable the compatibility updates window by default
|
|
Services.prefs.setBoolPref("extensions.showMismatchUI", false);
|
|
|
|
// By default, don't cache add-ons in AddonRepository.jsm
|
|
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
|
|
|
|
// Register a temporary directory for the tests.
|
|
const gTmpD = gProfD.clone();
|
|
gTmpD.append("temp");
|
|
gTmpD.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
|
registerDirectory("TmpD", gTmpD);
|
|
|
|
do_register_cleanup(function() {
|
|
// Check that the temporary directory is empty
|
|
var dirEntries = gTmpD.directoryEntries
|
|
.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
|
|
var entry;
|
|
while (entry = dirEntries.nextFile) {
|
|
do_throw("Found unexpected file in temporary directory: " + entry.leafName);
|
|
}
|
|
|
|
shutdownManager();
|
|
});
|