Bug 555349: Finalize the restartless add-on format. r=robstrong

This commit is contained in:
Dave Townsend 2010-04-28 09:23:36 -07:00
Родитель 7746c1c7e5
Коммит ab93423703
6 изменённых файлов: 400 добавлений и 158 удалений

Просмотреть файл

@ -107,12 +107,22 @@ const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"];
const PROP_TARGETAPP = ["id", "minVersion", "maxVersion"];
const BOOTSTRAP_REASONS = {
APP_STARTUP : 1,
APP_SHUTDOWN : 2,
ADDON_ENABLE : 3,
ADDON_DISABLE : 4,
ADDON_INSTALL : 5,
ADDON_UNINSTALL : 6,
ADDON_UPGRADE : 7,
ADDON_DOWNGRADE : 8
};
// Map new string type identifiers to old style nsIUpdateItem types
const TYPES = {
extension: 2,
theme: 4,
locale: 8,
bootstrapped: 64
locale: 8
};
/**
@ -393,6 +403,10 @@ function loadManifestFromRDF(aUri, aStream) {
addon.aboutURL = null;
}
// Only read the bootstrapped property for extensions
if (addon.type == "extension")
addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true";
addon.defaultLocale = readLocale(ds, root, true);
addon.locales = [];
@ -1191,7 +1205,7 @@ var XPIProvider = {
WARN("Could not uninstall invalid item from locked install location");
// If this was an active add-on then we must force a restart
if (aOldAddon.active) {
if (aOldAddon.type == "bootstrapped")
if (aOldAddon.bootstrap)
delete XPIProvider.bootstrappedAddons[aOldAddon.id];
else
return true;
@ -1212,8 +1226,8 @@ var XPIProvider = {
// If the old version was active and wasn't bootstrapped or the new
// version will be active and isn't bootstrapped then we must force a
// restart
if ((aOldAddon.active && aOldAddon.type != "bootstrapped") ||
(newAddon.active && newAddon.type != "bootstrapped")) {
if ((aOldAddon.active && !aOldAddon.bootstrap) ||
(newAddon.active && !newAddon.bootstrap)) {
return true;
}
}
@ -1250,7 +1264,7 @@ var XPIProvider = {
// If the add-on is bootstrappable and it should be active then
// mark it as active and add it to the list to be activated.
if (aOldAddon.type == "bootstrapped" && !aOldAddon.appDisabled &&
if (aOldAddon.bootstrap && !aOldAddon.appDisabled &&
!aOldAddon.userDisabled) {
aOldAddon.active = true;
XPIDatabase.updateAddonActive(aOldAddon);
@ -1281,7 +1295,7 @@ var XPIProvider = {
// If this is a visible add-on and it isn't userDisabled then we
// may need a restart or to update the bootstrap list.
if (aOldAddon.visible && !aOldAddon.userDisabled) {
if (aOldAddon.type == "bootstrapped") {
if (aOldAddon.bootstrap) {
// When visible and not userDisabled, active is the opposite of
// appDisabled.
aOldAddon.active = !aOldAddon.appDisabled;
@ -1330,10 +1344,10 @@ var XPIProvider = {
// If this was an active add-on and bootstrapped we must remove it from
// the bootstrapped list, otherwise we need to force a restart.
if (aOldAddon.type != "bootstrapped")
if (!aOldAddon.bootstrap)
return true;
delete XPIProvider.bootstrappedAddons[aOldAddon.id];
XPIProvider.unloadBootstrapScope(aOldAddon.id);
}
return false;
@ -1398,16 +1412,17 @@ var XPIProvider = {
// Update the database.
XPIDatabase.addAddonMetadata(newAddon, aAddonState.descriptor);
// Visible bootstrapped add-ons need to be added to the bootstrap list.
// Visible bootstrapped add-ons need to have their install method called
if (newAddon.visible) {
visibleAddons[newAddon.id] = newAddon;
if (newAddon.type != "bootstrapped")
if (!newAddon.bootstrap)
return true;
XPIProvider.bootstrappedAddons[newAddon.id] = {
version: newAddon.version,
descriptor: aAddonState.descriptor
};
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
dir.persistentDescriptor = aAddonState.descriptor;
XPIProvider.callBootstrapMethod(newAddon.id, newAddon.version, dir,
"install",
BOOTSTRAP_REASONS.ADDON_INSTALL);
}
return false;
@ -1566,12 +1581,11 @@ var XPIProvider = {
return true;
}
let bootstrappedAddons = this.bootstrappedAddons;
this.bootstrappedAddons = {};
for (let id in bootstrappedAddons) {
for (let id in this.bootstrappedAddons) {
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
dir.persistentDescriptor = bootstrappedAddons[id].descriptor;
this.activateAddon(id, bootstrappedAddons[id].version, dir, true, false);
dir.persistentDescriptor = this.bootstrappedAddons[id].descriptor;
this.callBootstrapMethod(id, this.bootstrappedAddons[id].version, dir,
"startup", BOOTSTRAP_REASONS.APP_STARTUP);
}
// Let these shutdown a little earlier when they still have access to most
@ -1580,8 +1594,13 @@ var XPIProvider = {
observe: function(aSubject, aTopic, aData) {
Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
JSON.stringify(XPIProvider.bootstrappedAddons));
for (let id in XPIProvider.bootstrappedAddons)
XPIProvider.deactivateAddon(id, true, false);
for (let id in XPIProvider.bootstrappedAddons) {
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
dir.persistentDescriptor = XPIProvider.bootstrappedAddons[id].descriptor;
XPIProvider.callBootstrapMethod(id, XPIProvider.bootstrappedAddons[id].version,
dir, "shutdown",
BOOTSTRAP_REASONS.APP_SHUTDOWN);
}
Services.obs.removeObserver(this, "quit-application-granted");
}
}, "quit-application-granted", false);
@ -1872,7 +1891,7 @@ var XPIProvider = {
return aAddon.internalName !=
Prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
return aAddon.type != "bootstrapped";
return !aAddon.bootstrap;
},
/**
@ -1891,7 +1910,7 @@ var XPIProvider = {
return this.selectedSkin !=
Prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
return aAddon.type != "bootstrapped";
return !aAddon.bootstrap;
},
/**
@ -1907,7 +1926,7 @@ var XPIProvider = {
return aAddon.internalName ==
Prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
return aAddon.type != "bootstrapped";
return !aAddon.bootstrap;
},
/**
@ -1923,85 +1942,53 @@ var XPIProvider = {
return aAddon.internalName ==
Prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
return aAddon.type != "bootstrapped";
return !aAddon.bootstrap;
},
/**
* Calls a method in a bootstrap.js loaded scope logging any exceptions thrown.
* Loads a bootstrapped add-on's bootstrap.js into a sandbox and the reason
* values as constants in the scope. This will also add information about the
* add-on to the bootstrappedAddons dictionary and notify the crash reporter
* that new add-ons have been loaded.
*
* @param aId
* The ID of the add-on being bootstrapped
* @param aScope
* The loaded JS scope to call into
* @param aMethods
* An array of methods. The first method in the array that exists in
* the scope will be called
*/
callBootstrapMethod: function XPI_callBootstrapMethod(aId, aScope, aMethods) {
for (let i = 0; i < aMethods.length; i++) {
if (aMethods[i] in aScope) {
LOG("Calling bootstrap method " + aMethods[i] + " on " + aId);
try {
aScope[aMethods[i]](aId);
}
catch (e) {
WARN("Exception running bootstrap method " + aMethods[i] + " on " +
aId + ": " + e);
}
return;
}
}
},
/**
* Activates a bootstrapped add-on by loading its JS scope and calling the
* appropriate method on it. Adds the add-on and its scope to the
* bootstrapScopes and bootstrappedAddons dictionaries.
*
* @param aId
* The ID of the add-on being activated
* @param aVersion
* The version of the add-on being activated
* The add-on's ID
* @param aDir
* The directory containing the add-on
* @param aStartup
* true if the add-on is being activated during startup
* @param aInstall
* true if the add-on is being activated during installation
* The nsILocalFile for the directory containing the add-on
* @param aVersion
* The add-on's version
* @return a JavaScript scope
*/
activateAddon: function XPI_activateAddon(aId, aVersion, aDir, aStartup, aInstall) {
let methods = ["enable"];
if (aStartup)
methods.unshift("startup");
if (aInstall)
methods.unshift("install");
let bootstrap = aDir.clone();
bootstrap.append("bootstrap.js");
if (bootstrap.exists()) {
let uri = Services.io.newFileURI(bootstrap);
let principal = Cc["@mozilla.org/systemprincipal;1"].
createInstance(Ci.nsIPrincipal);
let scope = new Components.utils.Sandbox(principal);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
createInstance(Ci.mozIJSSubScriptLoader);
loadBootstrapScope: function XPI_loadBootstrapScope(aId, aDir, aVersion) {
LOG("Loading bootstrap scope from " + aDir.path);
// Mark the add-on as active for the crash reporter before loading
this.bootstrappedAddons[aId] = {
version: aVersion,
descriptor: aDir.persistentDescriptor
};
this.bootstrapScopes[aId] = scope;
this.addAddonsToCrashReporter();
let principal = Cc["@mozilla.org/systemprincipal;1"].
createInstance(Ci.nsIPrincipal);
this.bootstrapScopes[aId] = new Components.utils.Sandbox(principal);
let bootstrap = aDir.clone();
bootstrap.append("bootstrap.js");
if (bootstrap.exists()) {
let uri = Services.io.newFileURI(bootstrap);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
createInstance(Ci.mozIJSSubScriptLoader);
try {
loader.loadSubScript(uri.spec, scope);
loader.loadSubScript(uri.spec, this.bootstrapScopes[aId]);
}
catch (e) {
WARN("Error loading bootstrap.js for " + aId + ": " + e);
}
this.callBootstrapMethod(aId, scope, methods);
// Copy the reason values from the global object into the bootstrap scope.
for (let name in BOOTSTRAP_REASONS)
this.bootstrapScopes[aId][name] = BOOTSTRAP_REASONS[name];
}
else {
WARN("Bootstrap missing for " + aId);
@ -2009,34 +1996,58 @@ var XPIProvider = {
},
/**
* Dectivates a bootstrapped add-on by by calling the appropriate method on
* the cached JS scope. Removes the add-on and its scope from the
* bootstrapScopes and bootstrappedAddons dictionaries.
* Unloads a bootstrap scope by dropping all references to it and then
* updating the list of active add-ons with the crash reporter.
*
* @param aId
* The ID of the add-on being deactivated
* @param aShutdown
* true if the add-on is being deactivated during shutdown
* @param aUninstall
* true if the add-on is being deactivated during uninstallation
* The add-on's ID
*/
deactivateAddon: function XPI_deactivateAddon(aId, aShutdown, aUninstall) {
if (!(aId in this.bootstrappedAddons)) {
ERROR("Attempted to deactivate an add-on that was never activated");
unloadBootstrapScope: function XPI_unloadBootstrapScope(aId) {
delete this.bootstrapScopes[aId];
delete this.bootstrappedAddons[aId];
this.addAddonsToCrashReporter();
},
/**
* Calls a bootstrap method for an add-on.
*
* @param aId
* The ID of the add-on
* @param aVersion
* The version of the add-on
* @param aDir
* The nsILocalFile for the directory containing the add-on
* @param aMethod
* The name of the bootstrap method to call
* @param aReason
* The reason flag to pass to the bootstrap's startup method
*/
callBootstrapMethod: function XPI_callBootstrapMethod(aId, aVersion, aDir,
aMethod, aReason) {
// Load the scope if it hasn't already been loaded
if (!(aId in this.bootstrapScopes))
this.loadBootstrapScope(aId, aDir, aVersion);
if (!(aMethod in this.bootstrapScopes[aId])) {
WARN("Add-on " + aId + " is missing bootstrap method " + aMethod);
return;
}
let scope = this.bootstrapScopes[aId];
delete this.bootstrappedAddons[aId];
delete this.bootstrapScopes[aId];
let methods = ["disable"];
if (aShutdown)
methods.unshift("shutdown");
if (aUninstall)
methods.unshift("uninstall");
this.callBootstrapMethod(aId, scope, methods);
let params = {
id: aId,
version: aVersion,
installPath: aDir.clone()
};
this.addAddonsToCrashReporter();
LOG("Calling bootstrap method " + aMethod + " on " + aId + " version " +
aVersion);
try {
this.bootstrapScopes[aId][aMethod](params, aReason);
}
catch (e) {
WARN("Exception running bootstrap method " + aMethods + " on " +
aId + ": " + e);
}
},
/**
@ -2113,14 +2124,19 @@ var XPIProvider = {
aAddon.active = !isDisabled;
XPIDatabase.updateAddonActive(aAddon);
if (isDisabled) {
if (aAddon.type == "bootstrapped")
this.deactivateAddon(aAddon.id, false, false);
if (aAddon.bootstrap) {
let dir = aAddon._installLocation.getLocationForID(aAddon.id);
this.callBootstrapMethod(aAddon.id, aAddon.version, dir, "shutdown",
BOOTSTRAP_REASONS.ADDON_DISABLE);
this.unloadBootstrapScope(aAddon.id);
}
AddonManagerPrivate.callAddonListeners("onDisabled", wrapper);
}
else {
if (aAddon.type == "bootstrapped") {
if (aAddon.bootstrap) {
let dir = aAddon._installLocation.getLocationForID(aAddon.id);
this.activateAddon(aAddon.id, aAddon.version, dir, false, false);
this.callBootstrapMethod(aAddon.id, aAddon.version, dir, "startup",
BOOTSTRAP_REASONS.ADDON_ENABLE);
}
AddonManagerPrivate.callAddonListeners("onEnabled", wrapper);
}
@ -2174,8 +2190,16 @@ var XPIProvider = {
requiresRestart);
if (!requiresRestart) {
if (aAddon.type == "bootstrapped")
this.deactivateAddon(aAddon.id, false, true);
if (aAddon.bootstrap) {
let dir = aAddon._installLocation.getLocationForID(aAddon.id);
if (aAddon.active) {
this.callBootstrapMethod(aAddon.id, aAddon.version, dir, "shutdown",
BOOTSTRAP_REASONS.ADDON_UNINSTALL);
}
this.callBootstrapMethod(aAddon.id, aAddon.version, dir, "uninstall",
BOOTSTRAP_REASONS.ADDON_UNINSTALL);
this.unloadBootstrapScope(aAddon.id);
}
aAddon._installLocation.uninstallAddon(aAddon.id);
XPIDatabase.removeAddonMetadata(aAddon);
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
@ -2225,7 +2249,7 @@ const FIELDS_ADDON = "internal_id, id, location, version, type, internalName, "
"updateURL, updateKey, optionsURL, aboutURL, iconURL, " +
"defaultLocale, visible, active, userDisabled, appDisabled, " +
"pendingUninstall, descriptor, installDate, updateDate, " +
"applyBackgroundUpdates";
"applyBackgroundUpdates, bootstrap";
// A helper function to simply log any errors that occur during async statements.
function asyncErrorLogger(aError) {
@ -2261,7 +2285,7 @@ var XPIDatabase = {
":updateKey, :optionsURL, :aboutURL, :iconURL, " +
":locale, :visible, :active, :userDisabled," +
" :appDisabled, 0, :descriptor, :installDate, " +
":updateDate, :applyBackgroundUpdates)",
":updateDate, :applyBackgroundUpdates, :bootstrap)",
addAddonMetadata_addon_locale: "INSERT INTO addon_locale VALUES " +
"(:internal_id, :name, :locale)",
addAddonMetadata_locale: "INSERT INTO locale (name, description, creator, " +
@ -2278,7 +2302,7 @@ var XPIDatabase = {
"internal_id=:internal_id",
getActiveAddons: "SELECT " + FIELDS_ADDON + " FROM addon WHERE active=1 AND " +
"type<>'theme' AND type<>'bootstrapped'",
"type<>'theme' AND bootstrap=0",
getActiveTheme: "SELECT " + FIELDS_ADDON + " FROM addon WHERE " +
"internalName=:internalName AND type='theme'",
@ -2299,7 +2323,8 @@ var XPIDatabase = {
makeAddonVisible: "UPDATE addon SET visible=1 WHERE internal_id=:internal_id",
removeAddonMetadata: "DELETE FROM addon WHERE internal_id=:internal_id",
// Equates to active = visible && !userDisabled && !appDisabled && !pendingUninstall
// Equates to active = visible && !userDisabled && !appDisabled &&
// !pendingUninstall
setActiveAddons: "UPDATE addon SET active=MIN(visible, 1 - userDisabled, " +
"1 - appDisabled, 1 - pendingUninstall)",
setAddonProperties: "UPDATE addon SET userDisabled=:userDisabled, " +
@ -2369,9 +2394,11 @@ var XPIDatabase = {
if (disabled == "true" || disabled == "needs-disable")
migrateData[location][id].userDisabled = true;
let targetApps = ds.GetTargets(source, EM_R("targetApplication"), true);
let targetApps = ds.GetTargets(source, EM_R("targetApplication"),
true);
while (targetApps.hasMoreElements()) {
let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
let targetApp = targetApps.getNext()
.QueryInterface(Ci.nsIRDFResource);
let appInfo = {
id: getRDFProperty(ds, targetApp, "id"),
minVersion: getRDFProperty(ds, targetApp, "minVersion"),
@ -2482,7 +2509,7 @@ var XPIDatabase = {
"pendingUninstall INTEGER, descriptor TEXT, " +
"installDate INTEGER, updateDate INTEGER, " +
"applyBackgroundUpdates INTEGER, " +
"UNIQUE (id, location)");
"bootstrap INTEGER, UNIQUE (id, location)");
this.connection.createTable("targetApplication",
"addon_internal_id INTEGER, " +
"id TEXT, minVersion TEXT, maxVersion TEXT, " +
@ -2618,7 +2645,8 @@ var XPIDatabase = {
addon.installDate = aRow.installDate;
addon.updateDate = aRow.updateDate;
["visible", "active", "userDisabled", "appDisabled",
"pendingUninstall", "applyBackgroundUpdates"].forEach(function(aProp) {
"pendingUninstall", "applyBackgroundUpdates",
"bootstrap"].forEach(function(aProp) {
addon[aProp] = aRow[aProp] != 0;
});
this.addonCache[aRow.internal_id] = Components.utils.getWeakReference(addon);
@ -2768,7 +2796,8 @@ var XPIDatabase = {
addon.installDate = aRow.getResultByName("installDate");
addon.updateDate = aRow.getResultByName("updateDate");
["visible", "active", "userDisabled", "appDisabled",
"pendingUninstall", "applyBackgroundUpdates"].forEach(function(aProp) {
"pendingUninstall", "applyBackgroundUpdates",
"bootstrap"].forEach(function(aProp) {
addon[aProp] = aRow.getResultByName(aProp) != 0;
});
this.addonCache[internal_id] = Components.utils.getWeakReference(addon);
@ -3042,8 +3071,8 @@ var XPIDatabase = {
stmt.params.installDate = aAddon.installDate;
stmt.params.updateDate = aAddon.updateDate;
copyProperties(aAddon, PROP_METADATA, stmt.params);
["visible", "userDisabled", "appDisabled",
"applyBackgroundUpdates"].forEach(function(aProp) {
["visible", "userDisabled", "appDisabled", "applyBackgroundUpdates",
"bootstrap"].forEach(function(aProp) {
stmt.params[aProp] = aAddon[aProp] ? 1 : 0;
});
stmt.params.active = (aAddon.visible && !aAddon.userDisabled &&
@ -3842,20 +3871,35 @@ AddonInstall.prototype = {
// See bug 553015.
// Deactivate and remove the old add-on as necessary
let reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
if (this.existingAddon) {
if (Services.vc.compare(this.existingAddon.version, this.addon.version) < 0)
reason = BOOTSTRAP_REASONS.ADDON_UPGRADE;
else
reason = BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
if (this.existingAddon.bootstrap) {
let dir = this.existingAddon._installLocation
.getLocationForID(this.existingAddon.id);
if (this.existingAddon.active) {
if (this.existingAddon.type == "bootstrapped")
XPIProvider.deactivateAddon(this.existingAddon.id, false,
isUpgrade);
// If this is an upgrade its metadata will be removed below
if (!isUpgrade) {
XPIProvider.callBootstrapMethod(this.existingAddon.id,
this.existingAddon.version,
dir, "shutdown", reason);
}
XPIProvider.callBootstrapMethod(this.existingAddon.id,
this.existingAddon.version,
dir, "uninstall", reason);
XPIProvider.unloadBootstrapScope(this.existingAddon.id);
}
if (isUpgrade) {
this.installLocation.uninstallAddon(this.existingAddon.id);
}
else if (this.existingAddon.active) {
this.existingAddon.active = false;
XPIDatabase.updateAddonActive(this.existingAddon);
}
}
if (isUpgrade)
this.installLocation.uninstallAddon(this.existingAddon.id);
}
// Install the new add-on into its final directory
let dir = this.installLocation.installAddon(this.addon.id, stagedAddon);
@ -3878,8 +3922,17 @@ AddonInstall.prototype = {
XPIDatabase.getAddonInLocation(this.addon.id, this.installLocation.name,
function(a) {
self.addon = a;
if (self.addon.active && self.addon.type == "bootstrapped")
XPIProvider.activateAddon(self.addon.id, self.addon.version, dir, false, true);
if (self.addon.bootstrap) {
XPIProvider.callBootstrapMethod(self.addon.id, self.addon.version,
dir, "install", reason);
if (self.addon.active) {
XPIProvider.callBootstrapMethod(self.addon.id, self.addon.version,
dir, "startup", reason);
}
else {
this.unloadBootstrapScope(self.addon.id);
}
}
AddonManagerPrivate.callAddonListeners("onInstalled",
createWrapper(self.addon));

Просмотреть файл

@ -1,9 +1,19 @@
Components.utils.import("resource://gre/modules/Services.jsm");
function enable() {
Services.prefs.setIntPref("bootstraptest.version", 1);
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 1);
}
function disable() {
Services.prefs.setIntPref("bootstraptest.version", 0);
function startup(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 1);
Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
}
function shutdown(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 0);
Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
}
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
}

Просмотреть файл

@ -6,7 +6,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>bootstrap1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>64</em:type>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Test Bootstrap 1</em:name>

Просмотреть файл

@ -1,9 +1,19 @@
Components.utils.import("resource://gre/modules/Services.jsm");
function enable() {
Services.prefs.setIntPref("bootstraptest.version", 2);
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 2);
}
function disable() {
Services.prefs.setIntPref("bootstraptest.version", 0);
function startup(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 2);
Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
}
function shutdown(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 0);
Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
}
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
}

Просмотреть файл

@ -6,7 +6,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>bootstrap1@tests.mozilla.org</em:id>
<em:version>2.0</em:version>
<em:type>64</em:type>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Test Bootstrap 1</em:name>

Просмотреть файл

@ -2,6 +2,15 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const APP_STARTUP = 1;
const APP_SHUTDOWN = 2;
const ADDON_ENABLE = 3;
const ADDON_DISABLE = 4;
const ADDON_INSTALL = 5;
const ADDON_UNINSTALL = 6;
const ADDON_UPGRADE = 7;
const ADDON_DOWNGRADE = 8;
// This verifies that bootstrappable add-ons can be used with restarts.
Components.utils.import("resource://gre/modules/Services.jsm");
@ -10,8 +19,20 @@ Services.prefs.setIntPref("bootstraptest.version", 0);
const profileDir = gProfD.clone();
profileDir.append("extensions");
function getActivatedVersion() {
return Services.prefs.getIntPref("bootstraptest.version");
function getActiveVersion() {
return Services.prefs.getIntPref("bootstraptest.active_version");
}
function getInstalledVersion() {
return Services.prefs.getIntPref("bootstraptest.installed_version");
}
function getStartupReason() {
return Services.prefs.getIntPref("bootstraptest.startup_reason");
}
function getShutdownReason() {
return Services.prefs.getIntPref("bootstraptest.shutdown_reason");
}
function run_test() {
@ -33,7 +54,7 @@ function run_test_1() {
ensure_test_completed();
do_check_neq(install, null);
do_check_eq(install.type, "bootstrapped");
do_check_eq(install.type, "extension");
do_check_eq(install.version, "1.0");
do_check_eq(install.name, "Test Bootstrap 1");
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
@ -62,7 +83,9 @@ function check_test_1() {
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getActivatedVersion(), 1);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_eq(getStartupReason(), ADDON_INSTALL);
do_check_true(b1.hasResource("install.rdf"));
do_check_true(b1.hasResource("bootstrap.js"));
do_check_false(b1.hasResource("foo.bar"));
@ -100,7 +123,9 @@ function run_test_2() {
do_check_false(b1.appDisabled);
do_check_true(b1.userDisabled);
do_check_false(b1.isActive);
do_check_eq(getActivatedVersion(), 0);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), ADDON_DISABLE);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
@ -118,9 +143,13 @@ function run_test_2() {
// Test that restarting doesn't accidentally re-enable
function run_test_3() {
shutdownManager();
do_check_eq(getActivatedVersion(), 0);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), ADDON_DISABLE);
startupManager(0, false);
do_check_eq(getActivatedVersion(), 0);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), ADDON_DISABLE);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
@ -152,7 +181,9 @@ function run_test_4() {
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getActivatedVersion(), 1);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_eq(getStartupReason(), ADDON_ENABLE);
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
@ -170,10 +201,14 @@ function run_test_4() {
// Tests that a restart shuts down and restarts the add-on
function run_test_5() {
shutdownManager();
do_check_eq(getActivatedVersion(), 0);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), APP_SHUTDOWN);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
startupManager(0, false);
do_check_eq(getActivatedVersion(), 1);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_eq(getStartupReason(), APP_STARTUP);
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
@ -198,7 +233,7 @@ function run_test_6() {
ensure_test_completed();
do_check_neq(install, null);
do_check_eq(install.type, "bootstrapped");
do_check_eq(install.type, "extension");
do_check_eq(install.version, "2.0");
do_check_eq(install.name, "Test Bootstrap 1");
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
@ -223,7 +258,10 @@ function check_test_6() {
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getActivatedVersion(), 2);
do_check_eq(getInstalledVersion(), 2);
do_check_eq(getActiveVersion(), 2);
do_check_eq(getStartupReason(), ADDON_UPGRADE);
do_check_eq(getShutdownReason(), ADDON_UPGRADE);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
@ -249,7 +287,9 @@ function run_test_7() {
function check_test_7() {
ensure_test_completed();
do_check_eq(getActivatedVersion(), 0);
do_check_eq(getInstalledVersion(), 0);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
@ -291,7 +331,9 @@ function run_test_8() {
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getActivatedVersion(), 1);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_eq(getStartupReason(), APP_STARTUP);
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
run_test_9();
@ -311,6 +353,133 @@ function run_test_9() {
do_check_eq(b1, null);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
do_test_finished();
run_test_10();
});
}
// Tests that installing a downgrade sends the right reason
function run_test_10() {
prepare_test({ }, [
"onNewInstall"
]);
AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
ensure_test_completed();
do_check_neq(install, null);
do_check_eq(install.type, "extension");
do_check_eq(install.version, "2.0");
do_check_eq(install.name, "Test Bootstrap 1");
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
do_check_true(install.addon.hasResource("install.rdf"));
do_check_true(install.addon.hasResource("bootstrap.js"));
do_check_false(install.addon.hasResource("foo.bar"));
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
prepare_test({
"bootstrap1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onInstallStarted",
"onInstallEnded",
], check_test_10_pt1);
install.install();
});
}
function check_test_10_pt1() {
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
do_check_neq(b1, null);
do_check_eq(b1.version, "2.0");
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getInstalledVersion(), 2);
do_check_eq(getActiveVersion(), 2);
do_check_eq(getStartupReason(), ADDON_INSTALL);
do_check_true(b1.hasResource("install.rdf"));
do_check_true(b1.hasResource("bootstrap.js"));
do_check_false(b1.hasResource("foo.bar"));
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
prepare_test({ }, [
"onNewInstall"
]);
AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
ensure_test_completed();
do_check_neq(install, null);
do_check_eq(install.type, "extension");
do_check_eq(install.version, "1.0");
do_check_eq(install.name, "Test Bootstrap 1");
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
prepare_test({
"bootstrap1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onInstallStarted",
"onInstallEnded",
], check_test_10_pt2);
install.install();
});
});
}
function check_test_10_pt2() {
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_false(b1.appDisabled);
do_check_false(b1.userDisabled);
do_check_true(b1.isActive);
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_eq(getStartupReason(), ADDON_DOWNGRADE);
do_check_eq(getShutdownReason(), ADDON_DOWNGRADE);
do_check_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "2.0");
run_test_11();
});
}
// Tests that uninstalling a disabled add-on still calls the uninstall method
function run_test_11() {
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
prepare_test({
"bootstrap1@tests.mozilla.org": [
["onDisabling", false],
"onDisabled",
["onUninstalling", false],
"onUninstalled"
]
});
b1.userDisabled = true;
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 0);
do_check_eq(getShutdownReason(), ADDON_DISABLE);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
b1.uninstall();
check_test_11();
});
}
function check_test_11() {
ensure_test_completed();
do_check_eq(getInstalledVersion(), 0);
do_check_eq(getActiveVersion(), 0);
do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
do_test_finished();
}